Tuesday, February 12, 2008

Storing variables in a XOML workflow

One problem with XOML only workflows (i.e no code-behind) is the inability to define properties in your top-level workflow to store values that will be used throughout the lifecycle of the workflow, since you haven't got a class in which to define them. Well that's what I thought anyway until I had the idea of creating a custom activity to store these variables. It's all very simple, add the activity to your workflow and set the Value property to whatever you want, then bind it to any other activities that need access to the value.

Here's the code

    public class VariableActivity: Activity
    {
      private string value;
      [Description("The value of the variable")]
      public string Value
      {
        get { return value; }
        set { this.value = value; }
      }
    }

Update - Here's an example of binding the value of the variable to an activity property

<?xml version="1.0" encoding="utf-16"?>
<SequentialWorkflowActivity x:Name="Workflow" xmlns:ns0="clr-namespace:WFBuild.Activities;Assembly=WFBuild.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
  <ns0:VariableActivity x:Name="folderToZip" Value="c:\temp" />
  <ns0:ZipFolderActivity x:Name="zipFolder" Folder="{ActivityBind folderToZip,Path=Value}" />
</SequentialWorkflowActivity>

Sunday, February 10, 2008

Metastorm book

If you've ever wanted to read about Metastorm BPM when you haven't got access to a computer, before now there wasn't really anything available. Now my boss and BPM guru Jerome Pearce has written the 'Metastorm BPM Developer's Guide'. So you can now read all about developing processes when you're sat on the loo.

http://www.lulu.com/content/1862471

Lulu looks like a great website for cheap book publishing, something I hope to be taking advantage of soon. I'll keep you posted.

Friday, February 08, 2008

Who is Edward Phillips?

Over the last few weeks I've noticed a strange thing in iTunes. Occasionally a shared music library will pop up called 'Edward Phillips's Library'. I'm even able to connect to it and listen to music. It's great, he has over 11000 tracks, not all to my taste but still something else to listen to. I did wonder where this new source of music was coming from and tonight I figured it out. I had a look at my router's web interface and noticed a few unknown computers listed in the DHCP active IP table, in fact four computers (although I suspect they aren't all connected right now).

ever since I figured out what was causing our wireless issues I've switched back to an insecure wireless network and I've never bothered to filter out MAC addresses this time around. So it looks like people are taking advantage of the fact. Am I bothered? Not really. I've not noticed any particular performance problems and it seems like my generosity has been repaid by having access to some different music.

On another note, I wonder what the music industry makes of all this? I'm guessing they don't want people sharing out their music collections to all and sundry, we should be good consumers and buy all the CDs instead. But then it's Apple doing this and they make plenty of money for the music industry with their DRM downloads. So perhaps they'll turn a blind eye?

Currently listening to Purple Rain by Prince from the album Purple Rain

Wednesday, February 06, 2008

FeedDemon

I've been using RSS Bandit for many years. I did check out FeedDemon when I was initially looking for an RSS reader, but was put off by the price. Yeh I'm a tight arse, what was it, about $25 or so? But RSS Bandit was free and good enough for my needs. Anyway, when I found out FeedDemon was going to be free, I decided it was time to look at again. And boy am I glad I did.

First, it just works. Whereas RSS Bandit seemed to have a few bugs (the toolbar turning into a big red cross or the app not loading forcing me to delete some XML files) FeedDemon hasn't crashed once. Actually I'd like to see FeedDemon crash, just to see how it handles it (I'm hoping it offers to send the details off so it can be fixed)

Second, it's faster. Yes, I love .NET but real proper compiled Delphi code is always quicker, unless you screw it up, which Nick Bradbury hasn't done.

Third, it has more features. The dinosaur report has been most useful for clearing out feeds I never read.

I have only one request. I signed up for Newsgator for no other reason than I thought this feature would appear (Newsgator serves me no purpose since I'm pretty much always at the same machine) but it didn't. Since Newsgator presumably has a huge database of users and their feeds, I'm guessing it wouldn't be too difficult to suggest new feeds I should subscribe to based on my current feed list. I dunnow, perhaps there are privacy issues around this but I'd sure like to see it available.

Friday, February 01, 2008

Why dynamic languages suck

I had a little bit of JScript that wasn't working as expected. I stared and stared at it... For a very long time... Here it is.

var splitDetails = details.split("\t");
for (var i=0; i<splitDetails.Length; i++)
{
  // do something
} 

The problem was that the //do something was never getting hit. Life would have been easier with a debugger but this was server-side JScript in Metastorm which doesn't support debugging so I was forced to just stare at it to work out what was wrong.

No doubt you're now shouting at your computer "you're a fecking idiot! The problem is obvious!" and perhaps you'd be right. Perhaps I was just having a senior moment, which I think I'm entitled to have at my age. For those of you who haven't spotted the error I'd typed length with an upper-case L.

So what happens here? JScript doesn't throw an error because I've tried to access a property that doesn't exist, instead it returns null. Then I presume it converts that to 0 for the loop test so never drops into the code inside the loop.

This kind of thing is all too easy in a dynamic language. If I was using a compiled statically-typed language the compiler would have picked up the problem immediately. Perhaps the advantages of dynamic languages outweigh these kind of problems but personally I'd rather have the crutch of a compiler.

Wednesday, January 30, 2008

Building C# projects with Windows Workflow

wfbuild After reading a post about using Windows Workflow to build an SMTP server, I started thinking of something I could use WF for outside the world of business processes. For a long while I've also been planning on automating my build processes for various projects I've got on the go. I then realised I could combine the two and develop an automated build system with WF. At this point, if you have any experience of automated builds, you're probably thinking "Why the hell don't you use NAnt or MSBuild like any normal person?", which is a fair point.

But using WF has a few advantages. First, as far as I'm aware NAnt and MSBuild don't provide a graphical designer for their build projects (I'm quite happy to be proved wrong on this point) whereas I was able to knock together a designer based on the Microsoft example pretty quickly. Second, I just wanted to use WF for something not related to business processes. It is a very flexible technology and I'm not sure people have realised its potential. I blame the name, which makes everyone assume WF has something to do with workflow, which for many people has a very particular meaning. Third, a WF solution shares the main advantage of NAnt and MSBuild, the build scripts can be simple XML files. To be fair it does currently depend on Visual Studio being installed, although I'm sure I could build projects using the C# compiler directly (probably taking advantage of the MSBuild API in fact!).

So after a couple of hours work I had a few activities, a designer and a command-line application to run the workflow XOML (see my previous post) and a working build system. OK, I still have some tidying up to do but I was amazed at what I'd managed to achieve. After some more work I'll dump my code somewhere for people to play with.

Another take on this may be to use the WF designer to generate NAnt and MSBuild XML files directly. My understanding is that WF provides support for using your own custom DSLs. Something else to look at...

Update – You can now download the source code here. The only warranty that comes with it is that “it works on my machine”. In fact it’s quite possible it won’t work on your machine since some of the locations are hardcoded. There are four projects, one is the workflow designer control, then there is a GUI application that uses that control, there’s an assembly full of activities (some of which could be useful in other workflows) and finally there is a console application that can be used to run your build from the command line.

Monday, January 28, 2008

Running a XOML workflow from the command line

I needed to be able to execute a XOML workflow from the command line so I wrote this little app that takes a XOML file as an argument and executes it. This is part of something that should become a nice example of using WF to build applications for non-programmers, i.e. using activities as building blocks like Lego. More on that to follow, perhaps.

using System;
using System.IO;
using System.Threading;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Runtime;
using System.Xml;

namespace WFBuild
{
  class Program
  {
    private static AutoResetEvent waitHandle;
    static void Main(string[] args)
    {
      try
      {
        Console.WriteLine("WFBuild");
        if (args.Length != 1)
        {
          Console.WriteLine("Usage");
          Console.WriteLine("WFBuild <XOML file to execute>");
        }
        else
        {
          WorkflowRuntime workflowRuntime = new WorkflowRuntime();
          workflowRuntime.StartRuntime();
          workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted);
          workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowRuntime_WorkflowTerminated);

          FileStream stream = new FileStream(args[0], FileMode.Open, FileAccess.Read);
          XmlTextReader reader = new XmlTextReader(stream);
          WorkflowInstance instance = workflowRuntime.CreateWorkflow(reader);
          waitHandle = new AutoResetEvent(false);
          instance.Start();
          Console.WriteLine("Executing...");
          waitHandle.WaitOne();
        }
        Console.WriteLine("Press return");
        Console.ReadLine();
      }
      catch (WorkflowValidationFailedException exp)
      {
        // catch workflow failed validation exception and show validation errors
        Console.WriteLine("Validation failed:");
        foreach (ValidationError error in exp.Errors)
        {
          Console.WriteLine(error.ToString());
        }
        Console.WriteLine("Press return");
        Console.ReadLine();
      }
      catch (Exception e)
      {
        Console.WriteLine(e.Message);
        Console.WriteLine("Press return");
        Console.ReadLine();
      }
    }

    static void workflowRuntime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
    {
      Console.WriteLine("Workflow terminated");
      Console.WriteLine(e.Exception.Message);
      waitHandle.Set();
    }

    static void workflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
    {
      Console.WriteLine("Workflow completed");
      waitHandle.Set();
    }
  }
}

Update - I've updated the code to show validation errors

Thursday, January 24, 2008

Google wants me

OK, not quite but I did receive an email the other day saying

Google Sydney Engineering division are visiting London to talk about R&D in Australia. You have either been referred by a friend or current Google employee as someone who may be interested in joining us for a special G'Day Google event at Google UK in London.  The evening will include plenty of food and beverages, Aussie style of course...blah...blah... If you are also interested in what is happening in Google London, this will also be a chance to meet local Googlers who can give you an insight into our local engineering divisions.

This intrigued me. First, who had referred me? I don't know anyone at Google but I do know a few people in Australia, so I guess it must have been one of them (perhaps my boss is trying to give me a gentle hint!).

And why are Google Australia coming to the UK to try and recruit people? Do they think all our current bar staff want to return home and start working for Google? Are a lot of the Ozzies over here IT people? Or are they after getting UK residents to move over there?

Anyway, I was interested in going along, not because I think I'd have any chance of working for Google since my predominantly Windows background wouldn't fit in with their open source Linux and Python development. I also doubt I'd manage to refrain from laughing when asked one of their typical interview questions, presuming I got that far. But I was interested in knowing what they do and the kind of people who work there and they did mention beer... OK, they didn't mention beer but they did mention Australian beverages which I can only assume means beer.

They provided a link to their site to get more details and to RSVP. Except it doesn't work and hasn't done for two days. I'm wondering if this is some kind of test in itself, to weed out those people not clever enough to figure out how to find their web page.

Update - After a little research it would appear the URL provided on their email is a link to one of Google's internal subdomains. Clearly not all of Google's employees are geniuses.

Sunday, January 20, 2008

What are spammers up to?

The Random Pub Finder has a form for suggesting pubs to be added to the database. For a long time, we've had spammers posting lots of dodgy URLs via the form, which is not unexpected, since they think they will get added directly to the database. In fact, the suggestions just get emailed to me, in part to reduce the risk of dodgy URLs ending up on the site and also because I've never got round to automating the process.

Lately the spam has been increasing but not in any sensible way. I'm getting things like "Bill, Man Hatten", "Halo, Moscow" and "Diesel, Washington" coming through. It's left me scratching my head as to what these guys are up to. Why would they be submitting this stuff? It isn't linking through to anything and doesn't seem to contain anything of interest. Are we just the victims of an exceedingly stupid spammer?

Friday, January 18, 2008

Using var in C# 3.0

I hadn't really read much about the new var keyword in C# 3.0 but it had troubled me. I had assumed it was like the var keyword in JScript and meant C# was turning into a late-bound dynamic language which I really wasn't keen on. As I mentioned previously, this can cause problems in JScript.NET when the compiler can't infer the type.

So I was pleased to see that the IL generated by this is still the same as if strongly-typed variables were used and if the compiler can't infer the type from the code, it won't compile. But there's still something of a problem. The addition of this new keyword means that coders can now just declare every local variable using it (fortunately not member variables) which will lead to less clear code. Explicit declarations are good, there's no question what is going on.

And I just know in a few months/years time I'm going to be looking at some code I have to maintain and I will be swearing under my breath and asking why some pillock declared everything using var.

Friday, January 11, 2008

Great titles don't always lead to lots of readers

Some time ago Nick Bradbury (author of the marvelous FeedDemon which is now free!) said that the best way to increase feed readership is to use great titles. Whilst this is undoubtedly true if your blog has a lot of subscribers, the fact is most of us Z-list bloggers don't actually have many people who subscribe to our blogs. According to Google Sitemaps I currently have two people signed up via Google Reader, which I'm pretty astounded by given my tendency to ramble on incoherently about random subjects.

The fact is 90% of my hits come from Google and Google doesn't care about great titles, it cares about matching up search terms to relevant content. One good way to drive traffic to your site is to write about things that interests searchers and that other people aren't writing about, given that if some A-list blogger does write about it you'll get shoved down the search result list. For me, SonicWall, HTML tick marks and Vista sound problems are the most popular subjects...

Anyway, the things to have in your titles are the words people are likely to use when they are searching for something related to your post. If it's in the title, Google will consider the text more important than if it's just in the body of the post somewhere. So forget great titles, have relevant titles.

Tuesday, January 08, 2008

JScript.NET performance

JScript.NET doesn't really seem to have caught on too much. The reasons are clear, there is no IDE support for it in Visual Studio.NET and if the company that developed the language doesn't want to provide an IDE, who else is likely to?

But since Metastorm BPM uses JScript.NET as its primary interface to the world of .NET I have to use it on a fairly regular basis. Many moons ago I had a look at the IL code produced from compiling JScript.NET and was horrified to see it was all late bound calls. Funnily enough the primary reason somebody had decided to use JScript.NET rather than JScript was to try to improve the performance of 8000 lines of server-side JScript code (why he had ever thought that 8000 lines of JScript code would not have performance problems I don't know). Anyway, compiling it did improve the performance somewhat but probably not so much as if the IL code produced had not been late bound.

So now several years later, whilst preparing some course notes, I thought I'd take a look at what the IL code looked like these days in .NET 2. And the results are worth noting I think.

Say we have two methods as follows

        public static function DoSomethingFast() : Object
        {
            var test = new StringBuilder();
            test.Append("a");
            test.Append("a");

            return test.ToString();
        }   

        public static function DoSomethingSlow() : Object
        {
            var test = "Hello world";

            var test = new StringBuilder();
            test.Append("a");
            test.Append("a");

            return test.ToString();
        }

After compiling that with jsc.exe, the IL produced for the first method looks like this

.method public static object DoSomethingFast() cil managed
{
    .maxstack 3
    .locals init (
        [0] class [mscorlib]System.Text.StringBuilder builder,
        [1] object obj2)
    L_0000: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_0005: stloc.0
    L_0006: ldloc.0
    L_0007: ldstr "a"
    L_000c: call instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0011: pop
    L_0012: ldloc.0
    L_0013: ldstr "a"
    L_0018: call instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_001d: pop
    L_001e: ldloc.0
    L_001f: callvirt instance string [mscorlib]System.Text.StringBuilder::ToString()
    L_0024: stloc.1
    L_0025: br L_002a
    L_002a: ldloc.1
    L_002b: ret
}

Looking at that lifted my spirits since it looks like the kind of code you'd get from a compiled C# method, no late-binding at all. Now on to the second method, which has only one difference from the first one.

.method public static object DoSomethingSlow() cil managed
{
    .maxstack 11
    .locals init (
        [0] object obj2,
        [1] object obj3,
        [2] class [Microsoft.JScript]Microsoft.JScript.LateBinding binding,
        [3] class [Microsoft.JScript]Microsoft.JScript.LateBinding binding2,
        [4] class [Microsoft.JScript]Microsoft.JScript.LateBinding binding3,
        [5] object obj4,
        [6] object obj5,
        [7] object obj6)
    L_0000: ldstr "Append"
    L_0005: newobj instance void [Microsoft.JScript]Microsoft.JScript.LateBinding::.ctor(string)
    L_000a: stloc.2
    L_000b: ldstr "Append"
    L_0010: newobj instance void [Microsoft.JScript]Microsoft.JScript.LateBinding::.ctor(string)
    L_0015: stloc.3
    L_0016: ldstr "ToString"
    L_001b: newobj instance void [Microsoft.JScript]Microsoft.JScript.LateBinding::.ctor(string)
    L_0020: stloc.s binding3
    L_0022: ldstr "Hello world"
    L_0027: stloc.0
    L_0028: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_002d: stloc.0
    L_002e: ldloc.2
    L_002f: dup
    L_0030: ldloc.0
    L_0031: ldtoken TestSpeed.TestClass
    L_0036: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_003b: call object [Microsoft.JScript]Microsoft.JScript.Convert::ToObject(object, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_0040: stfld object [Microsoft.JScript]Microsoft.JScript.LateBinding::obj
    L_0045: ldc.i4.1
    L_0046: newarr object
    L_004b: dup
    L_004c: ldc.i4.0
    L_004d: ldstr "a"
    L_0052: stelem.ref
    L_0053: ldc.i4.0
    L_0054: ldc.i4.0
    L_0055: ldtoken TestSpeed.TestClass
    L_005a: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_005f: call instance object [Microsoft.JScript]Microsoft.JScript.LateBinding::Call(object[], bool, bool, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_0064: pop
    L_0065: ldloc.3
    L_0066: dup
    L_0067: ldloc.0
    L_0068: ldtoken TestSpeed.TestClass
    L_006d: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_0072: call object [Microsoft.JScript]Microsoft.JScript.Convert::ToObject(object, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_0077: stfld object [Microsoft.JScript]Microsoft.JScript.LateBinding::obj
    L_007c: ldc.i4.1
    L_007d: newarr object
    L_0082: dup
    L_0083: ldc.i4.0
    L_0084: ldstr "a"
    L_0089: stelem.ref
    L_008a: ldc.i4.0
    L_008b: ldc.i4.0
    L_008c: ldtoken TestSpeed.TestClass
    L_0091: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_0096: call instance object [Microsoft.JScript]Microsoft.JScript.LateBinding::Call(object[], bool, bool, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_009b: pop
    L_009c: ldloc.0
    L_009d: stloc.s obj4
    L_009f: ldloc.s obj4
    L_00a1: isinst object
    L_00a6: dup
    L_00a7: stloc.s obj5
    L_00a9: brfalse L_00ba
    L_00ae: ldloc.s obj5
    L_00b0: callvirt instance string [mscorlib]System.Object::ToString()
    L_00b5: br L_00ee
    L_00ba: ldloc.s obj4
    L_00bc: stloc.s obj6
    L_00be: ldloc.s binding3
    L_00c0: dup
    L_00c1: ldloc.s obj6
    L_00c3: ldtoken TestSpeed.TestClass
    L_00c8: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_00cd: call object [Microsoft.JScript]Microsoft.JScript.Convert::ToObject(object, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_00d2: stfld object [Microsoft.JScript]Microsoft.JScript.LateBinding::obj
    L_00d7: ldc.i4.0
    L_00d8: newarr object
    L_00dd: ldc.i4.0
    L_00de: ldc.i4.0
    L_00df: ldtoken TestSpeed.TestClass
    L_00e4: call class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine::CreateEngineWithType(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_00e9: call instance object [Microsoft.JScript]Microsoft.JScript.LateBinding::Call(object[], bool, bool, class [Microsoft.JScript]Microsoft.JScript.Vsa.VsaEngine)
    L_00ee: stloc.1
    L_00ef: br L_00f4
    L_00f4: ldloc.1
    L_00f5: ret
}

Suddenly things look terribly messy again, with lots of late-binding goo all over the place. The reason is pretty straight forward I think. In the first case, the compiler can infer the type of the test variable, whereas in the second case it can't because the variable is used to hold two different types. It does however mean that minor changes to code can lead to it being compiled completely differently without any warning, which is a bit of a worry.

Thursday, January 03, 2008

Writing software for mutant women

For me, the best part of "Dead Ringers" (the David Cronenberg film, not the mildly amusing impressions show) is the gynaecological instruments developed by one of the freaky medical twins. He's so out of his head on drugs that he thinks the women who are coming to his surgery are all mutants so he has to produce some terrifying new instruments to deal with them.

What brought this to mind was seeing the film again for the first time in about 15 years and also reading one of my Christmas books, "Why Software Sucks". Although seemingly unrelated, I think there is some common ground. As Davis Platt points out many times in his book, the reason why software sucks is because us software developers don't understand our users. We may not be out of our minds on drugs (at least not all the time) but our lack of empathy with our users mean we continually produce software that doesn't meet their needs.

I'm mostly in agreement with him here, although I'm not sure if he's having a go at developers directly or more generally companies that produce software. As a rule, the actual developers don't always have much of a say in what gets pushed out the door. Product management, sales, marketing, CTOs, QA and a host of other people have a pretty big input into what goes into software and how it should look and feel, whereas developers are often just following orders.

There is a reason I'm not too keen on the book, it does tend to re-hash a lot of what Alan Cooper wrote in "About Face". To be fair he is aiming at a different audience, normal people, who are pretty unlikely to have read Cooper's book. Me being a geek and not a normal person have read Cooper's book and Platt's book. If you're a developer, I'd recommend "About Face", if you're just a normal person forced to use software, I'd recommend "Why Software Sucks".

And as a developer I'm trying my best to write software that is not for mutant women.

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.