Monday, December 31, 2007

Vista usage

I read this post on Mike Taulty's blog, which seemed to show widespread adoption of Vista

http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2007/12/06/10012.aspx

which led me to this post from Craig Murphy, again showing widespread adoption of Vista

http://www.craigmurphy.com/blog/?cat=27

which led me to this slightly more cynical view from Thomas Lee

http://tfl09.blogspot.com/2007/10/vista-adoption-numbers.html

which led to the original post from Alex Eckelberry

http://hackersblog.itproportal.com/?p=768

which again shows a less than rosy picture for Vista adoption. My guess is the first two are getting much higher numbers because they are MS-based development blogs, which are read by MS geeks, who are the people most likely to be using Vista. From my small sample space (from the Random Pub Finder) in December, of the 89% visitors who are using Windows, 7.6% are using Vista, meaning it's just overtaken Windows 2000. Personally I think this is a much more realistic figure. Most non-techy people I know don't know nor care what Vista is and continue to use XP quite happily. And I think they may be quite sensible, what are the compelling reasons to upgrade? I'm yet to find them. Under the hood there may be lots of changes, but on the surface there's not a lot to see.

os

Thursday, December 20, 2007

Calling a COM object on a remote machine with no type library

I'd always assumed to connect to a remote COM object required the client code to reference the type library for the COM object and for the type library to be registered on the client machine as well, but somebody showed me a way of doing this today that appears to avoid the need for either of these.

      Type type = Type.GetTypeFromCLSID(new Guid("{12345678-1234-1234-1234-123456789012}"), server, true);
      
      Object obj = Activator.CreateInstance(type);
      
      return (string)obj.GetType().InvokeMember("Method", 
        BindingFlags.InvokeMethod, null, objTest, new object[1]{param});

Admittedly it uses reflection, which will probably be slower than a direct call to the interface, but given it's a call over the wire, this probably won't be the slowest part of the call anyway. And it sure makes deployment easier.

Wednesday, December 12, 2007

Solving the Vista "Failed to play test tone" sound problem

It's almost a year since I installed Vista on my work machine. I sorted out most of the glitches a long time ago but I've not had any sound the whole time, which I kind of learned to live with. I'd pretty much given up trying to fix it, thinking I'd wait for SP1, which I was sure would sort out the problem.

So I saw SP1 RC1 was available and installed it and excitedly tried my sound. Still nothing... OK I thought, now is the time to figure out what the problem is. I tried out some of the SysInternals tools to see if they could provide some kind of useful info, basically to see if the audio subsystem was trying to access a registry key or file that didn't exist or it didn't have access to (I'd already decided it probably wasn't a driver problem since a completely different sound card had exactly the same problem).

I got nowhere with that so I moved onto my next idea, trying to figure out what was going on in the audio log files. If you change the registry key HKLM\System\CurrentControlSet\Control\WMI\AutoLogger\Audio\Start value from 0 to 1, log files get generated in %WINDIR%\system32\LogFiles\Audio (called something like AudioSrv.Evm.001). But I have no idea how to view these log files, they are in some kind of proprietary binary format. So I'd thought I'd harass Larry Osterman. I don't know the guy, but he's the only member of the Windows Audio team who I'm aware of that blogs. But before I had a chance to make a fool of myself I saw a link on one of his comments by someone who'd solved their own Vista sound problem for themselves.

There were two nuggets in there. First up was the suggestion to debug the issue using the WinAudio sample from the Vista SDK. That way, you find out exactly what error message the audio APIs are returning. The second nugget was the solution to his particular problem, which was to delete the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole\DefaultAccessPermission. I didn't think it would fix my machine, since I'd followed quite a few other suggestions without success, but I tried it anyway. It didn't work. Or so I thought, until I rebooted and my speakers miraculously started making noise.

So I'm a happy camper, but there's a few things I don't understand. Why is the logging file format not documented? Perhaps it is, but it should be more widely publicised if it is. For that matter, why's it not just a text file? And why doesn't the audio stack just write to the event log like most other Windows components when something goes wrong? If I'd seen something in there saying access was denied to some COM component I'd have a clue what the problem was. 

And see this below? That's what I'm listening to. Yes, music!

Currently listening to Something , Blue Jay Way (Tran by The Beatles from the album Love

Addendum for non-technical readers

  • From the Windows Start menu, choose 'Run...'.
  • Type regedit in the 'Open' text box.
  • Navigate to 'Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole
  • Delete the registry key 'DefaultAccessPermission'. If it isn't there, chances are this isn't the problem causing your lack of sound.
  • Beware that fiddling with your registry can cause system problems if you modify important settings.

Sunday, December 02, 2007

The last CRT

Some time ago our TV died, trying to switch it on led to a light coming on for a few seconds, then it turned itself off. My guess was the power supply had died or the screen itself had died (for a few days the picture displayed hadn't been the right size).I did think for a second about getting it fixed but then thought it would be as cheap to buy a new TV. After all, the price of TVs has been dropping for a while. And my brother would no doubt be able to fix it, so the TV wouldn't go to waste. But then I actually had a look for a TV and was unable to find any CRT TVs anywhere. I did find some LCD TVs that were pretty cheap but there were a few problems.

  • Most of them were too big to fit into our living room without removing shelving. Not everybody wants a 32"+ screen.
  • Call me a Luddite but the picture quality of LCD screens doesn't seem that good. OK, when high definition TV becomes ubiquitous, the picture quality may be pretty good, but for a 28" screen I don't really need high definition TV.
  • Most LCD screens seem very fragile. They look like they can get knocked over very easily. Having a five year old wandering around with an LCD screen in the room is asking for trouble.

I know I'm not really getting into the new technology here but I think CRT TVs are great. The picture quality, even on very cheap models, is pretty damn good. The technology is proven. The prices are now great, if you can actually find one. Finally I discovered probably the last CRT TV available, from Argos.

Some of you may be thinking "it's analogue, you won't be able to receive anything on it in a couple of years", which is a fair point*. But we have a DVR with FreeView so that's not an issue for us. Also, there is a digital version available for a few quid more. Funnily enough, delivery on this TV took a very long time. Apparently the demand for it is pretty high, so perhaps I'm not the only person who thinks CRT TVs are actually pretty good.

* Digital TV itself seems something of a con. OK, there are more (mostly crap) channels available, although chances are you'll need a better aerial to receive them but why turn off the analogue signal?  Obvious really, it forces everybody to upgrade their kit so equipment manufacturers get some additional income and the government can sell of the old analogue bandwidth to get some extra revenue. Everybody's a winner, except the public. Expect a switch to HD only channels in a few years time.

Wednesday, November 28, 2007

What can I do with an LCD screen and a COM port?

There is something particularly gratifying about writing software that drives other bits of hardware. I guess it's the feeling of controlling something in the real world, rather than just making square boxes do things in the virtual world of PCs. .NET 2 made controlling COM ports easy through the SerialPort class, and there is a library available for controlling the USB ports. Then I saw this article about driving LCD screens from .NET, again via the serial port.

So now it's all very easy to control hardware from my preferred development environment, I just have to find a useful application to develop.

One possibility I've been mulling over for a while is some software to monitor electricity usage. Apparently there are electricity meters out there that will do this for you, showing pretty graphs etc, but the electricity companies aren't keen on installing them. Presumably this is due to the cost and also perhaps because it might encourage people to use less power, which isn't really in their interests. You can buy a clamp meter to non-invasively measure power usage but the ones that actually come with any kind of data interface are damn expensive so until I can find a cheap one, this particular idea is on the back burner.

Saturday, November 24, 2007

Boethius's Wheel

"It's my belief that history is a wheel. 'Inconstancy is my very essence,' says the wheel. Rise up on my spokes if you like but don't complain when you're cast back down into the depths. Good times pass away, but then so do the bad. Mutability is our tragedy, but it's also our hope. The worst of times, like the best, are always passing away."

So Boethius wrote about 1500 years ago*. And as far as I can see, it still holds true today, on a personal level and on a national/global level. Gordon Brown may have said "no more boom and bust" but the fact is he always had very little control over the economic cycle. It's human nature to over extend ourselves during the good times, thus driving the boom to unsustainable levels then doing the opposite during the downturn. And it looks to me like the 10 years of boom is just about to come to an end. Interesting times.

*I'm not particularly well read, I picked up on this quote when a tramp shouted it at Tony Wilson in "24 Hour Party People". It's then alluded to when Wilson is forced to present "Wheel of Fortune" in order to make ends meet.

Buy Nothing Day

Today was Buy Nothing Day, which I think is a top idea. I've been aware of this for a few years, but today I only heard about it after I'd popped out to buy some batteries. They are rechargeable so I don't feel too bad about it.

Friday, November 23, 2007

What is click fraud?

There are plenty of examples of obvious click fraud, people setting up dubious websites with advertising then using nefarious means to get clicks on those ads, companies clicking on their competitors ads to drive up their competitors' costs. And these kind of examples of fraud are quite possibly detectable by the companies who run the ads. But there seems to be a whole other bunch of other stuff that may also be considered fraudulent, or at the very least not valid clicks.

For instance, my daughter can now use Google to search for stuff on the internet but she has no idea of what the difference is between a sponsored ad and a normal search result and since she can't read everything, she just clicks on whatever she feels like. And, until she works out how to use my credit card, she won't be buying any Charlie and Lola gear from one of the sites she visits.

Or what about me when I see an ad for some dubious debt company or some get rich quick scheme and I click on the link purely to cost them some money?

Or what about those websites that try to disguise their ads as just another link on their site?

These may or may not be fraudulent, but I'm fairly certain Google and the other ad companies can't tell they aren't real punters who are actually interested in the website's offerings. Presumably companies will continue to pay up until they aren't getting a return on their outlay, which is definitely one of the advantages of online ads. And that being the case, it seems like one great way to stop these dodgy companies from advertising is actually to just click on their ads...

Tuesday, November 20, 2007

Posting errors to a website

The exception logger I've written is one of the most popular pages on my proper website but I've never been completely happy with it. I've always wanted to be able to get the details of an unhandled exception from the user of my software to me easily. I added support for sending an email some time ago, but that's never felt like a good solution since the user must configure their SMTP connection before sending off the email. Then I read a website posting suggesting a much better solution, post the error to a website, then email the error to the relevant address from the web server. That way, email only needs to be configured in one place, on the web server. Admittedly if the user isn't hooked up to the internet it won't work, but sending an email via SMTP will also generally fail in that scenario.

The exception logger code shows how to do the client-side code, but here it is anyway.

    private void LogToWebsite(string error)
    {
      Uri uri = new Uri("http://www.yourwebsite.com/SubmitBug.aspx");
      HttpWebRequest httpWebRequest    = (HttpWebRequest) WebRequest.Create(uri);
      httpWebRequest.Method    = "POST";
      httpWebRequest.ContentType = "application/x-www-form-urlencoded";

      Encoding encoding = Encoding.Default;

      string parameters = "msg=" HttpUtility.UrlEncode(error);

      // get length of request (may well be a better way to do this)
      MemoryStream memStream = new MemoryStream();
      StreamWriter streamWriter = new StreamWriter(memStream, encoding);
      streamWriter.Write(parameters);
      streamWriter.Flush();
      httpWebRequest.ContentLength = memStream.Length;
      streamWriter.Close();

      Stream stream = httpWebRequest.GetRequestStream();
      streamWriter = new StreamWriter(stream, encoding);
      streamWriter.Write(parameters);
      streamWriter.Close();

      using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse())
      using (StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream()))
      {
        streamReader.ReadToEnd();
      }
    }

A couple of points to make here. I'm using POST rather than GET, because GETs have a maximum length of data that can be passed (8K if I remember correctly). The error is URL encoded, so that ampersands don't truncate the posted error message. The code to work out the length of the content may not be required, you may be able to get away without passing it at all and there may be a better way of calculating it. I did it that way when I was fiddling around with the encoding type, since different encoding types would naturally produce different lengths of content. Finally reading back the response data isn't really necessary since we don't do anything with it.

Now onto the server-side code, which is pretty simple.

  protected void Page_Load(object sender, EventArgs e)
  {
    if (!string.IsNullOrEmpty(Request.Params["msg"]))
    {
      SmtpMail.SmtpServer = "localhost";
      SmtpMail.Send("mail@yourwebsite.com", "error@yourwebsite.com", 
        "Bug report", Request.Params["msg"]);
    }
  }

The only thing to note here is that because we are using POST, the data is passed in via the Params property, rather than the QueryString property.

Monday, November 19, 2007

Developing custom state activities

I've banged on before about how extensible Windows Workflow is (assuming you can work out how to extend it), but there is at least one place where it isn't quite as extensible as one would like, state activities.

I was trying to create a custom state activity that contained a state initialization activity and an event driven activity and various other activities within those. The first problem I encountered was when I inherited from StateActivity. Trying to add any activities at design-time failed. This seems to be down to the state activity designer. One solution would have been to write my own designer class, but this is complicated by the fact that the standard state designer is internal so can't be inherited from. I guess I could have decompiled the code using Reflector and made the required mods in my own code, but that seemed too much like hard work. So I decided to construct the activity in code instead.

Then I bumped into the second problem. Adding activities in code is pretty straightforward, just do it in the constructor after the call to InitializeComponent, like so.

public UserActivity()
{
  InitializeComponent();
  CanModifyActivities = true;
  try
  {
    // add activities here
  }
  finally
  {
    CanModifyActivities = false;
  }
}

Having said that, if you're doing all your activity construction in code, you can actually get rid of the call to InitializeComponent and get rid of the designer.cs file, since you're not going to need any design-time support.

Anyway that's the theory. In practice, it doesn't quite work out like this, because the StateActivity's validator doesn't like you to add child activities to a state activity. At least this class is public so I could inherit from it, but there aren't really any extensibility points to allow for modification of its behaviour. So I initially wrote my own validator class, that doesn't do anything at all. I later realised that if the validator wasn't going to do anything, I could actually just use ActivityValidator instead. This isn't perfect since it means none of the child activities I've added will be validated, but it works well enough for my scenario.

One further problem (although it isn't an issue for me) is that an end user of your custom state activity won't be able to add their own child activities. I guess the solution to this once again is to write your own designer.

Another related problem is the addition of a set state activity. Since my users can't add one themselves, I have to add one but I don't know the name of the state to transition to until the activity is in use. This was pretty easy to solve, just add a TargetStateName to my activity and pass that value through to my own set state activity.

public string TargetStateName
{
  get
  {
    return setState.TargetStateName;
  }
  set
  {
    setState.TargetStateName = value;
  }
}

My final problem was that the state activity was displayed with all of its child activities visible to the end user, although they are not editable. I still haven't solved this issue, although I suspect the solution will yet again involve writing my own designer class. If only that standard state designer wasn't internal...

Thursday, November 15, 2007

Sharepoint create task workflow error - Not supported exception

If you've tried to create tasks in workflows hosted in Sharepoint using the CreateTask activity you may have come across this error in the Sharepoint logs.

System.NotSupportedException: Specified method is not supported.     at Microsoft.SharePoint.Workflow.SPWorkflowTask.SetWorkflowData(SPListItem task, Hashtable newValues, Boolean ignoreReadOnly)     at Microsoft.SharePoint.Workflow.SPWinOETaskService.UpdateTaskInternal(Guid taskId, SPWorkflowTaskProperties properties, Boolean fSetWorkflowFinalize, Boolean fCreating, HybridDictionary specialPermissions)     at Microsoft.SharePoint.Workflow.SPWinOETaskService.CreateTaskWithContentTypeInternal(Guid taskId, SPWorkflowTaskProperties properties, Boolean useDefaultContentType, SPContentTypeId ctid, HybridDictionary specialPermissions)     at Microsoft.SharePoint.Workflow.SPWinOETaskService.... 

Although saying that, my own searches on the Internet didn't bring up anything, so I may be the only one. Or there may not be many people using workflow in Sharepoint, which is odd given that it's free, which compares very favourably with most workflow products.

But I digress. I thought this would be pretty easy to solve. First up, have a look at the implementation of SPWorkflowTask.SetWorkflowData in Reflector and see what causes it to throw the exception. Which is where things got difficult. Reflector told me the method was obfuscated. I don't know if this is true or if Reflector was getting confused but I left it for a while since I had other things to do. Then I had a thought, perhaps I could still have a look at the code in Reflector if I chose IL as my language of choice. And indeed I could. And this is what the area of interest looked like.

    L_01e8: br.s L_01f0
    L_01ea: newobj instance void [mscorlib]System.NotSupportedException::.ctor()
    L_01ef: throw
    L_01f0: ldloca.s id
    L_01f2: ldsfld valuetype Microsoft.SharePoint.SPContentTypeId       Microsoft.SharePoint.SPBuiltInContentTypeId::WorkflowTask
    L_01f7: call instance bool Microsoft.SharePoint.SPContentTypeId::IsChildOf(valuetype Microsoft.SharePoint.SPContentTypeId)
    L_01fc: brtrue.s L_0200
    L_01fe: br.s L_01ea
    L_0200: ldarg.0

At lines L_01f0 to L_01f7, the method checks to see if the content type is a child of workflow task. If not, it throws the exception. Turns out my workflow.xml file had its TaskListContentTypeId attribute set to 0x0108 (task), rather than 0x010801 (workflow task). I changed that and everything worked, the task was created and I was on my way to workflow nirvana.

It's just a shame that whoever implemented this bit of code didn't use the NotSupportedException that takes an error message as a parameter, then they could have told me what the problem was...

Sunday, November 11, 2007

Pies and Prejudice

One of the sure signs of getting older is listening to Radio 2, not in some kind of ironic post-modern way but because, of all the options available, it seems like the best one. And if you listen to Radio 2 then it's pretty much required to like Stuart Maconie, since it seems like half their output involves him. Fortunately I fall into that category. The man's funny and it's always good to hear a Northern accent on the radio (even if it has been softened, presumably from too many years living in the South).

So when I saw his book 'Pies and Prejudice - In Search of the North' I was interested. When I read the back cover 'A northerner in exile, stateless and confused' I thought yep that's me and promptly bought it (OK I put it on my Amazon wish list and somebody bought it for me several months later).

And it's a great read. He writes like he talks, so if you like his shows you'll like the book. Any Northerner reading it will no doubt be disappointed by his lack of coverage of <insert name of place that is important to you>. I was surprised by the lack of mention of Lancaster, one of the more attractive towns in Lancashire and the Trough of Bowland, Lancashire's best kept secret. I wasn't so surprised by him avoiding Blackburn and Darwen, which even the most rabid Lancastrian would have to admit are dumps. And even when he does cover areas in some depth, anybody with some inside knowledge will probably think he's missed out some important parts.

But covering every little nook and cranny isn't really the point of the book. Maconie is attempting to redress the balance of the coverage of the North. It's not all terraced houses, grim weather, even grimmer food, unemployment and fighting, which is what you may think based on the typical drama based anywhere in the North. And he succeeds in doing that. Unfortunately he may have succeeded but I suspect the people who should read it, the people with those prejudices about the North, quite likely won't be the people buying it. And perhaps that's no bad thing. Do we really want the North to become yet another weekend escape from London, with property prices driven up by bankers who only ever visit every month? Perhaps it's best for the prejudices to remain intact, the people who matter know it's not true. Lets keep it our little secret.

Tuesday, October 30, 2007

Using Sharepoint activities in a workflow designer

If you're hosting the Windows Workflow designer control in your own application (this shows you how to host the control) and you want to use the Sharepoint activities, these are the files you'll need (these are for Sharepoint Services, I think there are more activities for MOSS 2007).

Microsoft.SharePoint.dll

Microsoft.SharePoint.Security.dll

microsoft.sharepoint.WorkflowActions.dll

microsoft.sharepoint.WorkflowActions.intl.dll

microsoft.SharePoint.workflowactions.intl.resources.dll

I'm not sure of the rules regarding redistribution of these files since I'm only using it for my own personal use. If you're having trouble getting any of these files out of the GAC then read this post which explains how to get them out.

Monday, October 29, 2007

MSDN forums not very useful

How hard can it be to write some code for a web forum? After all it's a problem that has been solved thousands of times before. Well apparently it's too hard for Microsoft. I've been trying to post to their MSDN forums for the last week and whenever I try to create a new thread or reply to a thread I get the following error message.forums

 

 

 

I've tried clearing out all the crud from IE, with no joy. So I'm forced to kick off a Virtual PC session to post, not very handy.

Not only that, when I get an email alert telling me about a reply to my post, the link starts with 'h ttp://forums.micro' so it doesn't fecking work.

A no-op activity

I'm in the process (no pun intended) of converting one business process XML language to XOML via some XSLT. I'm not really sure how some of the source activities will map to Windows Workflow activities so I'd like a no-op activity for the time being, just so I get the correct layout of the process, without having to nail down all the details. The solution is actually very simple, just use the base Activity class whose Execute method does nothing, simply returning ActivityExecutionStatus.Closed. Although it's not available in the Visual Studio toolbox, Visual Studio copes quite happily with a XOML file that contains an Activity activity.

Sunday, October 28, 2007

Web applications aren't the solution to every problem

I've been working on an ASP.NET website for a while now. For the admin side of things, I've added a few pages for updating the database and such like. There have always been problems with updating the data, which is read in from some text files and then dumped in the database. First there were problems with the size of the data in the files, which was solved initially via the maxRequestLength attribute in web.config. Then the file upload to the web server was still taking a long time so I started to require the data to be uploaded via Remote Desktop or FTP. Then I had problems with the SQL update timing out. This was solved by increasing the value of the executionTimeout attribute in web.config. But after a while even this didn't work and the update failed when the request timed out. I could probably increase the timeout some more or I could use some other technique to get it working, but I eventually came to the conclusion that this just isn't what I should be doing. ASP.NET sets these values quite low for a reason, it's just trying to protect itself from poorly written applications. A request to a web server should take as short a time as possible before getting the hell out of there. That's what the web sever is designed to handle.

So I ripped out the code and dumped it in a WinForms application. WinForms has no problems with threads that hang around for a long time so that solved my initial problem. And, even better, I can give feedback to the user very easily, without fiddling round with AJAX. It's also more secure. Remote desktop connections to this server are only accepted from certain IP addresses, whereas anyone might find the admin pages (even though they are password protected).

So to the moral of the story, remember that not every problem is a nail and there are other tools in your toolbox other than the hammer.

Thursday, October 25, 2007

The problem with LINQ

In principle it sounds like a great idea. Rather than having to learn SQL, XPath and whatever other query language you care to mention, just learn LINQ and use it to access any data source you like. That's great and all, if I'm using .NET 3.5 for every single project I work on. And until I am (which I'm guessing will be, er, never), LINQ is actually yet another thing to learn. So now I need to know SQL, XPath and LINQ. Or perhaps not, perhaps best to just forget LINQ and stick to the old stuff, since it works and is available on most platforms. When LINQ is available everywhere, then I'll start to think about learning it. 

Tuesday, October 23, 2007

MySQL error messages = crap

OK, it's an old version of MySQL (4.0.25 to be precise) but the error messages are completely useless. Almost any kind of SQL syntax error shows this helpful message.

#1064 - You have an error in your SQL syntax.  Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT PubID FROM Pubs )  LIMIT 0, 30' at line 1

Of course the problem is that MySQL 4 doesn't support subqueries (!), apparently MySQL 5 does and perhaps my ISP will upgrade one day...

Monday, October 22, 2007

SonicWall VPN client for Vista released

My post about the SonicWall Vista client beta is one of the most popular things on here, lord knows why. Anyway, the final version has been released and it is working perfectly for me. I've not had any problems with Vista complaining about two computers with the same IP address being connected to the network. I realise I'm really tempting fate writing that...

Friday, October 19, 2007

Licensing

Somebody emailed me asking what the licensing terms were for some code he'd found. I was surprised somebody would bother asking since I tend to assume any code I find on the web is public domain (although I wouldn't dream of stealing somebody's prose - odd how I think of code differently to the written word). But of course this isn't necessarily the case. In fact I believe if no licensing terms are explicitly stated, the code is by default copyright of the author.

So to clarify the licensing for anything on this site, any code samples are public domain. Do what the hell you want with them. I do however claim the copyright for the English on here, not because I think it's particularly valuable but because I don't want spammers stealing it for their own sites. Not that a little copyright theft is likely to stop them of course, but I don't want to make it perfectly legal for them either.

Whether any of this is legally binding, who knows, but I'm definitely not hiring a lawyer to tell me either way.

Improved Excel-like select all in a DataGrid

My first attempt at this worked mostly but if the user clicked anywhere in the grid that wasn't a cell, the whole grid would be selected, which wasn't what I was after. So here's an improved version which will only select all when the user clicks in the top left cell.

    public void SelectAll()
    {
      BindingManagerBase bm = BindingContext[DataSource];
      for (int i = 0; i < bm.Count; i++)
      {
        Select(i);
      }
    }

    protected override void OnClick (EventArgs e)
    {
      base.OnClick(e);
      Point client = PointToClient(Cursor.Position);
      HitTestInfo hitInfo = HitTest(client);
      if ((hitInfo.Column == -1) && (hitInfo.Row == -1))
      {
        if (client.X < TableStyles[0].GridColumnStyles[0].Width)
          if (client.Y < TableStyles[0].PreferredRowHeight)
            SelectAll();
      }
    }

Friday, October 12, 2007

Radiohead's pay as much you like strategy

I had every intention of buying the new Radiohead album from their website. After all, they've done exactly what I've asked for. It's DRM-free and the price is reasonable (since it's whatever I consider to be reasonable*). But I had some problems. First the site constantly timed out when I tried to connect yesterday (which I guess is a good sign since it suggests they are getting a good number of hits). When I did manage to connect and I registered, I was asked for my mobile number, which was a required field. Er, I don't have a mobile. And if I did, why do I need to give the number to you?

Unsurprisingly, the album is pretty easy to find from illegitimate sources and there are no hoops to jump through to get it from those sources. So if I wasn't fussed about a bit of copyright infringement, that's what I'd probably do.

It's clear the music industry and Radiohead are still missing the point about downloading music. Price isn't the key issue, although it's important (iTunes' pricing is completely screwed, I might as well buy the CD). Downloading has to be as easy, if not easier than the illegal alternatives.

*Since I've been disappointed with every Radiohead album released in the last ten years but have kept buying them in the hope they would repeat the brilliance of 'OK Computer', a reasonable price for me would probably not be very high.

Wednesday, October 10, 2007

Words that annoy me

Spelunking - This word comes from the caving community, the weird guys who like to go down small wet holes at the weekend (fnar, fnar). They may be weird, but their sport is kind of dangerous and possibly exciting. Now the geek community have appropriated the word to describe, er, groping around in new technology or source code. It seems to be a desperate attempt to make IT sound much more interesting than it really is. Look, computing is not cool, get over it.

Grok - What is wrong with 'understand'? It's been in the English language for quite some time and everybody (including normal, non-geek people) knows what it means. Also see above.

Regards - Email and the web have always seemed pretty informal to me. There was a time in about 1990 when I ended emails with some kind of sign off, but now I find my name is plenty to mark the end of a message. So when I see 'regards' (or worse 'kind regards'), I think that the person sending that message has some kind of problem. In fact, I wonder why they are being so formal and wonder if they are implicitly telling me to feck off.

Consumer - Is that all we are good for? Buying more tat to help fuel the economy? I am not a consumer, I am a person. When I think of consumers, I think of Fight Club - "working jobs we hate so we can buy shit we don't need"

Tuesday, October 02, 2007

Excel-like select all in a DataGrid

Clicking on the top-left cell in Excel selects the whole worksheet, which always seemed like a good use of an otherwise useless cell. So I've done the same with the DataGird-derived control I've been using in one of my apps. It's pretty damn simple to implement, but for the lazy amongst you, here's some code.

    public void SelectAll()
    {
      BindingManagerBase bm = BindingContext[DataSource];
      for (int i = 0; i < bm.Count; i++)
      {
        Select(i);
      }
    }

    protected override void OnClick (EventArgs e)
    {
      base.OnClick(e);
      HitTestInfo hitInfo = HitTest(PointToClient(Cursor.Position));
      if ((hitInfo.Column == -1) && (hitInfo.Row == -1))
      {
        SelectAll();
      }
    }

Tuesday, September 25, 2007

A simple 'Check for updates'

My brother asks when I'll post something interesting on here, but it hasn't happened yet so I doubt it will anytime soon... Anyway it seems the most boring posts are the most popular so I'll keep on posting crap.

Back to the point of this post. Lots of applications these days will check for an update when they start or when you press a 'Check for updates' button or menu item. Some really annoying applications have some little helper process that runs continuously and checks on a regular basis then throws up a big dialog telling you that there's an update (that's you Apple).

Not wanting to be left out, I thought I'd do the same. But being incredibly lazy, I couldn't be bothered to implement it completely. So this little bit of code will read a text file stored on the web server and tell the user that an update is available. It won't do anything fancy like download it for them or install it but I might add that at some point. Usage is simple, set the Url property to tell the component where the text file is stored, then call CheckLatestVersion() to show a message saying there is a new version available.

 

using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Windows.Forms;

namespace FreeFlowAdministrator
{
    /// <summary>
    /// Component to check for updates for an application.
    /// </summary>
    public class UpdateChecker : System.ComponentModel.Component
    {
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;

    public UpdateChecker(System.ComponentModel.IContainer container)
    {
      ///
      /// Required for Windows.Forms Class Composition Designer support
      ///
      container.Add(this);
      InitializeComponent();
    }

    public UpdateChecker()
    {
      ///
      /// Required for Windows.Forms Class Composition Designer support
      ///
      InitializeComponent();
    }

    /// <summary> 
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if(components != null)
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }


    #region Component Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      components = new System.ComponentModel.Container();
    }
    #endregion

    private string url;
    public string Url
    {
      get
      {
        return url;
      }
      set
      {
        url = value;
      }
    }

    public string GetLatestVersion()
    {
      HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url);

      // Sends the HttpWebRequest and waits for the response.
      HttpWebResponse myHttpWebResponse = myReq.GetResponse() as HttpWebResponse;
      try
      {
        Stream response = myHttpWebResponse.GetResponseStream();
        StreamReader readStream = new StreamReader(response, System.Text.Encoding.GetEncoding("utf-8"));
        return readStream.ReadToEnd();
      }
      finally
      {
        // Releases the resources of the response.
        myHttpWebResponse.Close();
      }
    }

    public void CheckLatestVersion()
    {
      string latestVersion = GetLatestVersion();
      string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
      if (latestVersion == currentVersion)
        MessageBox.Show("You are using the latest version");
      else
        MessageBox.Show(string.Format("The latest available version is {0}\nYou are using version {1}", latestVersion, currentVersion));
    }
  }
}

Thursday, September 20, 2007

Getting directions using Google Maps

I'm not sure when this feature was added but Google Maps now lets you drag your route around, if you want to go via a particular place. I'm not sure how useful this will be for me, but it's certainly cool to play with. Saying that I have asked for a web app to create Google Maps routes previously, and this is certainly heading in the right direction. It's possible to embed the map in a web page via an IFrame pretty easily, but I can't add markers and images, so not quite there yet. I was going to embed the route of my bike ride today but of course it doesn't understand about going off road or, erm, going through pedestrianised areas so I didn't have any luck with that.

Another great new feature is the information about bus departures, although weirdly I'm only seeing these in my local area, Kingston. It seems to be getting its information from Transport Direct, a website I've never seen before, but that looks like it goes some way to fulfilling my request for a site with complete knowledge of all the different transport options. It seems to be missing some pricing information but it knows about trains, buses and cars. Depressingly enough, cars are always cheaper than the more environmentally friendly option. Saying that, it claims a 200 mile journey by car is more environmentally friendly than the train, if the car has 4 occupants. But the train will be running anyway and using just the same amount of CO2, so what to do?

Some time ago I complained that Google Maps' directions to Heathrow from my house were completely ludicrous. Well it looks like they've fixed that as well, I'm sure they must have read my post...

Wednesday, September 19, 2007

Buy a people carrier

PeopleCarrier It got us to Spain and back in one piece with not a hiccup, but now it's time to pass our trusty vehicle on to someone else. If you're interested then check it out. All reasonable offers will be considered.

Monday, September 17, 2007

More reliable wireless

When I set up our wireless router, I initially set it up as an unsecured network and restricted access to certain MAC addresses. This worked fine but when we had visitors who wanted to use our wireless connection, it was a bit of a pain to allow access to their laptop. So I changed the router's configuration so it used WPA security and it all seemed to be working fine.

But then a couple of things happened which I didn't realise were related to this seemingly simple change I'd made. First, the wireless connection would stop working every few hours. This affected any computer connected to the network and would typically last a few minutes and then everything would be fine again. This was particularly annoying when I was connected to somebody's VPN and would have to re-establish a connection. The second problem was my Vista PC started to lock up completely. This was less frequent, about once a day.

I put the first problem down to the number of wireless networks in close proximity to our house. I guessed that they were just interfering with each other and causing the connection to drop every so often. Either that or our router was just getting on a bit. Then the other day I thought I'd give Vista's wireless diagnostics a spin and it told me the wireless security key was incorrect. What the...? This kicked my befuddled brain into action and realised these connection problems had started at the same time as the switch to WPA. So I switched back to an insecure network and lo and behold, the wireless connection is working a treat. Not only that but I haven't (fingers crossed) had a Vista freeze since the switch back.

So, if you're having wireless troubles, consider switching to an insecure network. I guess it may well be specific to my router but it may just work. Yeh, hackers can probably read my email, but frankly I hope they have more interesting things to do with their time.

Sunday, September 16, 2007

Are we in a fast moving industry?

It's virtually taken for granted that we work in a fast moving industry and the facts would appear to back up this theory. Just look at the new programming tools coming out of Microsoft (I'm just going to talk about them since I don't really even try and keep up with technology coming out of other companies). In no particular order, some of them are: WPF, WCF, WF, Silverlight, Linq, CardSpace, Virtual Earth, ASP.NET AJAX, Vista...

But hang on a second, who is using this stuff and how much of it will be relevant in five years time? Myself, the only new technology I'm looking at is Windows Workflow, since it may well be useful in my job. The rest may become relevant at some point and if and when it does I'll start looking at it then. And I'm a  geek who likes to fiddle around with new technology. What about the normal people out there who aren't obsessed with software?

Well, it doesn't look so good there either. Take IE7, which has been out for almost a year. Usage stats on the sites I maintain suggest just over 30% of IE users have upgraded to version 7. This is a free bit of software, but still almost 70% of users haven't upgraded. Admittedly some of these will be corporate users who have probably decided not to roll it out yet, but why not? Probably because IE6 is good enough, thank you very much.

And what about Vista? Well I'm seeing just short of 5% of Windows users are on Vista, which makes it somewhat more popular than Server 2003 (and who browses the internet from Server 2003 anyway?) but still less popular than Windows 2000 which, as the name suggests, is about 7 years old.

Here's something else to illustrate my point. I was up the loft the other day and found the notes for a C++ course I went on about 10 years ago. Flicking through them I realised I should probably keep hold of them, since they were still relevant today. Not only were they useful to C++ programming, but also to C#. The basic ideas behind object-oriented programming still apply. And you know what, if I still had the notes for my university C++ course from 17 years ago, they would probably still be relevant as well*

So yes, it's certainly possible to think we work in a fast-moving industry and it's certainly possible to work as if everything is in a constant state of flux. But it's also possible to move into a slower lane and just watch the shenanigans going on from a distance, wait for the dust to settle and learn the stuff that actually becomes important.

 

*You may be wondering why I needed to go on a C++ course when I was working if I'd already been taught it at university. Well I never paid much attention at uni, I was more interested in getting drunk and trying to pull young ladies...

Tuesday, September 11, 2007

Handling 404 errors in PHP

If you're running a PHP site on your own server, you can find out what 404 errors have occurred by viewing the log files. If, like me, you're running your PHP site on a cheap and cheerful host, you're probably not going to have access to the log files. But you can probably still do something to find out about them.

The first thing to do is ensure your users see a useful error message, rather than the default provided by your host, which will probably be some page advertising their services. This can be achieved via the .htaccess file, if your site is running on Apache. IIS has similar features, available through its admin tool (although I can't believe there are many people using PHP on IIS). Add something like the following to the .htaccess file to tell Apache to show your custom error page.

  ErrorDocument 404 /error.php

The next thing to do is to make sure you get notified if somebody ends up on a non-existent page on your site, so just add the following code to error.php somewhere.

  
  $message = "URL: " . $_SERVER["REQUEST_URI"] . "\n" . "Referrer: " . $_SERVER["HTTP_REFERER"] . 
    "\n" . "Browser: " . $_SERVER["HTTP_USER_AGENT"];
  mail("you@wherever.com", "404 error", $message);

And that's that. Well except the referrer doesn't seem to work for me... But it's already been useful, I've found one spurious bit of CSS trying to load an non-existent image, although I've also had quite a few false positives.

Friday, September 07, 2007

Yet more tick mark fun

I've written about this three times now. Read my previous posts here

But it turns out it's not as simple as the browser somebody is using. The other day I noticed that my tick marks weren't displaying on a computer that had IE7 installed. After more fiddling around I realised the fonts installed on that particular PC must be different to the ones installed on other PCs I'd tested and IE7 was unable to find the tick mark. I'm not sure whether IE7 only uses the current font or if it tries other fonts in an attempt to find the correct symbol but in any case it just displayed a square block.

Because of this I'm thinking perhaps the only 100% reliable solution is to either use an image or to display a checkbox in all situations, not just for IE6. In a desperate attempt to avoid either of those solutions, I've started using the 'Arial Unicode MS' font for my document. I guess this is fairly unlikely to work on Macs and Linux boxes, but they aren't particularly high on my list of required supported environments and they will fall back to more conventional fonts anyway, which hopefully work for these computers.

Update: Even the ‘Arial Unicode MS’ font won’t always work, even on Windows. This font is not installed by default on Windows systems, but it does come with Office, which I guess most Windows users will have installed.

Sunday, September 02, 2007

Road trip map

After some fiddling around with Google Maps I've managed to come up with a little map of our journey through France and Spain. It would be way cool if somebody came up with a web app that could be used by normal people to do this kind of thing...

Monday, August 27, 2007

Increment by increment

Popular music lyrics don't often use the word "increment" so I was pleased to hear British Sea Power use it on their first album. In fact they use quite a few unusual words and phrases. But "increment" has stuck in my head because it's such an important word to me.

Take programming for instance. Joel Spolsky calls development "The Game of Inches", but personally I prefer to think of it as a game of millimetres. It's probably partly due to my metric background but also inches suggests you might spot you've made progress after a day, whereas often I'll only recognise some progress has been made on a development project after weeks or months. Perhaps I'm just a slower programmer...

The fact is you can't make a lot of headway in a day. Which is probably why agile development has become popular. I'm not sure about parts of agile development but the idea of taking baby steps to get to where you want to be seems like a pretty damn good idea and I've done it myself for years.

Big designs have lots of problems. It's impossible to keep the details of a massive project in your head. Then things change part way through, or the design wasn't as well thought out as you initially thought. Then your boss tells you we need to release something next week... So it goes on.

The Random Pub Finder started as a craply designed site with hardly any content. Six years later, after hundreds of tiny steps, I'm now quite proud of it.

The FreeFlow Administrator started as a simple project to solve a particular problem I had. A year or so later and it's a fully fledged application that beats the Metastorm provided tools in pretty much every respect.

The commonality between these two projects is that both started small and have had working functionality from the start and at all the intermediate steps. Perhaps it's agile but I prefer to think of it as incremental. Agile brings to mind scrum meetings and pair programming. Perhaps they help but they aren't the important bit as far as I'm concerned.

Which makes me wonder why you'd want to develop stuff any other way. Most projects I've seen with huge design documents actually fall into an incremental development lifecycle as they progress anyway, so why not start out that way?

Thursday, August 23, 2007

What I learnt on the road trip

Take as few tents as possible - Campsites in Europe generally charge per person, per vehicle and per tent. Since you can't do much about the first two, the only way to save money is to bring a single tent that's big enough for everybody to fit into. The downside of this is the possibility that your big tent won't fit in the plot, but most sites seem to provide a pretty big space for you.

Take some kind of mattress - I've always slept directly on the ground in the past without problem, but the campsites in Europe can have much harder ground than the sites in blighty, with little or no grass, leading to some not very restful nights.

Spanish people stay up very late and have exceedingly loud TVs - I guess I already knew they stay up late, but hadn't considered the potential problems when staying in a camp site. Most of Spain takes the whole of August off and some of them head off to camp for a month, along with their tellies. They are then involved in an arms war in an attempt to drown out the sound of other campers' TVs, leading to yet more lack of sleep. In the site we were in, we also right next to the train line into Barcelona... Loudest... campsite... ever...

People still camp - I was under the impression that there were only a few foolish people who still bother to use tents when on holiday but we found quite a few camp sites that were full when we arrived.

You can camp for free in France - As well as the standard service stations on major French roads, there are plenty of aires that are just a bit of land with toilet facilities and running water. We didn't use them ourselves and they are probably only for hardcore campers who are happy to do without modern facilities like swimming pools, shops and showers, but it's good to know they are available if necessary.

Driving is more fun than flying - OK, a 3 hour flight may be quicker and cheaper than a 3 day drive but it doesn't give you any kind of idea of how far you travelled or let you visit any intervening places. 

GPS would be useful - A bog standard map is fine when travelling on the major roads but when you get into a city things can get a bit tricky. If you've got a detailed map of each city you'll visit you'll probably be OK, otherwise GPS will probably make your life easier. I can't verify this since we didn't go for the GPS option. 

The UK should sign up for the Euro - It's surely inevitable so why not go ahead and do it? Life would be so much easier, no more carrying around two currencies, no more trying to work out how much something really costs. So we may lose some sovereignty, so what? Get over it.

Wine in Spain is very cheap - 8 euros for 10 litres...

Wednesday, August 08, 2007

Road trip

It all started when my brother read Trek by Paul Stewart. The book describes the story of four people attempting to cross Africa (including the Sahara) in a Morris Minor during the 50s. Unfortunately it all ended in tragedy. Oddly enough, due to this story, we head out on Thursday on a road trip of our own. Not quite as dangerous as crossing the Sahara (although by all accounts, thanks to GPS, even that journey is much less risky than in the past), we're heading off to Spain to see my dad. The original plan was to go in a Rover P4, but the demands of teenagers mean we have to go in an air-conditioned people carrier.

From a personal perspective, I also see this as part of the slow travel movement. I haven't worked out the figures but I'm hoping this will be better for the environment than flying (although staying at home would be even better of course) and will be much more fun than being crammed on an EasyJet plane.

So move along, there won't be anything to see here for a while.

Wednesday, August 01, 2007

Visual Studio Express 2008

I'm a big fan of the Visual Studio Express 2005 range of products. They cost nothing and are pretty powerful. In some respects they're better than the full-blown version of Visual Studio. They are super quick to load and if you're using FTP to upload ASP.NET web sites, Web Developer is easier to use than Visual Studio. I've been using it for the FreeFlow web site for a while. So I took the plunge and downloaded the beta 2 of the 2008 version of the C# and Web Developer.

First impressions are good. The install was flawless. C# comes with a WPF designer, so I can finally get down to learning that without having to hand-code it. The Web Developer version comes with some AJAX controls so I can play around with them as well. And it's still free.

The question is will there come a time when all Microsoft's development tools are free? We're signed up for the Empower program so they are virtually free for us anyway and presumably lots of other companies are on the same or similar programs. So my guess is Microsoft aren't actually making any money on development tools and with the free tools now being pretty damn good, I'm pretty sure they'll give the full versions away some time soon just to keep us all hooked.

Friday, July 20, 2007

Rain

So there was a bit of rain today, almost enough to flood our house but not quite. And our roof leaked. But the shed is intact. Then as quickly as it arrived, it stopped. And a few hours later, all the evidence had gone. Here are some pictures.IMAG0150 IMAG0155

Wednesday, July 18, 2007

How rich are you?

I've got a theory, not a particularly original theory admittedly, that we all think we're poorer than we actually are (something to do with our celebrity obsessed culture). It only takes a few seconds to plug in some numbers to the Channel 4 Rich-o-meter or The Institute for Fiscal Studies' 'Where do you fit in?' page to see how rich, or otherwise, you are. The Channel 4 one is interesting since it allows you to compare yourself to the rest of the world, which shows even a very modest UK salary is pretty damn good when compared globally. The Institute for Fiscal Studies' version is probably more accurate since it is based on household income rather than personal income.

Tuesday, July 17, 2007

Adding services to the Windows Workflow runtime and a simple activity implementation

There are two groups of Windows Workflow service that you'll come across. The first group are the well-known services that are used by the runtime itself (well-known because they are well-known to the runtime not necessarily to the developer using the runtime...). These are things like the persistence service, tracking service, data exchange service etc. You can use these services out of the box or you can inherit from them and modify them as required. Fortunately a lot of the methods in these services are virtual so you can quite easily hook in new functionality (and quite possibly get yourself in a whole heap of trouble as well).

The second group of services are services that can be used by your own custom activities. At this point it's worth considering when using your own service makes sense. For instance I downloaded an email sender activity that required me to configure the SMTP host address and port as properties on the activity. Since it was a standalone activity, this was probably the sensible option, since it simplified usage. But if I were to use the activity in many different workflows and the SMTP host changed I'd need to update every workflow to work with the new SMTP server. So if I was to write the activity myself it may well make more sense to handle all this kind of configuration in an email sender service. This also has the advantage of being able to pull the plug on emails being sent out if a poorly written workflow was sending out too many emails. So it depends on what you're trying to achieve whether services make sense or not.

To actually add your own service to the runtime is pretty straightforward. Define an interface and mark it with the ExternalDataExchange attribute.

    [ExternalDataExchange]
    public interface ISendEmail
    {
      void Send(string toAddress, string message);
    }

Next write a class that implements the interface.

    public class EmailSenderService : ISendEmail
    {
      public void Send(string toAddress, string message)  
      {
        // send it...
      }
    }

I'm not entirely sure whether you have to use an interface or if you can just use a class straight off but I've only used interfaces myself and there are at least a couple of good reasons why this is the best way to go. First, it decouples your service implementation from the activity that is calling it. For the email sender example, this means you could replace the SMTP sender with a new implementation using Exchange (or whatever) to send the email without needing to change your activity implementation. Second, if you want to use the CallExternalMethod activity to call into the service, this only accepts an interface for the InterfaceType property.

After that, all you need to do is add the service to the runtime.

    // external data exchange service
    ExternalDataExchangeService dataService = new ExternalDataExchangeService();
    runtime.AddService(dataService);
    
    // email sender service
    EmailSenderService emailService = new EmailSenderService();
    dataService.AddService(emailService);

To call the ISendEmail methods, you have two choices, hook up a CallExternalMethod activity or write a custom activity to call the method. Since I've not yet shown an activity that does anything useful, I'm going down the latter route. Create a new activity via the Add/Activity... popup menu item and add some code as follows.

    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
    {
      // generate alerts
      ISendEmail sendEmail = (ISendEmail)executionContext.GetService(typeof(ISendEmail));
      if (sendEmail != null)
      {
        sendEmail.Send("doogal@doogal.co.uk", "blah");
      }

      return ActivityExecutionStatus.Closed;
    }

This is the simplest activity that you can implement, it executes some code then returns saying "I'm done". When I work out how to implement some more complex activities, I'll write about it.

Monday, July 09, 2007

Blogging is like the lottery

The lottery people Camelot used to say "You've got to be in it to win it". Of course the chances of a big win are tiny but if you never buy a ticket you'll never win anything at all. So it is with blogging, you're pretty unlikely to ever get a big crowd of people to your blog, but occasionally you might get that £10 win. Every post is another ticket in the blogging lottery and every so often you'll get a few hits from one.

Which is what happened with my little post about SonicWall on Vista, if only because nobody else has bothered to post about it. And if anybody wants to know, the latest beta still has the same problems for me, Vista complains that two computers with the same IP address are connected to the network. But I'm coming to expect these kind of issues with Vista. Bring on SP1...

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...