Sunday, July 08, 2007

Annoying Vista UI

In Vista, Media Player and Photo Gallery have very similar user interfaces. The toolbar thingy at the bottom of their windows looks almost exactly the same.

wmp This is Media Player
photo_gallery This is Photo Gallery

But the big button in the middle works differently. If I'm watching a video in Media Player, the biggest button on the toolbar pauses the video. In Photo Gallery, it switches to slide-show mode, an operation that takes several seconds and I never want to do. If I'm not thinking about what application I'm in, I'll hit that big button to pause the video and see my computer grind to a halt whilst it does something I don't want it to do. Why have very similar UIs if the buttons do different things??? Consistent UIs are only any use if they behave consistently.

Thursday, July 05, 2007

The Complicators

I was under the impression that computing was meant to make our life easier, but it seems there's a bunch of people out there who want to make my life more difficult through poorly thought out APIs or badly designed user interfaces. I call them the complicators.

There are three types of complicators.

Dumb Complicators - These are the developers who aren't really too hot at programming. They copy and paste and generally fumble around in the dark until they have a solution that seems to work, then quickly move onto the next problem, not thinking too much about how the UI or API they've just produced will be used in the outside world. This may be down to inexperience, the need to get something out there ASAP or just a lack of interest in producing quality software.

Evil Complicators - The second group of complicators are a cynical bunch who complicate things to make themselves indispensable to their company. Depending on how cynical I'm feeling, I may confuse either of the other two groups as being members of this lot. 

Clever Complicators - The third group are generally incredibly intelligent but have no way of empathising with the poor people who are going to have to use their software. They understand the complexity and can't understand why anybody else wouldn't be able to. I worked with a very clever guy whose API consisted of a programming language using XML to build the function calls, parameters and the graph used to join all these bits together. Yes, he was a big fan of XSLT. Yes, what he produced was incredibly powerful and clever. Actually trying to use his API from another programming language was an absolute nightmare. On the other hand, I worked with another very bright chap whose API was also based on XML. The difference being that his API simply described data and a few parameters. The actual implementation details of how it all worked under the hood was completely hidden from the API, except where necessary. The wonderful thing about that API was that the simplicity of it made me look good, since I was able to write my code on top of it in super quick time.

This third group of complicators interest me. It seems like there are plenty of clever people in the IT industry but a very select few who can actually take complicated problems and abstract them into something simple for the rest of us to understand. And doing that is the only thing that can drive technology forward. As Einstein said "Make everything as simple as possible, but not simpler."

Wednesday, July 04, 2007

Something for the ladies

Or more specifically any mothers who've stumbled across this site. My other half has set up a website dedicated to providing tips for mothers and parents in general. There are other sites out there providing similar resources (Mumsnet being the obvious example if only due to their legal problems with Gina Ford) but Mums Guide is providing a different angle on things.

She was after a link from the Random Pub Finder (due to it getting many more hits than this site) but I couldn't for the life of me think of any kind of link between pubs and parenting.

Tuesday, July 03, 2007

Aren't web services meant to solve interoperability problems?

Somebody has written a web service using JBoss. I'm trying to call it from .NET 1.1 or, heaven forbid, the MS SOAP toolkit. And neither of them can call any operations that have parameters, which kind of restricts what we can do with it. I guess one solution might be to have methods called method1(), method2(), method3() etc all the way up to MAXINT. Doesn't seem a great idea. Or perhaps an ASP.NET 2 web service could call the JBoss web service and expose the functionality as another web service? Yeh it's daft, but it might just work.

So could it be the folks telling us web services were the panacea for all our interoperability problems were actually just snake oil salesmen? Surely not...

Monday, June 25, 2007

Adding icons to custom activities

In a few places I've worked, it seems like some people's jobs pretty much entails selecting and adding icons to development projects. Nice work if you can get away with it and that's what I spent some time doing the other day.

It's actually ridiculously easy to add an icon to a custom activity. In fact it's just the same as adding an icon for any component. Add the image to your project, set its 'Build Action' property to 'Embedded Resource', then add the following attribute to your activity class.

[ToolboxBitmap(typeof(UserActivity), "user.png")]

The icon will then appear in the toolbox and will be shown when the activity is rendered in the workflow designer. It's possible to completely change the rendering of the activity and I may investigate that at a later date, but this simple addition seems to provide the biggest bang for buck.

Sunday, June 24, 2007

Property Snake

I remember Fucked Company appearing on the web around the time of the dot-com bubble bursting so perhaps the appearance of the Property Snake website (along with House Price Crash) is evidence that the property bubble is finally about to burst. That and the more bearish coverage in the press...

Thursday, June 21, 2007

Windows Workflow isn't workflow

Ask anybody who's had any experience of workflow software what it is and they'd likely give you a different answer to the next person you ask. But I'm fairly certain if they took a look at Windows Workflow they'd probably say it's not what they understand workflow to be about.

I'm the same and it's one reason why when I first looked at WF I was a little disappointed. But as time goes on and I play with it more and more I'm realising what a really nice piece of technology it is. In fact I'd say I'm actually very pleased that Microsoft didn't build another workflow system because it very likely wouldn't have met my needs either. There are hundreds of workflow systems out there, all implemented differently from each other and all meeting certain needs and failing to meet other needs.

I guess somebody at Microsoft realised this and rather than producing yet another workflow system (YAWFS for short), they wrote a framework for developing any kind of long-running process you'd like to build on top of it. As far as I'm aware nobody else has tried to come up with anything similar and the outcome is far more impressive than YAWFS. The book 'Essential Windows Workflow' explains it a whole lot better than I can but essentially it's a whole new way of writing software. No longer do you need to worry about what to do when you need to wait for a day before moving onto the next step in your code and it seems like threading issues will be much reduced. I'm almost inclined to think that WF may be the answer to a lot of the multi-threading pain we suffer today and really need to solve PDQ in a world of multi-core processors, although I need to delve deeper to know for sure. If you come from a .NET background, you can reuse those skills in WF, although it has to be said the learning curve can be quite steep. 

And it seems like they've succeeded in their aims. Not only are people using WF for workflow products, they are using it for things that you wouldn't necessarily associate with workflow. In conclusion, I think it rocks.

Wednesday, June 20, 2007

Compiling a XOML workflow

In my last post I talked about validating a XOML workflow. Another way to validate a workflow is to just try and compile it and see what you get back from the compiler. The disadvantage of this approach is the fact you have to start to write things out to file. So I've been validating using the previous method and then just compiling when necessary*. The downside of this approach is the x:Class attribute which can't be present when trying to execute a XOML workflow and must be present when trying to compile it. The simple workaround for this is to add the x:Class attribute before writing the XOML out to a temporary file. Anyway here's the code -

        // copy to a temporary file and add the x:Class attribute
        string tempFileName = Path.GetTempPath() + "temp.xoml";
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xoml);
        doc.DocumentElement.SetAttribute("Class", "http://schemas.microsoft.com/winfx/2006/xaml", WorkflowName);
        doc.Save(tempFileName);
        try
        {
          // Compile the workflow
          WorkflowCompiler compiler = new WorkflowCompiler();
          WorkflowCompilerParameters parameters = new WorkflowCompilerParameters();
          parameters.LibraryPaths.Add(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
          parameters.ReferencedAssemblies.Add("MyActivities.dll");
          parameters.OutputAssembly = string.Format("{0}.dll", WorkflowName);
          compilerResults = compiler.Compile(parameters, tempFileName);
        }
        finally
        {
          File.Delete(tempFileName);
        }
        
        StringBuilder errors = new StringBuilder();
        foreach (CompilerError compilerError in compilerResults.Errors)
        {
          errors.Append(compilerError.ToString() + '\n');
        }

        if (errors.Length != 0)
        {
          MessageBox.Show(this, errors.ToString(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
          compileOK = false;
        }

* Another reason I did it this way was because I implemented the validation piece before I implemented the compilation piece and didn't realise I could just re-use the compiling code...

Tuesday, June 19, 2007

Validating a XOML workflow

If you're using workflow assemblies, there is no need to validate your workflow since the compilation process has already done this for you. But if you're working with XOML files you'll probably want to validate it at some point before deploying it. Unfortunately there doesn't seem to be anything provided by Windows Workflow just to validate a workflow. The workaround is to start up the runtime and load up your XOML and see what errors you get. You can use something like the following (ripped off and modified from a newsgroup posting) -

      WorkflowRuntime workflowRuntime = new WorkflowRuntime();
      workflowRuntime.StartRuntime();

      StringReader stringReader = new StringReader(xomlString);
      XmlTextReader reader = new XmlTextReader(stringReader);
      try
      {
        instance = workflowRuntime.CreateWorkflow(reader);
      }
      catch (WorkflowValidationFailedException exp)
      {

        StringBuilder errors = new StringBuilder();

        foreach (ValidationError error in exp.Errors)
        {
          errors.AppendLine(error.ToString());
        }

        MessageBox.Show(errors.ToString(), "Validation errors");
        retVal = false;
      }

One thing to be aware of is that you can't add the x:Class attribute when you use this technique (which you must add if you want to compile your XOML later on). This Catch-22 can be quite easily solved and I'll talk about it in a later post.

If you're wondering why you'd use XOML, I think it probably makes sense when you're automatically generating your workflows from some other source.

Invalid FORMATETC structure (Exception from HRESULT: 0x80040064 (DV_E_FORMATETC))

I've been caught out by this twice now. If you create a new web control by creating a new class and then inheriting from Control (or some other derived class) it will appear in your toolbox, but when you try and put it on a web form, either nothing happens or you'll get the error:

Invalid FORMATETC structure (Exception from HRESULT: 0x80040064 (DV_E_FORMATETC))

This somewhat baffling error message is caused by the fact that when you create a new class Visual Studio 2005 makes it private by default. I'm not sure if this is a good or bad thing, it sure as hell beats the "everything is public, every parameter is passed by reference, every variable is a variant" ethos that used to cause so much horrendous VB code to come into existence. Just a shame the error message doesn't give a clue to the real problem.

Implementing your own workflow type in Windows Workflow

Windows Workflow is extensible in almost every way. But one thing I didn't realise until seeing this example is that it is possible to implement your own workflow type as well. This looks really interesting. Although the state machine workflow models what I want to do pretty well, I'm not too happy with the UI of the designer so I might have to look into this further.

Saturday, June 16, 2007

Validator for a custom activity in Windows Workflow

Last time I talked about adding a property to a custom activity, this time I'll talk about validating the value of that property.

So we have a custom activity called UserActivity with a property called Form and we want to ensure the workflow designer has specified a value for this property. First thing to do is write our validator class, which looks like this - 

  class UserActivityValidator : ActivityValidator
  {
    public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
    {
      UserActivity activity = (UserActivity)obj;
      ValidationErrorCollection validationErrorCollection = base.Validate(manager, obj);

      // Don't validate when the activity is standalone  
      if (activity.Parent == null)
      {
        return validationErrorCollection;
      }
      
      // check Form property has been set
      if (string.IsNullOrEmpty(activity.Form))
        validationErrorCollection.Add(ValidationError.GetNotSetValidationError("Form"));

      return validationErrorCollection;
    }
  }

All we do is override the Validate method, call the base implementation, then check to see if the activity has a parent. This check ensures we don't validate the activity when we are designing and coding the activity itself. The next line does the actual work, using a helper method provided by the ValidationError class. This can be used in the most common type of validation, checking to ensure the property has a value. If that's not what you're doing, create an instance of the ValidationError class instead, using whichever constructor you need. 

Once we've written the class all that remains is to tell WF what class should be used to validate the activity, which we do by adding an attribute to the activity declaration.

[ActivityValidator(typeof(UserActivityValidator))]
public partial class UserActivity: HandleExternalEventActivity

And that's it. Now compiling a workflow assembly using the custom activity should show a compiler error if the Form property has not been set.

Friday, June 15, 2007

Activity properties in Windows Workflow

I initially thought Windows Workflow was horrendously complicated, needlessly so. I've since realised that it's just exceedingly extensible but one problem is that there aren't a great deal of internet resources to search through to find the answer. No surprise I guess since this is pretty new technology. So I've decided to add some little tidbits. I'll present a few very short posts about a very small subset of the functionality, a kind of Windows Workflow for dummies. Given that I'm a dummy myself, I am perfectly placed to do this I reckon. Of course since I'm still learning all about WF, these posts may be incomplete or plain wrong.

So first up, adding a property to a custom activity. Properties in WF don't work like properties in normal .NET classes, they are based around dependency properties, which are properties that can be attached to any class deriving from DependencyObject. All activities inherit from DependencyObject so they can use dependency properties. I believe dependency properties must be used when you want to let your workflow designers be able to bind their properties together. Normal properties would have no way of knowing how to update other bound properties when their value had changed.

What is probably worth mentioning at this point is that WPF also uses dependency properties and dependency objects. They look very similar to the WF ones, but they are defined in a different place (System.Windows for WPF, System.Workflow.ComponentModel for WF) so I can only assume there are some subtle differences between the two.

Anyway, here's some code for a simple WF property.

    public static DependencyProperty FormProperty = DependencyProperty.Register("Form", typeof(string), typeof(UserActivity));
    
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [Browsable(true)]
    [Description("The form to be displayed when the user initiates the activity")]
    public string Form
    {
      get
      {
        return ((string)(base.GetValue(UserActivity.FormProperty)));
      }
      set
      {
        base.SetValue(UserActivity.FormProperty, value);
      }
    }

So we have a custom activity called UserActivity with a property called Form. It looks like a normal property except the getters and setters use the GetValue and SetValue methods defined in the DependencyObject class. Beyond that all we need to do is register the dependency property. Once that is done, the property should appear just like any other in your workflow.

Thursday, June 14, 2007

Metastorm BPM 7.5 review

It's been around a while so it's about time I wrote down my thoughts. If you want to see the official line take a look at the Metastorm Press Release, I'm just going to talk about things that interest me, which in general means the core product and any .NET integration pieces.

One of the main reasons I wasn't particularly keen on version 7.0 was the requirement to disable DEP before installing. Although this was rectified in the first service release, installing still required disabling DEP, installing 7.0, re-enabling DEP then installing the service release. Thankfully this has all been fixed in 7.5 and I presume 7.5 can be installed straight over 6.6, although I haven't tried this yet.

I've been running with 7.5 web client against some 6.6 engines to get a feel for it. Although this isn't supported and it does seem to have a few issues, it's pretty useful for testing out the various client-side hacks we've employed in the past. There's not been any major innovation in the web client but it has had a few minor tweaks and I'm very much of the opinion that small things matter. The things I like are

  • the ability to kick off an action without opening the folder, this is a quite a productivity boost.
  • fixed headers on a grid. I'm sure it took an afternoon to implement but it will please many many people.
  • the error message when a required field has not been populated tells you the name of the field. Again, a minor change but shows someone is paying attention to the details

You can also add links from the web client to other sites. It's seem a bit of a random feature but I'm sure some people will love it.

The Designer on the other hand seems somewhat unloved, which is understandable given Metastorm have started on a complete rewrite of it. There aren't really any new features to speak of (except the .NET event delegation which I'll talk about shortly). There are some serious performance issues when using it, it seems to flicker constantly when moving around and adding fields to a form takes forever. I've had it crash a few times but I'm not sure it's any less stable than previous versions. The pointless dialog count is about the same, although I did find a new one when it couldn't locate the ELB file in the database so it asked if it should look for an XEL instead. Yes of course you should, do you really need to ask???

So onto the .NET integration. Initially I thought this was pretty poor but upon closer examination it is actually reasonably good. Tell the Designer you want to delegate your map events to a .NET assembly then go into Visual Studio and create an assembly for the map via a wizard. I thought this was the end of it, which left me wondering what would happen when I updated the procedure. It turns out there is some integration with Visual Studio but the installer didn't activate the add-in. Once I had that running, I was able to synchronise the procedure and assembly and deploy it.

There's a few problems with this integration though

  • not all events are covered. When button pressed and when the user selects a row in a grid are the obvious ones, but calculated fields and basically all places where e-Work formulae are used need to be available.
  • deploying the assembly requires stopping and starting the engine. Oddly, Metastorm have worked around this by stopping and starting the engine for you from Visual Studio rather than going down the more obvious route of using shadow copies. This is fine for a development machine but deployment to a production machine would be much easier without this requirement. ASP.NET has been doing it for years...
  • I think the Process Orchestrator requires a separate license purchase. Really this should be part of the main product and until it is, I don't think we'll be interested in using it.

.NET 3, worst... name... ever...

People have got their fingers burned in the past. Installing .NET 2 did cause some applications to break (OK, I can only think of Metastorm e-Work off the top of my head but most people I deal with are using that bit of software) so every time I tell them to install .NET 3 to use the wonders of Windows Workflow I have to include a disclaimer that .NET 3 isn't really a new version of .NET, it runs on top of .NET 2 so shouldn't break anything blah blah blah. Worst... name... ever...

Monday, June 11, 2007

Browsers getting interesting again

Well interesting is perhaps stretching it, but certainly confusing. IE7 has been out for a long time now and it seems to have stalled at about 30% usage. Then today Apple released a version of Safari for Windows. So now I have yet another browser to worry about, because I'm guessing quite a few people will start using it, because it's not Microsoft. Yeh, it's based on Mozilla but it's bound to be different to FireFox and it's bound to be different to Safari on the Mac.

And IE7 doesn't make life any easier. I can't forget about IE6, because most people are still using it, but enough people are using IE7 that I can't ignore that either. I will however continue to ignore Opera, Konqueror, Lynx, Netscape and any older versions of IE. Complain if you like, but I have better things to do with my time than worry about browsers with miniscule market share.

Friday, June 08, 2007

House prices to reach ten times salary? Bollocks

According to a report from a government think tank, house prices may be 10 times average salaries by 2025. It's all down to supply and demand apparently. Sure, looking at the figures in a simplistic fashion, with an increasing population and inadequate house building then house prices should continue to increase. But think about it a little longer and it clearly doesn't make any sense. Who will be buying these houses? First time buyers are already priced out of the market. Buy to letters might take up the slack but only if rents rise in line with house prices, otherwise they'd be better off investing their money elsewhere. So will rents rise as well? Given that a lot of our increase in population is driven by economic migrants, who generally are in pretty poorly paid jobs and renting, if rents rise dramatically, will they continue to come here? Probably not, since they'll be better off staying at home. So demand won't increase, meaning prices fall back to a more sensible level. There's also the question of whether the indigenous population would hang around with such ridiculous prices, meaning even less demand for housing.

On reflection, I suspect this report is purely some spin to get the idea of relaxing planning laws onto the agenda, thus enabling more house building. But whatever it's about, it's bollocks.

Wednesday, June 06, 2007

Great photo

IMAG0115How come my 4 year old daughter can take better photos than me? I have no idea what this is a photo of but I love it. She is also available for designing Olympics logos.

Tuesday, June 05, 2007

Bye bye eBay, hello Freecycle

I've sold lots of my old crap on eBay, but I've got a little fed up with it. Most of the stuff I've sold isn't high value so the hassle of eBay just isn't worth it. It takes a long time to put together the sale details, take pictures and package the item and post it. Then eBay takes their cut (whether you manage to sell your item or not) and PayPal takes a cut. Then there are the problem buyers, who fail to pay or have to be chased up. So for quite a lot of work, the returns are pretty slim.

So I've given up on it and I'm going to start giving stuff away on Freecycle. All I need to do is post a message and hopefully get a response. It has the added advantage of working at the local level, so goods aren't going to be transported half way across the world. Apparently there are over 3 million users already, pretty impressive.

Friday, June 01, 2007

Ah, so flickr does have a business model

I'd always presumed flickr was one of the web 2.0 sites that seemed to think it could somehow make money out of thin air. Then I was uploading some photos from the Random Pub Finder when I hit the site's 200 picture limit. Of course I can upgrade to a Pro account, but really it's not worth it for me. The only reason I was using flickr was to get some inbound links to the RPF and that isn't worth $24.95 a year. Because RPF's business model is non-existent...

New version of Windows Live Writer

livewriterThere is a new version of Windows Live Writer available. It looks prettier and has squiggly line spell checking but beyond that I don't know if it offers a lot. My problems with Live Writer in the past has been its inability to upload pictures to Blogger (which by all accounts is Blogger's fault) and its inability to check for new versions. I only found out about this new version after stumbling across a review of it. Admittedly, programs that do check for updates tend to upset me as well since they seem to require a new install every time I run them, so there's no pleasing me...

Anyhoo, I'll see if it uploads images now.

Time passes...

OK, so it can't upload images to Blogger directly but it will upload them to an FTP site and hook them into the page. The FTP integration is pretty sweet, although I guess it'll be no use to a lot of punters. But it solves my problem.

So that and the squiggly line spell-checking means I'll give it the thumbs up, even if it can't spell colour.

Some more time passes...

OK, perhaps I should have used the thing before writing a review. It now supports Blogger tags as well, meaning I'll now very rarely need to use the Blogger web interface, so even more thumbs up.

Thursday, May 31, 2007

A grey water experiment


We are constantly told that investing in a water butt to water our gardens is a good thing, it saves water and hence the planet. What is never mentioned is the fact that when you need it most, your water butt is probably not going to be much use to you. When it hasn't rained for several weeks and the inevitable hose-pipe ban is in place, you water-butt won't have a drop of water in it. Because of this, my water butt isn't getting filled up with rain water. Although the general consensus seems to be that using grey water for watering your garden may not be a good idea, as ever I decided to ignore any advice given to me and hooked our water butt up to the outlet from our bathroom. I'm not sure all houses are configured like ours, but the used water from our sink and bath goes out through a couple of pipes into a drainpipe so using this for the water butt was very simple.


And it seems to work fine. The water can smell a bit, but it doesn't seem to kill our plants and we can water the garden without feeling any guilt. One thing, remember not to wee in the shower... Although I'm sure you'd never do such a thing.

Wednesday, May 23, 2007

More on JScript exceptions

I don't know much about JScript, so when I need to find out anything I head off to Google. So that's what I did when I wanted to know about throwing exceptions. Almost every example I've seen shows something like - 

throw "something went wrong";

Initially I thought this would be wrapped up as an exception by the JScript runtime, but it turns out it is actually a string that gets thrown. Which may be fine in some cases. The problem comes when you want to catch exceptions. If you want to catch all exceptions thrown in a bit of code (yeh I know, bad idea, but this is JScript not software engineering...) then things will get complicated. The runtime will throw exceptions but your own code is throwing strings, so you have to deal with them differently. Turns out the solution is pretty straighforward -

throw new Error("something went wrong");

Now everything is an exception and life is good.

As a side note, I've not considered throwing anything other than an exception before. C# doesn't allow it, although I guess IL probably does since it needs to support JScript.NET. Delphi partially allows it, you can throw any object you like but not strings since they aren't proper objects in the Delphi world. But it's not clear to me why you'd ever want/need to do it...

The new Google Analytics

So there's a new version of Google Analytics available which is lovely and shiny and new. But they seem to have removed the ability to show data by month or hour. Monthly data is useful to get an overview of how traffic is increasing/decreasing over time, since daily data is too volatile. Hourly data is great to see what's happening on your site today.

Tuesday, May 15, 2007

Why I'm not using UAC

Lots of people keep saying I should switch User Access Control on. So I thought I'd give it a try since I wanted to see how painful it was or if it was actually not so painful after all. And of course I'd like my software to run with UAC enabled without causing any issues.

But here's the problem. Vista is already screwed up on my machine, what with no sound, a SonicWall VPN client that screws up TCP/IP and complete machine freezes every couple of days. And UAC makes it even worse! Internet Explorer has some serious problems when UAC is enabled. It won't print, links that open new windows don't work and the dropdown menu on the back button doesn't work. Explorer always opens folders in a new window, even though it is set up to reuse the same window. Lots of applications won't work at all unless I run them as administrator (Visual Studio being the obvious example) which rather defeats the point of UAC. And these are the problems I've found after using UAC for about two days so there are bound to be more.

And of course I've not even mentioned the annoying dialogs that appear fairly regularly to disrupt my workflow.

So in principle using UAC may be a good thing and dogfooding it constantly with my own code would be a nice thing to do, but in practice it is too painful and I'm putting it back in its box until SP1 comes out. 

Monday, May 14, 2007

How the web was won

In one of my previous jobs I was responsible for helping to develop a native Windows client. At the same time, a web client was developed by another team. Although the native client had some advantages (it was much quicker and generally more reliable*), the web client was much more successful. I suspect this was mostly down to the deployment issues you have with a native Windows client. Even now in a world of ClickOnce deployment, web clients are still preferred by a lot of customers.

But one thing that has become apparent to me since doing more integration work is that web clients also have one other major advantage over native clients. Whether you intend to or not, when you develop a web client you get a plugin architecture for free. Doing the same thing in a native client takes a lot of thought and development time, but anybody can hack around with a web client to make it look like they want or do what they want, via CSS, Javascript or even server-side hacks with some systems. Yeh, these hacks may well break between releases, but it still makes web stuff very attractive to integrators. To do the same thing in a native client would likely involve going back to the vendor to ask for a change in functionality or an extensibility point where code can be plugged in, which might take months, rather than an afternoon of fiddling around. And that I reckon is the killer reason why the web has won.

* This was no reflection of the development team working on the web client. It's just that, all other things being equal, writing a native Windows client is much simpler than writing a web client.

Tuesday, May 08, 2007

Virtual Earth vs Google Maps part 3

Many moons ago I tried out Windows Live Local (now called Virtual Earth I think) and decided it didn't meet my needs since it didn't show any London tube stations or train stations which made getting round London kind of difficult. I checked it out again today and it now has got tube and train stations. This is actually a step ahead of Google Maps that only show tube stations. However it seems to be a bit buggy currently since sometimes stations appear at one zoom level, disappear when you zoom in, then re-appear again after more zooming, which I presume is not by design. Check out Herne Hill.

So finally these Web 2.0 sites have almost caught up with Streetmap, which I don't think has been updated in about 5 years. Kind of shows up the 'we move in a fast moving industry' mantra as a complete lie.  

Friday, May 04, 2007

Even more on tick marks in HTML

It's always the seemingly simplest probems that have the most long-winded solutions. So my last post was wrong, although that solution worked in some HTML docs, as soon as I switched to HTML 4.01 FireFox started showing extra crud, as you'll notice if you're viewing this in FireFox. So here's a solution for tick marks in HTML 4.01, still using conditional comments, which works in IE6, IE7 and FireFox 2, probably...

<![if !IE]> &#10003;<![endif]>
<!--[if IE 7]> &#10003;<![endif]-->
<!--[if lt IE 7]> <input type="checkbox" disabled="disabled" checked="checked" /> <![endif]-->

And to complete the whole thing, here's an XSLT template to put a tick mark in your HTML output.

  <xsl:template name="outputTickMark">
<xsl:text disable-output-escaping="yes">
&lt;![if !IE]&gt; &#10003; &lt;![endif]&gt;
&lt;!--[if IE 7]&gt; &#10003; &lt;![endif]--&gt;
&lt;!--[if lt IE 7]&gt; &lt;input type="checkbox" disabled="disabled" checked="checked" /&gt; &lt;![endif]--&gt;
</xsl:text>
</xsl:template>

and here's a tick mark that might work this time.



By the way, I found this article on conditional comments particularly useful.

Update: The absolute final word on this subject is here

Wednesday, May 02, 2007

More on tick marks in HTML

My original post about displaying a tick mark in HTML is one of the more popular hits on this site. This fact and the increasing usage of IE7 and FireFox drove me to come up with a nicer solution than the one proposed there (using the lowest common denominator checkbox input field).

Internet Explorer supports something called conditional comments. Using these you can serve up different content to different browsers directly in your HTML, rather than doing funky server-side things. I have to use this approach since I'm generating a static HTML file. So here is the markup -

<!--[if IE 7]><!--> 
&#10003;
<!--<![endif]-->
<!--[if lt IE 7]>
<input type="checkbox" disabled checked>
<!--<![endif]-->

The if IE 7 comment will be rendered by IE7 and any other browser. The if lt IE 7 comment should only be rendered by IE6 and below. I've tested it in IE6, IE7 and FireFox 2, which covers enough bases for my needs. And here is the output, which hopefully should show some kind of tick mark.


Update - there's a more up to date and correct version of this available here

JScript try catch syntax

I alway forget the syntax for JScript exception handling and searching on the web always seems to bring up the wrong thing, so here is an example as my own personal reminder.

    try
    {
        // do stuff
        return "blah";    
    }
    catch(e)
    {
        return e.message;
    }

Yeh, I know, I should be able to remember that... I blame it on the crap JScript editors, none of which provide the mental crutch of code completion.

Remember too, JScript requires the e, you can't use catch().

Sunday, April 29, 2007

SonicWall VPN Client for Vista beta

One of the problems I've had with Vista is the fact the SonicWall VPN Client doesn't work with it and SonicWall haven't released a new version for Vista. Well they've finally got their arse in gear and released a beta version of the VPN client.

Send an email with “subscribe” as the subject of the email to gvc40-join@listserv.sonicwall.com if you’re interested in participating in the beta program.

It kind of works for me, although after a while Vista complains that two PCs are connected to the network with the same IP address. Then everything falls apart. Restarting the VPN client and my wireless connection fixes the problem.

Addendum (28th October) - The SonicWall VPN client for Vista is now out of beta and it is all working fine for me now.

Phew, it's not just me

First off, Canary Wharf is one weird place. I haven't been there for a while but it's almost like it has a dress code, everybody is wearing a suit, or shirt and trousers at the very least. So when I rolled out of the station in my old jeans with a few days facial hair growth I thought I'd be picked up by the police as a terrorist suspect. Fortunately I didn't bump into any police men. I was fairly sure Credit Suisse wouldn't let me in either but perhaps they'd been warned that a group of geeks was visiting.

I was attending a London .NET User Group meeting for a talk about Windows Workflow. The first part of this talk was reasonably interesting, although it was clear the presenter has been reading the same book as me, so a lot of it wasn't new to me. But things got interesting when one of the guys who's been developing a WF application at Credit Suisse talked about their experiences. The things that came up were pretty much the same issues that I've come up against.

First, the versioning story just isn't there yet. Storing serialized objects in the database means upgrading workflows can be somewhat tricky, since after an upgrade the deserialization is unlikely to work. Which is why I think using serialization is generally a bad design decision, unless the life of the serialized objects will be very short (copying to the clipboard, sending an object across the wire etc). So they've been forced to write some code to get pertinent information from the live workflow, destroy the instance and recreate it using the new workflow version and re-assign the state data.

The second problem is actually caused by the same issue. You might think since the workflow is stored in a SQL Server database, you could just query it to find all the instances that have a particular property, but again the fact that the worklfows are opaque blobs means this isn't possible. They worked around this by extending the persistence service to store relevant data relationally whenever a workflow is persisted.

It's nice to know that most problems can be worked around. There is so much flexibility in the WF hosting and runtime that almost all of the default implementations can be re-implemented to do what you want. I just hope this doesn't lead to Microsoft not fixing what are clearly problems with the default implementations.

One final thing that was mentioned that I was coming round to thinking myself is that state machine workflows are actually the way to go for many kinds of problems. Most of the talk in books and the web seems to be about sequential workflows but I think state machines are actually a better way to go most of the time. Of course this may just be due to my background in Metastorm e-Work which uses something closer to a state machine than a sequential workflow.  

Friday, April 27, 2007

More Vista pain

I finally cracked. I'd had enough of the complete lack of support from Dell about my lack of sound on Vista. The guy said he'd get back to me, of course he never did. Emails to Dell got no response. Yeh I could kick off another support call but it would just involve several hours of them uninstalling and installing drivers and failing to get anywhere, which I've been doing pretty well myself for several months. To top it all off, Dell sent me an email asking me to fill in a questionaire about my customer service experience. When I clicked on the link, all I got was a message telling me my access had expired. They even sent me a reminder to fill in the survey a few days later, again access expired. Hey Dell, there may be a reason you're not getting much feedback on your customer service...

So I thought, stuff this I'll buy a new soundcard, £20 and all my pain will go away. It all started out really well, Vista recognised it, went away to find the drivers and everything installed correctly, or so it said. Then I tried to actually get some sound out of the PC, nothing, nada, zilch. I pressed the dreaded 'Test' button, 'Failed to play test tone'. Aaaagh! Cheers Vista, why couldn't you play the test tone? I almost long for the usual massive hex number, at least something to work from. The event log is empty.

So now I have no idea what the problem is caused by. Presumably it isn't a driver or hardware problem, so is my Vista install broken? I really don't want to reinstall the whole thing.

On another note, Vista has developed a nice habit of locking up completely, the mouse stops moving, the keyboard doesn't respond, CTRL-ALT-DEL does nothing, the only thing I can do is hit the reset button.

Five years, thousands of developers and what do we have? A prettier version of XP that for me is less stable and other than IIS 7 doesn't really offer much new.

Tuesday, April 24, 2007

Recruiters - how about reading my CV?

I'm not looking for a job currently but my CV must be out there somewhere on the internet from the last time I was looking for work. And it's pretty clear quite a few recruiters just do a keyword search and send out emails without actually ever reading my CV. So I've had emails about C++ positions, which I haven't touched for almost 10 years, PHP jobs, which I'm familar with but wouldn't consider a strong point, testing jobs and even jobs in the US.

Maybe the 'throw enough shit at the wall and some of it will stick' technique works but I suspect it's not highly effective. I tend to remember the recruiters who actually bother to ring me up and actually listen to what I have to say and if I ever need to contact a recruiter again I'll go to them first, since they appear to care about placing people in the right job. The others are simply helping to confirm the bad reputation that recruiters have.

Monday, April 23, 2007

The ZX Spectrum is 25 years old

And how old does that make me feel?* Get an emulator and download some great games at World of Spectrum, read more about the mad genius Clive Sinclair at Wikipedia.

* Short answer - very

Friday, April 20, 2007

How to solve virtually any technical problem

People ask me technical questions all the time. I have no idea why, I don't know any more than anyone, perhaps the fact I can find the answer to these problems leads people to think I actually know the answer. So I'm going to present to you the two steps to find the solution to almost any technical problem.

Google for it - The internet holds the answer to pretty much any question you can ask. The key to finding the answer is to know how to search. This is pretty much trial and error, if one search doesn't bring up the answer, try another phrase until you get something that looks related. If you're getting a specific error message, search for that.

Another point to make here is that if you can't find what you're looking for, consider whether what you're trying to do is a good thing to be doing. I remember trying to find information about hosting .NET WinForms controls in Internet Explorer and not having much luck at all. This was a big red warning light to me that perhaps even attempting to do this kind of work was a bad idea since it would appear very few other people were doing it.

Infinite monkeys - You know the saying, given an infinite number of monkeys with an infinite number of typewriters and infinite time, one of them will produce the works of Shakespeare. So it is with fixing technical problems. Try enough things and one of them will be the right answer. The key to this technique is only to make one change at once, otherwise one of your changes may fix the problem and your other change might break it again. Or you forget one of the changes you made and don't set it back to what it was before and you're in an even worse position.

So now I've given you the tools, you don't need to bother me anymore, OK? 

Tuesday, April 17, 2007

Buy less crap

The idea touted by Red that somehow we can help charities by buying more stuff is frankly preposterous. The idea put forward by Buy (Less) Crap is to not purchase more consumer tat and donate the money we would have spent to charity instead. Now that seems much more sensible. How many fecking iPods do we need anyway?

Monday, April 16, 2007

Doogal's guide to parenting

You can read every book by Gina Ford you want but from my vast experience (ahem) there are only two things you need to master when bringing up a child.

Bribery and blackmail. 

Friday, April 13, 2007

No really, total rewrites don't make sense

I just found this fairly old blog post about rewriting Delphi apps in C# which sits so well with how I think (and puts the point across far more eloquently than I could ever do) that I had to post the link.

The bottom line is if you have an old code base to maintain, rewriting it completely may seem like an attractive option, but there are other ways of getting to the same place without throwing away years of investment in that old code base.

Wednesday, April 11, 2007

Swimming to New York

I'm not the first person to point out the humorous suggestion from Google Maps when you ask for directions from the UK to the US (see step 36), but it got me thinking.

One of the shortcomings of Google Maps is that it has no knowledge of anything but driving so it won't advise me to jump on a plane if I want to get to the US. For that matter if I ask it how to get to London Waterloo from my house, it doesn't know anything about trains so again will tell me how to drive there. It doesn't know anything about congestion charging or parking charges either so doesn't realise only the super rich or super stupid would ever consider driving into central London.

Transport for London has the opposite problem because it completely ignores the driving option, which in some circumstances might make sense (try taking public transport from SW London to SE London without dying of old age on the way for instance).

A while ago I wanted to investigate transport options for getting to my dad's house in Spain. Here things get even more complicated. There are loads of different websites with information about planes, trains and ferries but I had to find them and then compare prices, times etc.

But none of this stuff is properly integrated. The data is mostly available so it just requires an uber geek to figure how to build a mashup site that takes data from all these different sites and pulls it together in one place. I need to be able to enter a start location, an end location and then be able to get results sorted on which is cheapest or fastest or most environmentally friendly and lets me buy all the required tickets. Presumably if somebody did make such a thing, they could probably finance it by getting commission on the bookings.

And another thought, my guess is one of the reasons people who may be concerned about the environment still take flights rather than taking the overland option is that it is so much easier to book a flight than to book the alternative train and ferry. Oh, and cheaper... Er, and quicker... But at least this site could remove one obstacle.

So, any takers?

Tuesday, April 10, 2007

Maintainable XSLT

Doing a search on Google for 'maintainable XSLT' doesn't throw up a great deal but it seems like something that is really needed. There seem to be lots of resources out there telling me how to code in XSLT, but I haven't found any telling me how to do it elegantly or testably (is that even a word?). I've been working on and off on a project that takes an XML file and spits out a HTML representation of it. When I started off I decided to go with XSLT, rather than generating the output in C# using an XmlWriter. I still think that was the right decision. Even though XSLT is pretty verbose, generating HTML any other way isn't too concise or pretty either.

It's not a big XSLT file by any means (about 1500 lines), but I'm already finding it hard to manage and the tool support just isn't there. Visual Studio 2005 is a step up from 2003, but there are still plenty of things missing. There doesn't seem to be a way to get an overview of an XSLT file by showing what templates are in it. This means some of the possible advantages of splitting it out into separate templates are lost. But even if I was to split it out into separate templates, the markup required to call a template means the XSLT file might actually get bigger due to the calls to the templates!

Another thing I'd like to see is the ability to go to the definition of a template from a call-template call, but that doesn't seem to be available. How about regions, like in C#? OK, I want all the features of the C# editor in XSLT, am I barking up the wrong tree? XSLT is fairly different to C# and I'm coming at this with the mindset of a C# programmer but are there different ways of handling this complexity? Where should I be looking for this information?

Wednesday, April 04, 2007

Method calls on value types and boxing

It was DevWeek 2005 and I was in a session with Jeff Richter about low level .NET things and I foolishly asked him a question. He looked at me as if I had asked the most dumb question ever asked by anyone, so I decided not to follow up on my question, even though I didn't feel he'd really given me the answer I was looking for.

So what was the question? Well he was describing boxing of value types and how it can cause performance problems so it was best to avoid it where possible, even though it's not always clear when boxing is occurring. I'd asked didn't boxing need to occur for any method call on a value type. On reflection I'm not sure this was a particularly dumb question but I've never really got fully to the bottom of it, mainly because it's never really been much of an issue to me. But here's my take on it, which may or may not be accurate. Boxing is only going to happen if the method call is a virtual method where the value type doesn't override the base object implementation. Now it might be boxing would also be required if the value type did override the base method (assuming boxing is required to get the virtual method table), if value types could be inherited from. But they can't so the discussion is kind of irrelevant. This may well be why value types can't be inherited from, but this is all frankly getting way too complicated for me to understand, so I'll quickly move on.

Anyway to illustrate the point, here's a little test C# application.

namespace ConsoleApplication2
{
  struct ValTypeTest
  {
    int val;

    public ValTypeTest(int val)
    {
      this.val=val;
    }

    public override string ToString()
    {
      return val.ToString();
    }
  }

  class Class1
  {
    [STAThread]
    static void Main(string[] args)
    {
      ValTypeTest thing = new ValTypeTest(34);
      Console.WriteLine(thing.ToString());
      Console.WriteLine(thing.GetHashCode());

      int number = 34;
      Console.WriteLine(number.ToString());
      Console.WriteLine(number.GetHashCode());

      Console.ReadLine();
    }
  }
}

If you look at the IL code for this in Reflector using .NET 1.1 (I'll explain why I'm using .NET 1.1 shortly), you'll see this -

.method private hidebysig static void Main(string[] args) cil managed
{
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
    .entrypoint
    .maxstack 2
    .locals init (
        [0] valuetype ConsoleApplication2.ValTypeTest thing,
        [1] int32 number)
    L_0000: ldloca.s thing
    L_0002: ldc.i4.s 0x22
    L_0004: call instance void ConsoleApplication2.ValTypeTest::.ctor(int32)
    L_0009: ldloca.s thing
    L_000b: call instance string ConsoleApplication2.ValTypeTest::ToString()
    L_0010: call void [mscorlib]System.Console::WriteLine(string)
    L_0015: ldloc.0 
    L_0016: box ConsoleApplication2.ValTypeTest
    L_001b: callvirt instance int32 [mscorlib]System.ValueType::GetHashCode()
    L_0020: call void [mscorlib]System.Console::WriteLine(int32)
    L_0025: ldc.i4.s 0x22
    L_0027: stloc.1 
    L_0028: ldloca.s number
    L_002a: call instance string [mscorlib]System.Int32::ToString()
    L_002f: call void [mscorlib]System.Console::WriteLine(string)
    L_0034: ldloca.s number
    L_0036: call instance int32 [mscorlib]System.Int32::GetHashCode()
    L_003b: call void [mscorlib]System.Console::WriteLine(int32)
    L_0040: call string [mscorlib]System.Console::ReadLine()
    L_0045: pop 
    L_0046: ret 
}

As you can see, the call to GetHashCode causes the value type to be boxed, whereas the call to ToString doesn't, because ToString has been overridden whereas GetHashCode hasn't been. But if we look at the IL code in .NET 2, it looks like this

.method private hidebysig static void Main(string[] args) cil managed
{
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
    .entrypoint
    .maxstack 2
    .locals init (
        [0] valuetype ConsoleApplication2.ValTypeTest thing,
        [1] int32 number)
    L_0000: nop 
    L_0001: ldloca.s thing
    L_0003: ldc.i4.s 0x22
    L_0005: call instance void ConsoleApplication2.ValTypeTest::.ctor(int32)
    L_000a: nop 
    L_000b: ldloca.s thing
    L_000d: constrained ConsoleApplication2.ValTypeTest
    L_0013: callvirt instance string [mscorlib]System.Object::ToString()
    L_0018: call void [mscorlib]System.Console::WriteLine(string)
    L_001d: nop 
    L_001e: ldloca.s thing
    L_0020: constrained ConsoleApplication2.ValTypeTest
    L_0026: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
    L_002b: call void [mscorlib]System.Console::WriteLine(int32)
    L_0030: nop 
    L_0031: ldc.i4.s 0x22
    L_0033: stloc.1 
    L_0034: ldloca.s number
    L_0036: call instance string [mscorlib]System.Int32::ToString()
    L_003b: call void [mscorlib]System.Console::WriteLine(string)
    L_0040: nop 
    L_0041: ldloca.s number
    L_0043: call instance int32 [mscorlib]System.Int32::GetHashCode()
    L_0048: call void [mscorlib]System.Console::WriteLine(int32)
    L_004d: nop 
    L_004e: call string [mscorlib]System.Console::ReadLine()
    L_0053: pop 
    L_0054: ret 
}

Now it's no longer clear whether boxing occurs or not, because both calls use the IL constrained opcode. It would appear this opcode has been added for a variety of reasons, but one of them is to help with binary compatibility, so if a value type changes so it adds an override for a virtual method or removes an override, it will still work without any changes to the calling app. The downside of this is that boxing is even more hard to spot than it was before.

Saying that, worrying about boxing is often not really worth the trouble. It smells of premature optimization and in most cases isn't likely to cause problems. Saying that, it does suggest if you're writing your own value types, you're probably going to want to override most of object's base methods, particularly GetHashCode, which is used in quite a lot of places.

Tuesday, April 03, 2007

The number one internet resource for Metastorm crap

It's always interesting to look through the search terms that have led people here or to the other sites I have something to do with. It's certainly a great work avoidance tactic. So I was pleased to see that the term Metastorm crap had landed somebody here. Then when I did a search for Metastorm crap myself I was even more pleased to see I'm number one for that search term. So there you are, come here for all the Metastorm crap you could ever need...

In fact I've just done a search for Metastorm shit and I'm number one for that as well! I must point out at this point that although the words may have appeared on the same page, they didn't actually appear in the same context... OK, they do now, but I'm not implying anything, OK?

Saturday, March 31, 2007

Some useful web resources

I came across a couple of useful websites today which I thought I'd post here for others and for my own information.

First is Domain Tools, which provides the usual Who Is lookup and a whole lot more. Most useful I thought was the SEO text browser that shows how a website looks to a search engine and gives a few pointers to how your pages could be improved for SEO.

That site led me to About Us, that is an interesting attempt at generating a Wiki for websites. I imagine this could become an interesting site to browse and find new sites to visit. If nothing else, adding your site will add another inbound link to your site.

Another useful resource that I didn't find today but follows on from the SEO text browser above is the HTML Validator provided by the W3C. Running your site through this will show any problems with your HTML code. I've no idea if valid HTML will improve your search rankings but I'm fairly certain it won't hinder them.

Friday, March 30, 2007

The evil #if DEBUG

I recently received a bug report for an ASP.NET site I've been maintaining but when I went off to test out the problem running the site through Visual Studio everything worked fine. Slightly confused, I went back to the live site and immediately reproduced the problem. So I thought the problem must be caused by the site not being in sync with the the development box so I uploaded the latest code. The problem was still occurring... I was fairly unfamiliar with the code so I started stepping through it. You've no doubt guessed the problem by now from the title of this post. There was some code wrapped in an #if DEBUG directive that actually overrode the parameters passed into the page via a query string. I removed that and the problem on the live site was reproducible in Visual Studio. But I wasted an hour or so getting to the bottom of it.

So the moral of the story is don't change behaviour of your code with #if DEBUG statements. The only use I've found for #if DEBUG is to log things, and generally that can be avoided through the use of the trace listener infrastructure. 

Monday, March 26, 2007

Not everything is high priority

If every email you send to me is marked as high priority, they tend to lose their impact, I assume none of them are important. What will you do when you really do want to send me a high priority email? There is no priority zero...

I presume you're familiar with the boy who cried wolf?

Saturday, March 24, 2007

Meaningful descriptions for enums

Getting a string representation of an enum is pretty simple, just call ToString() on the enum. However, we often want to have a more user-friendly string to display to the user, that includes spaces and the like. I guess one way of achieving this would be to override ToString on our enum class, except you can't override ToString in an enum... And how to go the other way, from the string representation to the enum? The solution set out below uses an EnumDescription attribute that can be applied to the values of an enum. Now I can't claim to have come up with the idea for this, I think I first saw it on Richard Blewett's blog, but his archive doesn't seem to go back as far as it should. Saying that, I've also made quite a few alterations so I can claim some of this code as my own. The use of generics means it will only work with .NET 2.

using System;
using System.Reflection;

namespace Utils
{
  /// <summary>
  /// Attribute used to give textual descriptions to enums.
  /// </summary>
  [AttributeUsage(AttributeTargets.Field)]
  public sealed class EnumDescriptionAttribute : Attribute
  {
    /// <summary>
    /// Creates a new <see cref="EnumDescriptionAttribute"/> instance.
    /// </summary>
    /// <param name="text">Text.</param>
    public EnumDescriptionAttribute(string text)
    {
      this.text = text;
    }

    private string text;
    /// <summary>
    /// Gets the text description.
    /// </summary>
    public string Text
    {
      get
      {
        return text;
      }
    }
  }

  /// <summary>
  /// Helper class to get descriptions of enums.
  /// </summary>
  public sealed class EnumDescription
  {
    private EnumDescription()
    {
    }

    /// <summary>
    /// Gets the description of the specified enum. If there is no description attribute, returns the 
    /// enum as a string
    /// </summary>
    /// <param name="e">The enum to get the description of.</param>
    public static string GetDescription(Enum e)
    {
      string ret = e.ToString();
      Type t = e.GetType();
      MemberInfo[] members = t.GetMember(ret);
      if (members != null && members.Length == 1)
      {
        object[] attrs = members[0].GetCustomAttributes(typeof(EnumDescriptionAttribute), false);
        if (attrs.Length == 1)
        {
          ret = ((EnumDescriptionAttribute)attrs[0]).Text;
        }
      }
      return ret;
    }

    /// <summary>
    /// Gets the enum value from its description.
    /// </summary>
    /// <param name="description">Enum description.</param>
    public static T GetEnum<T>(string description)
    {
      Type type = typeof(T);
      FieldInfo[] fields = type.GetFields();
      foreach (FieldInfo info in fields)
      {
        // if the passed-in description is the name of the Enum, return that
        if (info.Name == description)
          return (T)Enum.Parse(type, info.Name);

        // otherwise look at descriptions
        object[] attrs = info.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);
        if (attrs.Length == 1)
        {
          if (((EnumDescriptionAttribute)attrs[0]).Text == description)
          {
            return (T)Enum.Parse(type, info.Name);
          }
        }
      }

      return default(T);
    }
  }
}

Usage is something like follows

  public enum LogEntryType
  {
    Workflow,
    [EnumDescription("User information")]
    User,
    [EnumDescription("Workflow runtime")]
    Runtime,
    Error
  }

  string logEntryTypeString = EnumDescription.GetDescription(logEntryType);

  LogEntryType eventType = EnumDescription.GetEnum<LogEntryType>(logEntryTypeString);

Wednesday, March 21, 2007

Not liking Vista so much

Doing a search on Google for 'liking Vista' brings up this site as the third in the list. I'm not triyng to show off by saying this, but it perhaps shows that there's not that many people who are liking using Vista. And I'm slowly moving from the liking Vista camp to the not liking Vista so much camp. To be fair most of the problems I'm having probably aren't Microsoft's fault but they are still annoying.

My major problem is the lack of sound coming out of my Dell Dimension machine. This was forgivable initially since I installed Vista before it hit the shops but even though Dell have released new drivers I still can't get anything to come out of my speakers. Vista thinks everything is OK, the only indication that something is wrong is the 'Failed to play test tone' error message when I do a test of my speakers. I've contacted Dell and talked to a tech support guy who said he'd get back to me and I've never heard anything from him since. It seems like other people are having the same problem so I'm hoping this should be resolved soon. Good job I've got an XP machine with sound for Skype.

Next up is my USB wireless connection. It works fine but every time Vista starts up it tells me the driver has been blocked (5 times for good measure) and doesn't tell me how to get rid of the error message.

Finally, SonicWall haven't released a version of their VPN client that works with Vista. Apparently they should have one out by Q2. Again my XP machine has saved me, since I need to use SonicWall virtually every day.

Dude, where's my AdSense revenue?

I was never going to become rich from my AdSense income, but I used to get a small amount of spending money from it. But over the last few months, even though I'm getting more hits to my sites, I'm getting less and less money from ads. I think it's down to the fact that I'm getting less money per click. There was a time when some clicks would earn me a dollar or more (which, to be fair, seems ridiculous) but now I'm lucky to get more than 20 cents per click. So I guess the question is whether there is a general downward trend in prices being paid for clicks or if Google is taking a bigger slice of the action. Of course it could be just me who's suffering from this and it may be something specific to my sites causing the lack of cash. 

Tuesday, March 20, 2007

Twitter - What the ...?

I don't get it, why would I want to use it? Why would I even want to read it, do I really care that complete strangers are on their way to work or having a dump? Even if there were people on there I knew, I wouldn't be interested. "Hmm, it's 11pm at night, what do you know, my rate Roger is off to bed, what a fecking surprise"

I believe we are meant to be looking for ways to save energy these days, so if anybody has access to the power switch on the Twitter server, just turn it off. You'll be saving us from the most pointless website ever and helping the environment at the same time.

Sunday, March 18, 2007

Has my fire really gone out?

So Paul Weller asked in 1993, to which the obvious answer was "In short yes. The Jam were pretty good, The Style Council had their moments (although sometimes in an unintentionally laugh out loud way) but your solo career has been pretty unspectacular". Just like the vast majority of musicians, Weller started suffering from Musician's Dementia when he hit the age of 30. Rather than produce anything worth listening to, they start to make sad facsimiles of their older material or they try and branch out into new genres and tend to look ridiculous.

So it is also true of IT? Do programmers in their 30s suffer from Programmer's Dementia? Have I got it? Would I even recognise it if I did have it? Am I just producing a sad facsimile of what I was doing in my 20s? Have I lost the ability to pick up new skills? Is there a good reason why coders over 30 are viewed with suspicion?

Or is this just the start of my mid-life crisis? Surely 10 years too soon.

Friday, March 16, 2007

Google, you are such a tease

You really are. The Random Pub Finder gets an inbound link and you start indexing all our pages, we get lots of hits, then a couple of weeks later our pages start dropping out of your index again. It's a little bit frustrating. And perhaps more concerning is the power you wield. But so long as you stick to your 'Do no evil' mission statement, I'm sure there isn't a problem, right?

Thursday, March 15, 2007

WFPad - DIY workflow designer

So previously I'd thought writing our own designer for Windows Workflow might be too time consuming, but today I found this lovely little designer which could be the basis for our own designer. It doesn't have any support for adding code to a workflow but I consider that a plus point. I don't actually want our business analysts to even see a code editor. If we can import and export to Visual Studio whilst keeping any code behind intact this may be a marvellous solution. Keep watching...  

Tuesday, March 13, 2007

Loving Windows Workflow, hating Windows Workflow

I've been messing around with Windows Workflow lately and it's a bit of a love-hate relationship. Here's what I love, the fact I've got a workflow runtime that I can host in any application and, even better, it costs nothing. Hosting the runtime and persisting workflows to SQL Server is also reasonably straightforward, although I'm not sure my implementation is rock solid (I'm sure I'll find out if/when we go live). I love the fact I can develop workflows in Visual Studio and I can drop into C# whenever the activities provided don't quite do what I want. I love being able to write my own activities so I can parcel up commonly used functionality. I love being able to plug activities and properties together without writing any code. But then I'd like to think a business person might be able to design workflows and I can't see them picking up Visual Studio to do that. OK, I can host the Designer as well, but that's a pretty big job that I don't have time to do. I guess we could look at using SharePoint or BizTalk, but that would rather defeat the purpose of using Windows Workflow, which is primarily the cost. This seems to be the major problem workflow has always faced, making it simple enough for your average business person to understood (not suggesting they are simple of course...) but powerful enough so it can be used for real integration work. Coming from a background of using Metastorm e-Work there's a few obvious things missing from Windows Workflow that are available in e-Work. One is roles management, the other is easy forms integration. I guess both can be achieved but I need to investigate how easy it will be to do them, especially integrating forms in a fairly generic way. There is a good reason for not providing these features, since Windows Workflow is meant to be database and client platform agnostic. As ever, making a product as flexible as possible tends to make it somewhat complex and I think Windows Workflow has turned up the flexibility to 11. I have a few other niggles. Versioning doesn't seem to be supported. Workflows are persisted using .NET serialization so rebuilding a workflow assembly can cause any running workflows to fail. In fact I've had situations where I'm unable to do anything with a workflow, even terminate it. One other problem is there seems to be no simple way of getting data in and out of a workflow, without designing this data exchange into the workflow itself, something I was hoping to avoid since I was hoping to be able to do it in a generic way. But overall, given it's a version 1 product, I think Windows Workflow is pretty cool. Hopefully some of my problems are just caused by not understanding how it all fits together, so it's time for me to go and read a book.

Thursday, March 08, 2007

Panic Over - Climate Change is a Swindle

I was under the impression that we were all in agreement, that climate change is happening and it's out fault. But according to Channel 4's documentary this evening, we've all been conned. Apparently, although historical rises in CO2 do match rises in temperature, it's the rises in temperrature that drive the rises in CO2 rather than the other way round. I get the feeling that if this were the case, there would be a few more dissenting voices in the scientific community so I won't be going out to buy a mechanical hippo tomorrow. Even if it is true, the fact is fossil fuels will run out one day so we have to find alternatives, and the sooner we start to do that the better.

Sunday, March 04, 2007

Is Peak Oil here?

Apparently Saudi oil production was down 8% in 2006. I don't know enough about the subject to know if this is important or not, but it makes for interesting reading. http://www.theoildrum.com/node/2325

New template

I've finally taken the plunge and upgraded my template for the latest version of the Blogger software. I think the design looks much slicker and the template editing tools in Blogger look more powerful, although I haven't really done much with them yet. I've lost a few things from my template which I will be adding back when I notice them. Edit- Yes, the template editing is much better. Adding tags to posts is a pretty poor experience though. It looks much like adding tags in GMail but the usability is pretty poor. If you add a tag to a post, you're then sent back to the top of the first page of your posts after applying the tag. You can choose to show 300 posts per page but then it all gets very slow and after applying a tag to a post, the post remains selected. This means you have to trawl through all your posts again to find the post to deselect it before adding any other tags.

Wednesday, February 21, 2007

Sorting in a DataGrid

There doesn't seem to be a lot of information around on getting sorting in a WinForms DataGrid to work with custom collections. So I've done the hard work for you and here it is. First I wrote a sorting class, hopefully generic enough to sort pretty much any kind of simple data types (if your collection contains other objects you'll probably need to update this somewhat).

  internal class Comparer : IComparer
  {
    private PropertyDescriptor sortProperty;
    private ListSortDirection sortDirection;
    public Comparer(PropertyDescriptor sortProperty, ListSortDirection sortDirection)
    {
      this.sortProperty = sortProperty;
      this.sortDirection = sortDirection;
    }

    #region IComparer Members

    public int Compare(object x, object y)
    {      
      Type propType = sortProperty.PropertyType;
      int sort = 0;
      if (propType == typeof(string))
        sort = string.Compare((string)sortProperty.GetValue(x), (string)sortProperty.GetValue(y));
      else if (propType == typeof(DateTime))
        sort = DateTime.Compare((DateTime)sortProperty.GetValue(x), (DateTime)sortProperty.GetValue(y));
      else if (propType.IsEnum)
        sort = (int)sortProperty.GetValue(x) - (int)sortProperty.GetValue(y);
      else if (propType == typeof(int))
        sort = (int)sortProperty.GetValue(x) - (int)sortProperty.GetValue(y);
      else if (propType == typeof(Type))
        sort = string.Compare(sortProperty.GetValue(x).ToString(), sortProperty.GetValue(y).ToString());
      else if (propType == typeof(bool))
        sort = string.Compare(sortProperty.GetValue(x).ToString(), sortProperty.GetValue(y).ToString());
      else
        throw new NotSupportedException();

      if (sortDirection == ListSortDirection.Descending)
        sort = -sort;

      return sort;
    }

    #endregion
  }

Then I had to implement the IBindingList interface. Actually most of the IBindingList interface is redundant (makes me wonder why it wasn't broken down into a few smaller interfaces, ISortList, ISearchList etc), so here are the bits that are different to the stubs provided by Visual Studio.

    void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
    {
      sortProperty = property;
      sortDirection = direction;
      objectList.Sort(new Comparer(sortProperty, sortDirection));
    }

    private PropertyDescriptor sortProperty;
    PropertyDescriptor IBindingList.SortProperty
    {
      get
      {
        return sortProperty;
      }
    }

    bool IBindingList.SupportsSorting
    {
      get
      {
        return true;
      }
    }

    bool IBindingList.IsSorted
    {
      get
      {
        return (sortProperty != null);
      }
    }

    void IBindingList.RemoveSort()
    {
      sortProperty = null;
    }

    private ListSortDirection sortDirection;
    ListSortDirection IBindingList.SortDirection
    {
      get
      {
        return sortDirection;
      }
    }

objectList is my internal ArrayList that contains the collection of objects I'm interested in.

I'm not entirely happy with this solution. I have a lot of collection classes and each one has this same bit of code in it, which always smells a bit bad to me. At some point I might try to create a base class that encapsulates this functionality but it doesn't look too straightforward, since IBindingList requires IList, ICollection and IEnumerable implementations as well... 

Tuesday, February 20, 2007

Metastorm Resources

As well as getting hits from people searching for remedies to their medical problems with their bell ends (hint: go and see a doctor), I also get a few hits from people trying to track down information about Metastorm e-Work/BPM. So here's a short list of potentially useful sites.

Process Mapping - OK, it's my employer so I'm biased but it has some useful stuff. If you develop with e-Work be sure to grab a copy of the Procedure Documenter Designer add-in, which not only automatically documents your procedure but also checks for problems in your procedure that are missed by the Designer's validation.

Process Mapping Forums - OK, it's my employer again but if you're looking for an online community to discuss e-Work issues, this is definitely the best place to go. The official Metastorm newsgroups are like a ghost town, the once great eworkdev.org is now a site about Chinese babies and the only other one I'm aware of, eworkhelp.org, seems to be dying due to lack of people and increasing spam.

FreeFlow - Hmm, alright, this is mine... But if you want to integrate .NET and e-Work, these class libraries may well help. There's also a replacement for the abysmal Metastorm admin tools, which I use everyday (can't remember when I actually used the official tools) 

BRD - Thought I better add something that hasn't got anything to do with me just to make me look a little bit impartial. BRD produce the Swift client, an alternative web-based client, that provides quite a few features not available in the standard Metastorm client.

If you have any other links, let me know and I'll update this list. 

Thursday, February 15, 2007

DataGrid copy to clipboard

Here's some code I knocked together to copy the contents of a WinForms DataGrid to the clipboard. Probably won't work for all possible data sources but it was good enough for my needs. 

    public void CopyToClipboard()
    {
      StringBuilder builder = new StringBuilder();
      for (int i = 0; i < ts.GridColumnStyles.Count; i++)
      {
        if (i > 0)
          builder.Append("\t");
        builder.Append(ts.GridColumnStyles[i].HeaderText);
      }
      builder.Append(Environment.NewLine);

      CurrencyManager manager = (CurrencyManager)BindingContext[DataSource];

      if (DataSource is DataTable)
      {
        DataTable dt = (DataTable)DataSource;
        foreach(DataRowView r in manager.List)
        {
          for (int i = 0; i < dt.Columns.Count; i++)
          {
            if (i > 0)
              builder.Append("\t");
            builder.Append(r[i].ToString());
          }
          builder.Append(Environment.NewLine);
        }
      }
      else if (DataSource is IList)
      {
        for (int i = 0; i < manager.List.Count; i++)
        {
          object item = manager.List[i];
          Type objectType = item.GetType();
          for (int j = 0; j < ts.GridColumnStyles.Count; j++)
          {
            string colName = ts.GridColumnStyles[j].MappingName;
            PropertyInfo propInfo = objectType.GetProperty(colName);
            object propertyValue = propInfo.GetValue(item, null);
            if (j > 0)
              builder.Append("\t");
            builder.Append(propertyValue.ToString());
          }
          builder.Append(Environment.NewLine);
        }
      }
      else
        throw new NotSupportedException();

      Clipboard.SetDataObject(builder.ToString(), true);
    }

Saturday, February 10, 2007

Resizing columns to fit in a DataGrid

I wanted to automatically set the width of columns in a WinForms DataGrid to something sensible based on the contents (using .NET 1.1 for a number of reasons). I've seen the solution to this in a couple of places but they didn't work for me because I'm using a grid that inherits from DataGrid and adds a few helper methods. So here's what I've come up with.

    public void AutosizeColumns()
    {
      Type t = GetType();
      t = t.BaseType;
      MethodInfo m = t.GetMethod("ColAutoResize", BindingFlags.Instance  BindingFlags.NonPublic);

      for (int i = 0; (i < TableStyles[0].GridColumnStyles.Count); i++)
      {
        m.Invoke(this, new object[]{i});
      }
    }

Sunday, February 04, 2007

The CSS media attribute

I was going to write about this some time ago and never got round to it but Jeff Atwood covers it much better than I could ever hope to. Another reason I thought I'd highlight this is after a conversation with one of our clients who'd paid a company to write an ActiveX control to produce printer friendly versions of web pages. There really wasn't any need, it's trivially simple to produce a stylesheet for printer-friendly output, generally all you need to do is hide some of your headers and menus etc. Even with my pathetic CSS skills I managed to create one for the Random Pub Finder.

On the other hand I'm not so sure about the use of a handheld stylesheet though. I did one of these for the Random Pub Finder as well, but as far as I can see we never get any hits from handheld devices. Also, I may be wrong but I think quite a few handheld devices actually ignore the handheld stylesheet and just render websites using the standard stylesheet. This is probably due to the lack of websites that actually provide a handheld stylesheet, meaning any handheld device needs to be able to cope with full-size pages anyway. And finally, it's a right pain testing your site against the huge number of devices out there, particularly as the emulators always seem to be a pain to set up.

Friday, February 02, 2007

Installing a .NET assembly with COM interop

I've written a .NET 1.1 assembly that needs to register for COM interop. I've been using the Visual Studio 2003 installer to build my installer, because it's generally good enough for what I'm doing and it's free. OK, it's not exactly free, but who buys Visual Studio for the installer bits?

Anyway, the installer had been working fine by setting the Register property of the assembly to vsdrpCOM but when I went to test the installer I'd built today after some changes to the assembly it wasn't getting registered. Oddly the install completed and didn't suggest there was a problem but the registry was empty. I could register the assembly using regasm, but that doesn't seem like such a good solution for customers!

So after a not so quick Google later I came across this newsgroup posting (near the bottom) which showed a solution to the problem. And it worked. Problem solved. What I particularly like about this solution is that I'm now back in control of registering my assemblies, rather than being in the hands of the installer's black box implementation, which I've had weird problems with before.

Thursday, February 01, 2007

So what is a bell end?

I noticed that a search for what's a bell end on Google brings up this site at number one, so I thought I better answer the question.

So here goes, bell end refers to the end of a man's, er, manhood. The name came about because it's shaped somewhat like a bell (go on have a quick check, nobody's looking) and it is, in fact, the end. It's a simple as that, although it's often used as something of a derogatory term, where someone might say "he's such a bell end".   

The obvious follow on question is why on earth I chose it as my blog name? And the answer to that is I'm not entirely sure. I've certainly been called it quite a few times (due to the surname) and couldn't think of anything more appropriate at the time.

BlogSpot not very well

Lots of people getting a bX-vjhbsj error, as am I. So you probably can't see this...