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.