Tuesday, February 24, 2009

Spotify – the future of music?

I’ve just discovered Spotify (via Tim Anderson) and so far I’m thoroughly impressed. Previous implementations of this kind of thing (last.fm, Pandora) have restricted your choices of what you can listen to, making them feel more like listening to a radio station than listening to your own music collection. But Spotify lets you choose exactly what you want to listen to from its database. It doesn’t have every song by every artist, like most online music sources there’s no Beatles (obviously Jacko doesn’t need the extra cash, which doesn’t quite match with what I’d heard), some of my obscure musical preferences are missing (no Godspeed You Black Emperor) and even the artists they do have aren’t generally complete. But that’s a minor quibble, there’s a still a huge range of music available.

Streaming is super quick and the ads are currently pretty infrequent and unobtrusive. The bandwidth requirements seem pretty minimal too, I could happily work in a Remote Desktop session whilst listening and I didn’t notice any dropouts.

So is this the future of music listening? I certainly hope so. The big question is whether their business model works. Will the ads cover costs? Will the music companies suddenly get cold feet like they did with Pandora in the UK? Will their servers continue to be so snappy when they become super popular? If the ads become too frequent or in your face, listening might become painful. OK, you can get rid of the ads by paying $9.99 a month but if I was to pay for any online music service I’d want to have the physical bits (with no DRM) so I’d be sure I can still listen to them if the company folded.

The only thing I’d really like to see is the integration of the online stuff with my own music library. So if I want to listen to my own ripped music I can from the same player, rather than having to jump out to iTunes. But in the mean time, I’ve got a lot of new (to me) music to listen to.

Sunday, February 22, 2009

BPM for business users

Prototype activity From my time on the Metastorm forums one thing is obvious, most people implementing Metastorm solutions are not business people. Mostly they are technical people, with a few not so technical people. And it’s generally the not so technical people who are having the biggest problems getting to grips with the product. Is that a failing of the software? I’d say not, although it could arguably be a failing of the marketing of the software, which like most BPM products suggests a non technical person can use it from start to finish to produce a working system.

This situation reminds me of other areas of software development, like the development of websites, or application development. A while ago, people thought anyone with a copy of FrontPage could develop their own website or anyone with a copy of VB could knock together a working application. But these other areas have matured enough to recognise that actually those pesky developers are still required (or to put it another way, designers can’t code and coders can’t design) and the development environments are now set up in such a way that the designers can work with the coders to produce a working system. For a website, the designer can produce a HTML layout that looks good and then the coder can add the required code so it does something. In the world of Windows development we are moving to WPF and other technologies where the layout is separated from the code, so the designer can produce the pretty screens and the coder can then add code to do stuff without both sides stepping on each others toes too much.

And for me, that’s the approach that BPM will have to take at some point, realising that the business people can’t code and the coders don’t understand the business, so trying to put only one of these people in charge of the BPM system will never fly. I think Windows Workflow is a step in the right direction with its ability to create custom activities. The coder can create all the required custom activities and then the business person can plug these together like Lego as required. Or this could work in the opposite direction, where the business user produces their process and adds dummy activities where they need functionality to be implemented and the coder goes off and creates the required activities.

With that in mind, I created a simple PrototypeActivity that can be added to a workflow and lets the user specify the inputs and outputs that will be need to be implemented. The screenshot shows how it looks in operation (although a proper implementation would probably provide a better user interface). The code is below.

  public enum PropertyDirection
  {
    In,
    Out,
    InOut
  }

  public class ActivityProperty
  {
    private string propertyName = "";
    public string PropertyName
    {
      get { return propertyName; }
      set { propertyName = value; }
    }

    private string description;
    public string Description
    {
      get { return description; }
      set { description = value; }
    }

    private PropertyDirection direction;
    public PropertyDirection Direction
    {
      get { return direction; }
      set { direction = value; }
    }

    public override string ToString()
    {
      return direction.ToString() + " - " + propertyName.ToString();
    }
  }

  public class PrototypeActivity: Activity
  {
    public static DependencyProperty PropertiesProperty =
      DependencyProperty.Register("Properties", typeof(ActivityProperty[]), typeof(PrototypeActivity));

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [Browsable(true)]
    [Description("The properties the activity requires")]
    public ActivityProperty[] Properties
    {
      get
      {
        return ((ActivityProperty[])(base.GetValue(PrototypeActivity.PropertiesProperty)));
      }
      set
      {
        base.SetValue(PrototypeActivity.PropertiesProperty, value);
      }
    }
  }

Friday, February 20, 2009

Rude Swiss

Condom shop in BaselContinuing with the rude theme, here’s a photo from my not so recent trip to Basel in Switzerland. We may assume it’s the Dutch who have the monopoly on lewd behaviour and the Swiss are more interested in banking, cuckoo clocks and chocolate. But perhaps the racial stereotypes are incorrect, here’s a shop devoted to selling condoms. I’m probably showing off my ignorance, but how can you have a shop that only sells condoms? Are there enough varieties to fill it? I didn’t enter to find out the answer.

Friday, February 13, 2009

Rude snowman

Rude snowman

Perhaps it’s just my dirty mind, but the snowman that Jo and Lola made the other day ended up looking less like a man and more like, well something else entirely. The huge erection has now melted away…

Monday, February 09, 2009

Not getting album artwork from iTunes

I had a thought the other day, how does iTunes get its album artwork? I guessed it was some kind of web service so fired up Fiddler to see if anything was getting thrown around. And sure enough iTunes was throwing a HTTP request to some server, that looked something like this (this is somewhat simplified since you can also pass CDDB information which presumably increases the chances of getting a correct hit).

http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa?an=Cult&pn=Love

Put that in your browser and you’ll see an XML document is returned. It’s in Apple’s favoured XML format and it looks like there is a URL in there that can be used to retrieve the album artwork. Unfortunately, plugging that URL into your browser won’t return the image… At this point I kind of lost interest because I didn’t actually have any use for getting hold of album artwork (unless I wanted to automate the process for iTunes but I’m not that anal about getting artwork). But I did have a bit of a look round the web and found these two articles on doing the same thing. And it would appear Apple are pretty keen on people not downloading artwork via their own code. Dunnow why, copyright issues I guess, but it looks like even if it is possible to hack the system, Apple will change the way it works to stop your code working.

If you do want to download artwork, I suspect Amazon Web Services is going to be a better approach.

Saturday, February 07, 2009

How to confuse IE with a 302 redirect

It’s very simple, create a PHP pages called test.php, the contents of which are

<?php
  header("Location: test.php");
  exit;
?>

Now point IE8 (probably earlier versions of IE as well I’d guess) at the page and watch as it tries in vain to fetch the page. It eventually times out, after trying to fetch the same page over 1000 times (take a look with Fiddler to see the problem). FireFox and Chrome are much more sensible in this scenario and give up quite soon. As the dumb developer who caused this problem, it took me quite some time to figure out my redirect wasn’t working rather than the website being very ill

WPF ASCII grid part 2

This is the second in a series where I try to learn how to develop WPF apps. The first part is here.

So my next step was to put all the ASCII grid code into a custom control. My first attempt was to inherit from Grid and add the logic to create the cells in the inherited class. The problem with this approach was that the grid’s row and column definitions could be edited in Visual Studio which I didn’t want. So I decided to inherit from Panel and add the grid as a child control. When I did that, running the application showed up nothing at all. I finally realised that was the wrong approach, since the Panel class is designed to allow the end user to add their own child controls. I guess the WPF runtime is looking at the XAML to see which children should be added to the panel and there aren’t any. So finally I inherited from Control and then had to figure out how to add the grid as a child. Control doesn’t have a Children property so it’s not obvious how to add your own child controls. But it turns out it’s pretty easy to do. You just have to override VisualChildrenCount and GetVisualChild as shown below.

  public class AsciiGrid : Control
  {
    static AsciiGrid()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(AsciiGrid), new FrameworkPropertyMetadata(typeof(AsciiGrid)));
    }

    private Grid grid;
    public AsciiGrid() : base()
    {
      grid = new Grid();

      const int width = 16;
      const int height = 16;
      for (int x = 0; x < width; x++)
      {
        grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star), });
      }
      for (int y = 0; y < height; y++)
      {
        grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star), });
      }
      for (int x = 0; x < width; x++)
      {
        for (int y = 0; y < height / 2; y++)
        {
          int asciiValue = ((y * width) + (x + 1));

          // number
          TextBlock rect = new TextBlock();
          rect.Text = asciiValue.ToString();
          rect.SetValue(Grid.RowProperty, y * 2);
          rect.SetValue(Grid.ColumnProperty, x);
          grid.Children.Add(rect);

          // ASCII value
          rect = new TextBlock();
          rect.Text = Convert.ToChar(asciiValue).ToString();
          rect.SetValue(Grid.RowProperty, (y * 2) + 1);
          rect.SetValue(Grid.ColumnProperty, x);
          grid.Children.Add(rect);
        }
      }
    }

    protected override int VisualChildrenCount
    {
      get
      {
        return 1;
      }
    }

    protected override Visual GetVisualChild(int index)
    {
      return grid;
    }
  }
So I now have a standalone control in my application. The application looks exactly as it did before but I’ve now got an exceedingly useful re-usable ASCII grid control. OK, maybe not so useful, but this is a learning experience, so cut me some slack. The only thing to note is the changes required to the XAML to host the control, which looks like this.
<Window x:Class="Ascii.AsciiWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Ascii="clr-namespace:Ascii"    
    Title="Ascii" Height="370" Width="413" Loaded="Window_Loaded">
    <Ascii:AsciiGrid x:Name="m_Grid">
    </Ascii:AsciiGrid>
</Window>

WPF ASCII grid part 1

Ascii

I thought it was about time I learned about WPF so thought I’d start with a very simple application, a grid showing the ASCII characters. It’s kind of pointless at the moment since you can find this information on the web pretty easily but I think I can make it somewhat more powerful and in the process learn more about WPF. This is what I’d like to add in the future and will hopefully blog about as I implement them

          • Turn the grid into a control
          • Show the extended ASCII characters
          • Make it look much sexier
          • Let the user choose the font
          • Let the user change the code page

The code currently looks like this. It’s all pretty straighforward. The first thing to note is the GridUnitType.Star enumerated value which means each column and row will be spaced equally in the window.

The other thing to note is the weird way the text blocks are assigned to the correct cells of the grid. It doesn’t seem a natural way to do it, but I guess once you know that’s how it’s done, it’s pretty straightforward.

    public AsciiWindow()
    {
      InitializeComponent();
      BindGrid();
    }

    private void BindGrid() 
    { 
      const int width = 16;
      const int height = 16;
      for (int x = 0; x < width; x++) 
      { 
        m_Grid.ColumnDefinitions.Add(new ColumnDefinition() 
        { Width = new GridLength(1, GridUnitType.Star), }); 
      }
      for (int y = 0; y < height; y++) 
      { m_Grid.RowDefinitions.Add(new RowDefinition() 
        { Height = new GridLength(1, GridUnitType.Star), }); 
      }
      for (int x = 0; x < width; x++) 
      {
        for (int y = 0; y < height/2; y++) 
        {  
          int asciiValue = ((y*width)+(x+1));

          // number
          TextBlock rect = new TextBlock();
          rect.Text = asciiValue.ToString();
          rect.SetValue(Grid.RowProperty, y*2); 
          rect.SetValue(Grid.ColumnProperty, x); 
          m_Grid.Children.Add(rect); 

          // ASCII value
          rect = new TextBlock();
          rect.Text = Convert.ToChar(asciiValue).ToString();
          rect.SetValue(Grid.RowProperty, (y * 2)+1);
          rect.SetValue(Grid.ColumnProperty, x);
          m_Grid.Children.Add(rect); 
        } 
      } 
    }

Read part 2

Wednesday, February 04, 2009

Bugs = complexity

Many moons ago I discovered a bug in some software I use on a fairly regular basis (I won’t tell you which software since it isn’t relevant to the story and I don’t want this to appear to be a critique of the company or their development process, though you can probably figure out who it is if you look round the rest of this site). The application allows you to write server scripts in JScript.NET. It also does some validation of these scripts but in certain circumstances this validation incorrectly flags up problems with scripts that aren’t actually problems. It was a fairly innocuous bug so I reported it but didn’t push for it to be fixed.

Fast forward a year or so and a new version of the software comes out with a new feature that validates your scripts when you try to deploy a project to the server. Generally a useful feature except the previous bug is still there. Meaning I now can’t deploy perfectly valid projects due to the interaction of the previous bug with this new feature. So a minor unfixed bug has blown up into a major issue.

Which got me thinking. The kind of bugs that don’t get fixed are these kind of things. They only occur in particular rare scenarios. If they happened all the time, the chances are they would get fixed, if they are serious enough. But they do add complexity to the product and testing. No longer can we assume doing A will cause B to happen. Most of the time it will be true but occasionally doing A will cause C to happen. So any testing of new features will have to take this into account. In fact, testing of any new feature will have to take into account any unfixed bugs that may impact the new functionality.

There are many reasons for trying to fix as many bugs as possible (keeping customers happy, trying to fight the constant entropy inherent in software development), may I add this to the list? 

Tuesday, February 03, 2009

Backups

My backup strategy over the last few years has been pretty lame. Every month, Outlook reminds me I should do a backup of my source and I burn a CD. This kind of worked for a while but I’ve been getting more and more nervous about it. Am I sure I’m backing up everything I need to? Most of my email is stored on the web in GMail but there’s heaps of other things on my PC that may be vital but I just haven’t realised it yet. Not only that, but I was getting to the stage where a single CD wasn’t enough and the time taken to burn even that single CD was starting to bore me.

So today when I popped into Maplin for a USB cable I thought I’d have a look at the external hard drives. The Toshiba Stor E looked like the best bang for buck, 500GB for £65. So far I’m impressed. It looks pretty robust, with an aluminium case and no pointless bling. Installation was exceedingly straightforward. Plug it in and Vista recognised it immediately. Then simply configure Vista to do an automatic backup every week and that’s it.

Admittedly my first backup is currently happening and it’s not super fast, but I guess that will improve once it’s done the first full backup. Once it’s finished I’ll have to check that it’s actually backed up everything I want it to, since the backup utility is pretty vague about what it’s going to do. And then I’ll have to think about taking my backup offsite, which for me means carrying the drive into the house. If my shed and house burn down, I’ll have bigger worries than having a decent backup. 

Sunday, February 01, 2009

Dan le Sac vs Scroobius Pip "Thou Shalt always Kill"

I just love this. It’s funny, has a great beat and what a beard!

One of the problems with working from home

However much it snows this evening, I can’t call the office in the morning claiming the weather is too bad for me to get to work.

Friday, January 23, 2009

A simple plug-in framework for .NET apps

If you want to add plug-in support to your application then you might start looking at Microsoft’s Managed Extensibility Framework or System.Addin. Whilst I have no experience of these frameworks, when I looked at them I decided they were just too complicated for my needs. Also I was targeting .NET 1.1 so couldn’t use either of them. All I wanted to be able to do was to write a class that implements a particular interface (for importing files from one of a number of external file formats), dump it in the application’s directory and for the application to recognise the assembly and add the supported file type to its list of available file types. I won’t bore you with the details of the interface, since it’s obviously specific to the application being developed, but here’s the code for loading the assemblies and creating instances of the available importers.

      // load all importers
      string applicationDirectory = System.IO.Path.GetDirectoryName(
        Assembly.GetExecutingAssembly().Location);
      DirectoryInfo info = new DirectoryInfo(applicationDirectory);
      FileInfo[] files = info.GetFiles();
      List<Type> types = new List<Type>();
      for (int i = 0; i < files.Length; i++)
      {
        // load assemblies and see what is in there
        if (files[i].Extension.ToLower() == ".dll")
        {
          Assembly thisAssembly = Assembly.LoadFrom(files[i].FullName);
          Type[] assemblyTypes = thisAssembly.GetTypes();
          for (int j = 0; j < assemblyTypes.Length; j++)
          {
            if (assemblyTypes[j].GetInterface("IImport") != null)
            {
              ConstructorInfo wfConstructor = assemblyTypes[j].GetConstructor(Type.EmptyTypes);
              object obj = wfConstructor.Invoke(null);
              IImport importer = (IImport)obj;

              importers.Add(importer);
            }
          }
        }
      }

This worked well for me. I’m sure it wouldn’t work in all scenarios and MEF/System.Addin may be more appropriate to more complex scenarios (if you are worried about versioning or running your add-in in a sandbox for instance) but it’s a quick and dirty method for implementing plug-ins.

Thursday, January 22, 2009

Turn on script debugging!

The other day I was pointed in the direction of a new website for a company I know. The owner was very pleased with the work a web design agency had done for him. Unfortunately the first thing I saw was a script error, then another then finally the page appeared. Pretty nice it was too, but the whole experience was somewhat spoiled by those script errors. Of course most people have script debugging turned off so they don’t see these errors but if you’re paying someone to develop your website it is something you need to do.

I wouldn’t recommend having it turned on all the time, the fact is the web is almost unusable if you do have script debugging enabled. Some websites cause so many errors that the only solution is to kill your browser and a large number of high profile websites have some kind of script error. Of course, some would say this is only an IE problem and point to the crappy way IE implements the debugger notification in a modal dialog box and that is certainly true. Perhaps it was done that way so that it is so in your face, that if you’re the developer of the site you just have to do something about it. I think the actual consequence is that anybody who has turned it on turns it off at the earliest opportunity.

But I digress, the point is if you’ve paid somebody to develop your website, make sure they’ve actually done a decent job, even if the only people who’ll notice they haven’t are anal geeks like me.

Wednesday, January 21, 2009

The value violated the integrity constraints for the column

I was trying to import some data from an Excel spreadsheet into SQL Server today and kept getting this error message.

Error 0xc020901c: Data Flow Task: There was an error with input column "PHNUMBR1" (207) on input "Destination Input" (96). The column status returned was: "The value violated the integrity constraints for the column.".
(SQL Server Import and Export Wizard)

I was having trouble working out what SQL Server was trying to tell me. The table didn’t have any constraints that I could see so I was at a loss. The only seemingly related post was this one, unfortunately in Chinese. But it gave me a clue. I saw the words ‘NOT NULL’ and realised that was the problem. The table had some columns that did not allow NULL entries. I removed these restrictions and the import worked.

So the questions for today are - am I just stupid? Should SQL Server provide a better error message? Should it just populate NOT NULL columns with empty strings?

Monday, January 12, 2009

Waiting for ever to be an overnight success

Mister Coding Horror Jeff Atwood talks some sense about overnight success, namely that it doesn’t generally happen. Success is a long road, it can takes years for a blog or software to reach any kind of critical mass. But I think he misses an important point. Namely, that success may never come, all that hard work may be for nothing. Take blogging. How many blogs are out there? Millions probably. How many are successful? A few thousand perhaps, so the chances of hitting it big are pretty remote. What about software? Since the barriers to entry are a little higher, there’s not so much software around but how many successful bits of software are there in each mature category you look at? Two or three generally. How many software products have fallen by the wayside? Generally many more. Websites? The same kind of success rates I’d guess.

So if you enjoy writing your blog, cool. If you enjoy hacking code, great. But don’t assume you’ll be successful, over any time span. Don’t think you will be able to retire to your own private island, however much effort you put into it. Have a plan B. Get a job that pays a reasonable wage (not just piles of stock options), put some money away, pay down your debts. Oh, and don’t call your blog ‘Bell End’.

Friday, January 09, 2009

XslTransform not generating XML declarations

I’d just started having a look at some .NET 1.1 code using the XslTransform class and I was having a bit of trouble. The program that read the generated XML expected an XML declaration at the top of the document. No problem I thought, just add an omit-xml-declaration="no" attribute to the XSLT. I did that and the XML declaration still didn’t turn up. So I checked the XslTransform class to see if that had some property to force the declaration to appear but no. Then I tried fiddling with the other xsl:output attributes in the XSLT, but still nothing.

So what about the XmlTextWriter class then? No, nothing on that to control the XML declaration (although it looks like later versions of the .NET Framework do include support for this). But hang on, why am I using the XmlTextWriter class anyway? The XslTransform class will take a stream instead. And tada! That fixed the problem and also saved a few lines of code. So only an hour of my life wasted. Hopefully this will save you that time.

Wednesday, January 07, 2009

Easy SQL

Accessing databases on Windows has been a constantly moving target over the years. First there was ODBC, a pretty simple C based API. When COM came along, MS decided that they needed to have an object based data access library so out came DAO, followed pretty swiftly by RDO, OLEDB and ADO. When .NET came out we got ADO.NET and then recently we've had LINQ to SQL and now Entity Framework. LINQ to SQL lasted such a short time that I didn't even have time to look at it properly before it died. And there's probably others I've forgotten about.

Some may not think this is a problem. After all, the older technologies are still available so there's nothing stopping you continuing to use them, but the chances are you'll have to maintain a project at some point that uses another data access technology so you'll need to have a passing knowledge of all of them.

So I have an idea, Microsoft. How about adding a common API to all your data access technologies? Here's my suggested interface.

public interface IEasySQL
{
  void Connect(string connectionString);

  void ExecuteSQL(string sql);

  object[][] SelectSQL(string sql);
}

I think it's fairly obvious how this interface works, so I won't bore you with the details. Now some of you are probably thinking it's missing a few features. Things like transactions, parameterised queries, that kind of thing. Fair point, but for me at least, this would be adequate 90% of the time. If I need the more complicated stuff I can always go to the underlying API, but for most of the time I can just throw a query at the database, because that's all I need to do. Yeh, it's not scalable, but generally my queries only return a few results, maybe a thousand, and this will work fine. So how about it, MS? And if anybody is interested, I can knock together an implementation using ADO.NET.

Sunday, January 04, 2009

The Black Swan - Nassim Nicholas Taleb

I don't think the author of this book is the person who coined the saying 'black swan' but that isn't really important. What is important is what he's trying to say, that we tend to ignore the impact of highly improbable events, mostly because we aren't able to predict them. The saying comes from the fact that we thought all swans were white until somebody found a black one and our definition of a swan was turned on its head.

I think this book has become a commercial success due to a little bit of serendipity, it arrived on the shelves of bookshops at about the same time as the financial meltdown kicked off, arguably a black swan in itself (although I'd argue quite a few people saw this coming but not many realised how serious it would become). I think that's what they call ironic.

Anyway onto the book. For me it only really comes into itself in the last few chapters where he gets into the technical details of our failure to predict, which is mostly down to our use of the Gaussian/normal distribution where it isn't applicable. We should be using fractals instead (before reading this I thought fractals were purely used to produce pretty pictures). The earlier chapters could have been shortened more for my liking, there's only so many ways you can say we fail to take notice of the impact of the highly improbable (except after the event when we manage to produce a narrative to explain why it happened and wasn't totally unexpected). I guess I'm more interested in the hard facts, rather than the philosophical musings. But that said, it's still a very good read and certainly makes one think. I guess the important point is that we should try to increase our exposure to potential positive black swans (business opportunities etc) whilst reducing our exposure to potential negative ones.

Saturday, January 03, 2009

Recycling is wrong

Our council finally got its recycling strategy sorted out recently. This means that recycling is collected once a week instead of every other week and they now collect cardboard and food waste which they didn't do previously. I'm quite happy with this since it means I don't have to make regular trips to the tip anymore. Of course there have been some teething problems and I'm seriously bored of all the complaints from my neighbours. It's no wonder that Australians call us whinging poms...

But I do keep thinking there is something wrong with the whole concept of recycling. I buy a bottle of wine, drink it, then the bottle gets taken away and recycled and made into another bottle. But, but... How much energy is being used up in producing a bottle that is only used once? When I was a lad, we returned bottles to the shop (fizzy pop rather than wine in those days) and got back 2p for our bother. The bottle then got sent back to the fizzy drinks company who refilled it and sent it out again. In fact, go to India and many other 'less developed' countries today and you'll find the same thing. When you buy a drink, it comes in a scratched old bottle that has obviously been used several hundred times before. Isn't this the model we should be adopting? It seems a much better use of resources. The thing is the supply chain is already in place, all those bottles get delivered to the shops, so the same lorries could be used to return the empties back to their original source. What am I missing?

Sunday, December 28, 2008

Content is still king

One of the phrases that was coined during the early years of the web was 'content is king'. The basic idea was that content drives traffic to your site. This is clearly correct for the simple case - if you have no content at all you'll get no visitors and when you add some content you'll hopefully get more than zero visitors.

But that was in the days of not so clever search engines. These days search engines don't just take notice of the content on your site, they look at all kinds of other things so there's much more emphasis on using search engine optimisation to optimise content to get as many hits as possible. I don't think there's anything wrong with using SEO and fiddling around with keywords, layouts, titles, URLs and reciprocal linking can certainly help (although it's quite possible to get it wrong as well and lose traffic) but it seems to miss the point. The absolute simplest way to increase traffic to your site is to add more content.

To illustrate, lets take a look at some real data. I've used archive.org and data from Google Analytics to produce this graph for the Random Pub Finder.

RPF stats

What does this show? I've taken the number of monthly visitors to the site and divided them by the number of pubs reviews on the website. This doesn't give the complete picture of the amount of content on site but I'm not sure how I can work out the number of unique URLs historically. Note, using page views instead of visitors produces a pretty similar graph.

So does it prove anything? I guess if content was the only thing affecting the number of visitors then we'd expect the trend line to be horizontal so there are clearly other factors at work. We've got more inbound links than we used to have (that big spike is when we got linked from another site). I also think age of content helps search engine rankings, although I don't have any proof for this theory. But given that I've done very little SEO work on the site, it does suggest increasing content increases visitors above and beyond a direct linear increase.

As a comparison, looking at the number of visitors to www.doogal.co.uk, where the amount of content has not increased very much, shows visitor numbers haven't increased as dramatically.

To look at it another way, every new page you add to your site will add X new visitors to your site, where X can be anything from zero to a very large number. Adding a new page will probably increase the number of visitors to your site, however small.

So in conclusion, don't waste your time and money on snake oil SEO 'experts', just add some more content to your site if you want to get more visitors.

Friday, December 26, 2008

Windows Search now slows down Internet Explorer as well

One of the things that caused bad initial experiences of Windows Vista was Windows Search. As soon as you'd installed Vista, off it went indexing files on your hard disk. This could take some time and although it runs as a background task it still seems to impact the performance of your PC. Lots of people just turned off the service, although if they'd left it on for a while they'd have discovered that once the initial scan had been finished, it generally had a pretty low impact on system performance.

I left it on but I've never been overly happy with it. Fact is I don't do much searching on my PC and when I do I'm not too bothered if it takes a while. So having a constant tax on my performance (however small) seems a waste. Not only that, but it doesn't seem to index the files that are important to me. So when I do do a search I invariably have to switch to the 'search everything' mode which is really slow meaning the whole indexing palaver is completely wasted. I guess I should have set it up to index everything but never bothered, thinking it would cause the indexer to use up even more processor time.

But along comes IE8 and Microsoft have decided to use Windows Search technology to power the drop down list of suggested sites. Previously I'd used this a lot, type the first few letters of a site that I visit frequently and it would appear in the drop down list and I could select it rather than having to type the full URL. In theory that still works, but Windows Search always takes several seconds to decide what to show in the list, so it's as quick just to type in the full URL. I guess the list of suggested sites is better but the delay means it's completely useless. And typing another letter causes the search to start again from scratch...

But there is a solution to this. Disable the Windows Search service and the drop down works just like it used to do and I'm happy again. I guess this is a classic unintended consequence of powering IE8 with Windows Search, that even more people disable it.

Sunday, December 14, 2008

Revenue models for free software

I have no ideological belief that software should be free, but it certainly seems to be an increasingly prevalent model for software.  Since I don't run my own software company the code I produce doesn't have pay the bills. But although I enjoy writing it, in an ideal world I'd still like to make some kind of income from my work. Probably my biggest piece of code that I've given away has been the FreeFlow library and Administrator, that can be used with Metastorm's BPM product. I've tried out a few different revenue generation techniques with varying degrees of success.

Sell the source - This was the initial model, let anyone download the binaries but charge for the source. It worked reasonably well for the FreeFlow library, which is used by developers to talk to the Metastorm engine. I guess developers are keen to get hold of the source for libraries they are using, to modify for their own needs and reduce the risk if the developer disappears. Unfortunately Metastorm introduced their own .NET library and as time has gone on there's been a lot more interest in the standalone FreeFlow Administrator application. With this, I suspect less people care about the code since if I disappear it won't actually affect their own applications, so sales of the source code have dropped off.

Website advertising - There's been advertising on the FreeFlow website for a long while and I've made a bit of spending money from it, but that's all. One positive of writing software for a BPM product is that most of the ads are for competing BPM products which seem to earn a good amount of money. The downside is that Metastorm is just one of many BPM companies and BPM is still a fairly niche area so I don't get a massive number of visitors to the site.

Donations - This is my latest attempt at earning a bit of cash. The website and the FreeFlow Administrator now have 'Donation' buttons. So far I've not earned anything from this at all. I'm not hugely surprised. If I'm using a free piece of software I very rarely donate any money. If it's shareware, I'm unlikely to pay for the full version even if it has nag screens (WinZip anyone?). The exception to this is SmartFTP, which I paid for because it needed re-installing every few months and it's actually a nice bit of software.

Ads in the application - I haven't tried this yet and I'm not even sure how ads can be added to a desktop application but it seems to be the path FeedDemon is going down. I may wait and see how well it goes for that software and my own donations before going down the same route. One of the nice advantages of not needing to earn any money from my software is I can try out different models for as long as I want before trying out something else.

A buy out - There has been interest from people wishing to buy FreeFlow off me but there have been no concrete offers as yet. I'd definitely consider a serious offer. Even though I have some emotional attachment to the code I've written I'm not dumb enough to refuse a reasonable amount of money if somebody offered it. Unfortunately as this is a niche product, I can't really see there being a huge number of suitors... 

In conclusion, I think if I was starting from scratch I'd probably try writing software with a potentially wider audience. Something like TimeSnapper, which is of use to almost anybody. And I'd probably try a similar pricing model to their software, a basic free version and a more feature-rich professional version. Or an ad-sponsored free version and a paid-for non-ads version. Now I just have to figure out what that software would be...

Thursday, December 11, 2008

Free controls for .NET

I've come across a couple of free controls for .NET recently which look pretty cool.

DevExpress - I've always loved DevExpress. Their Delphi controls rocked (and probably still do), their .NET controls rock and their pricing has always been pretty reasonable ($2000* for every .NET control they produce for a year). Now you can get some of them for free. The download is a bit large since it includes trial versions of everything they do but some of them look pretty useful. I haven't looked too closely but from a cursory look it seems like the ASP.NET controls are more useful than the WinForms controls, which are mostly just some edit controls.

Chart control - Microsoft bought the Dundas chart controls and now you can use the Microsoft updated versions in your ASP.NET and WinForms applications for free. I assume at some point these will get rolled into the .NET Framework meaning it will be possible to add charting to apps with no extra installation woes to worry about. It will be interesting to see what now happens to Dundas (and ChartFX and any other chart control companies for that matter). I guess they have products for other platforms and more specialised charting controls that will keep them in business. If not, they may live to regret the decision to sell their code...

* Actually $1999.99 but it's quite close to $2000 - does the .99 cent/pence ruse actually still work?

Friday, November 28, 2008

The predefined type 'System.Runtime.CompilerServices.ExtensionAttribute' is defined in multiple assemblies in the global alias

Extension methods are cool and they can be used from .NET 2.0. I'm not the first to suss this out but the trick is to add the following to your code.

namespace System.Runtime.CompilerServices
{
  internal sealed class ExtensionAttribute : Attribute { }
}

This works well but there is a gotcha. Say I have two assemblies and I've added the definition to both assemblies because I want to add extension methods to both, I get the warning message

The predefined type 'System.Runtime.CompilerServices.ExtensionAttribute' is defined in multiple assemblies in the global alias

I try to produce code that doesn't generate any warnings since it's easy to miss real warnings if there are these kind of false positives. One solution is to make the definition public in one of the assemblies but I wasn't keen on that idea because the assembly is a publicly available class library whose public API I didn't want to pollute with this hack.

So the final solution was to use the InternalVisibleTo attribute in one of the assemblies. This means ExtensionAttribute only needs to be defined in one assembly. There's more info here on how to do that with strongly named assemblies since it's not completely straight forward.

One final problem is that using the assembly in a .NET 3.5 application will show the warning again since ExtensionAttribute is already defined in the .NET Framework. As far as I'm aware there isn't a solution for this problem, except producing separate .NET 2 and .NET 3.5 versions of the assembly.

Tuesday, November 25, 2008

The RPF book has a cover

Capital BoozingThanks to my other half, the Random Pub Finder book now has a proper cover. Unfortunately, in the mean time, Lulu has changed their publishing prices which means the cost for an A5 book is now much more than their standard sizes. Which, in these credit crunch times, may be putting off some purchasers. So now I'm busily repaginating it to fit into a US Trade size, whatever that is. Whatever it is, it's much cheaper to publish. So you still may want to hold off purchasing it for a bit, if you were planning on doing.

Sales so far - 4. Ho hum...

Sunday, November 23, 2008

"One or more waypoints are not routable in the specified data source" error in MapPoint

I've searched on the internet for any information about this error message returned by the MapPoint web service but haven't been able to find anything, so this is a rundown on what I've found, although this is fairly obvious stuff.

Say you're trying to route between points A, B, C, D and E. Basically the error is saying that MapPoint was unable to route between at least two of these points. So the first thing to do is figure out which points MapPoint didn't like. this can be achieved by breaking the route down into its constituent parts and try to route A to B, B to C, C to D and D to E. The chances are that at least one of these will fail.

Now it's time to take a closer look at the two points that failed. I think the best way to do this is to visualise the route using Google Maps and/or Live Maps. For me, both sites successfully routed between the two points but Google Maps showed something strange. The suggested route involved driving to a T junction, turning right, driving to the end of the road and making a  U turn and driving back along it. Moving the point to the end of the road with the T junction fixed the MapPoint error. For me, being able to route between the points was more important than being completely accurate. I'm not sure what the solution would be if complete accuracy was required.

There's now a Live Maps web service that seems to have a very similar API to the MapPooint web service. I've not looked at this too closely but I'd be interested to know if it has a better algorithm for routing so these kind of errors are reduced. I'd also be interested to know if people have found different solutions to this error message.

Wednesday, November 12, 2008

How we got into this mess

I read 'Liar's Poker' a few years ago and it was a fascinating insight into the people who work on Wall Street (or did during the 80s). The author of that book has written an article chronicling the insanity that has led us to the financial turmoil we find ourselves in. Most of it will be familiar to many people, mortgages being sold to people who had no hope of repaying them, this toxic debt being packaged up to look like low risk AAA rated bonds and the fallout when this debt started turning bad.

the article mostly talks about Steve Eisman, who seems to be one of the financiers to see this trouble coming and started shorting the market. Ironically this actually made things worse...

But when Eisman bought a credit-default swap, he enabled Deutsche Bank to create another bond identical in every respect but one to the original. The only difference was that there was no actual homebuyer or borrower.

Which may help to explain why things have got so bad. Not only have a lots of loans gone bad, but lots of fantasy loans that never actually existed have gone bad as well.

Friday, November 07, 2008

Creating images on the fly in ASP.NET

Creating images on the fly in ASP.NET is pretty simple. Here is some sample code for a generic handler that can be used as a starting point for your own implementation.

<%@ WebHandler Language="C#" Class="ImageExample" %>

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web;

public class ImageExample : IHttpHandler
{
  public void ProcessRequest (HttpContext context)
  {
    using (Bitmap bitmap = new Bitmap(300, 200))
    using (Graphics graphics = Graphics.FromImage(bitmap))
    using (SolidBrush brush = new SolidBrush(Color.Red))
    {
      graphics.FillRectangle(brush, 0, 0, 300, 200);
      // TODO - draw your image

      context.Response.ContentType = "image/jpeg";
      bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
    }
  }

  public bool IsReusable
  {
    get
    {
      return false;
    }
  }
}

Sunday, November 02, 2008

How to recover the hits to your website

RPF hits

I discussed a month or two ago about how the Random Pub Finder database died and our visitors disappeared. So how to recover from that? It turns out we didn't need to do anything, Google (and presumably the other search engines, but really who cares about them?) re-indexed our pages and presumably ranked them as highly as before because October was our best month yet for visitors, finally over the 10,000 mark.

Custom activities in Windows Workflow 4.0

Another presentation from PDC, http://channel9.msdn.com/pdc2008/TL21/

Looks like authoring custom activities has got a whole lot easier and more powerful. Designer rehosting is much simpler. Looks like I might have to finally learn some WPF though.

Wednesday, October 29, 2008

Windows Workflow 4 at PDC

See the intro to WF 4.0 at http://channel9.msdn.com/pdc2008/TL17/. Watching at the moment and looks pretty cool. Fully declarative workflows, better designer, store workflow data in a proper queryable manner in the database.

Monday, October 27, 2008

Metastorm BPM 7.6 and Windows Workflow part 6 - state machines

stateThis will almost certainly be my last post about executing Windows Workflows from Metastorm BPM as I think I've covered pretty much everything there is to cover (although leave a comment if there is something I've missed).

One of the comments from one of the previous posts was regarding state machine workflows and whether these could be executed from the Metastorm environment. So I knocked together a simple state machine workflow in Visual Studio that starts, waits in its initial state for 5 minutes then moves to the final state.

As discussed before, there is no support for publishing your own VS authored workflows to the Metastorm database so I modified the little command-line tool I wrote previously to cope with state machine workflows. For brevity I won't post all the source, just the change required, which is as follows.

        for (int i=0; i<types.Length; i++)
        {
          if (types[i].IsSubclassOf(typeof(SequentialWorkflowActivity)))
          {
            workflowType = types[i];
            break;
          }
          else if (types[i].IsSubclassOf(typeof(StateMachineWorkflowActivity)))
          {
            workflowType = types[i];
            break;
          }
        }

So after publishing the workflow, I tried to execute it from Metastorm and it worked as expected. Cool.

The next test was to see what happened when I restarted the engine whilst a workflow was executing, since this caused problems with sequential workflows. I was expecting the same problem, since state machine workflows execute in the same way as sequential workflows. And I was correct in my supposition, the workflow never completed.

So in conclusion, state machines can be used in Metastorm but you'll need to hack the database. But WF in Metastorm won't be of use to you if you need to execute long running workflows. Which is a shame since this is probably the main purpose of WF.

Part 1 - The basics

Part 2 - The database tables

Part 3 - Using Visual Studio

Part 4 - Using your own activities

Part 5 - Long running workflows

Saturday, October 25, 2008

Oracle on Windows - the most painful application ever

Oracle

I'd been putting it off for as long as possible but I finally decided it was time for me to get the FreeFlow Administrator supporting Oracle as well SQL Server. Why did I put it off for so long? Because Oracle on Windows is horrible. I've never used it on Unix but I  assume it must be better than the Windows version, or how have Oracle survived so long? 

SQL Server just works, install it and you've got the database, management tools and drivers installed no problem. Try the same with Oracle and the first problem you've got is that the installer is some weird proprietary Java thing that doesn't behave like a normal Windows installer. Then you have to try and install ODBC and OLE DB drivers. My first installation attempt managed to destroy my database, the second time, after many Google searches, actually worked.

Another nice feature of the Oracle drivers for Windows is they seem to provide new versions every week that fix some bugs and introduce some other bugs, which all adds to the excitement. And how about this, an OLE DB driver is a 200MB download, WTF?

To be fair, some things have improved since my last foray into Oracle development. They now have a free version of their database, Oracle XE, which actually uses a proper MSI installer.  They have a new admin tool, SQL Developer, which does make life easier. Of course it's Java based so the UI is dreadful and there doesn't seem to be a way to create a table space which seems like the first thing you'd want to do but it is a step in the right direction.

So what's the point of this post? None really, except I finally got it all working and I'm feeling quite pleased with myself. Perhaps that's why people use Oracle, because they feel like they've actually achieved something after just installing the damn thing.

Tuesday, October 14, 2008

Do you really need to use a CAPTCHA?

I am completely lazy so I've put up with the spam coming to me from the Random Pub Finder for a long while now. Eventually I realised the signal to noise ratio was so low that I was actually missing real people submitting pub suggestions so I decided I had to do something. Again due to my laziness my first attempt at fixing the problem was just to block submissions from the common IP addresses used by the bots trying to submit things. This helped but still stuff was getting through.

So I started to think about implementing a CAPTCHA system, but even though there seems to be at least one good PHP implementation out there, laziness again got the better of me. I remembered Coding Horror which has used a sort of CAPTCHA system for several years but the difference is that the image is always the same. And has been as long as I remember. So I must assume that it is reasonably effective, given it's a site that gets a lot of hits, so I thought I'd try out the same system on the Random Pub Finder, since it's dead simple to implement.

I've only just implemented it and it's working 100% so far. If it continues to work, then it suggests all these spammy form submissions are completed automated and there is no human behind them at all. Which then leads to the conclusion that a full-blown CAPTCHA is probably not necessary for most sites.

Monday, October 13, 2008

Recursively copy a folder in C#

Perhaps I need to RTFM but I couldn't find any methods in the .NET Framework to copy a folder and its contents to another location. So this is my attempt. I haven't thoroughly tested it but it should be a reasonable starting point and worked for my single test case.

    private void CopyFolder(string sourceFolder, string outputFolder)
    {
      System.IO.Directory.CreateDirectory(outputFolder);

      string[] files = Directory.GetFiles(sourceFolder);
      foreach (string file in files)
      {
        File.Copy(file, Path.Combine(outputFolder, Path.GetFileName(file)), true);
      }

      string[] folders = Directory.GetDirectories(sourceFolder);
      foreach (string folder in folders)
      {
        string[] splitFolders = folder.Split('\\');
        string folderName = splitFolders[splitFolders.Length - 1];
        CopyFolder(folder, Path.Combine(outputFolder, folderName)); 
      }
    }

Thursday, October 09, 2008

Finding table and column references in SQL Server stored procedures

I keep needing to go off and search for this, so as a note to myself, here's the required SQL to find out where tables and columns are referenced in SQL Server stored procedures.

select * from information_schema.routines where routine_definition like '%tablename%'

I believe this only searches the first 4000 characters of the stored procedure, but this has generally been enough for my needs.

Tuesday, October 07, 2008

The risk premium

It looks like several hundred thousand people in the UK have discovered the meaning of the risk premium today. Whilst it's difficult not to have sympathy for all these people who have potentially lost money, it may help everybody else realise that no place is a completely safe haven for your savings and not all banks are made equal. So how to tell if a bank could be in difficulties? Well, firstly look at the rates they are prepared to pay to get hold of your savings. Banks don't offer higher rates because they are generous people who want to make you happy, it is fairly likely they are after your money because they need it because they are short of capital, which suggests they are more prone to fail. And that is the risk premium, you get a better interest rate because you're more likely to lose your money.

Another thing to look at is CDS spreads. Unfortunately these are difficult to get hold of unless you happen to have access to a Bloomberg terminal. Here's a list from about three weeks ago, a higher number means the market considers the bank to be higher risk. And as you can see, quite a few of the banks near the top of the list have already got into difficulty. It was this list (along with the failure of Glitnir) that made me pull my money out of Icesave (Landsbanski) a week ago. Phew.

Kazkommerts 1040
Glitnir Bank 950
Kaupthing 850
IKB 625
Wachovia Corp 572
VTB Bank 570
Landsbanki 550
Anglo Irish 485
Banca Italease 462
Capital One Bank 424
ICICI 405
HBOS 327
Bank of Ireland 310
Allied Irish Banks 268
Citigroup Inc 264
Nationwide 230
Bank of America 201
Standard Chartered Bank 199
Natixis 192
JPMorgan Chase 191
Wells Fargo 188
UBS 183
Barclays 169
Erste 166.7
RBS 162
Raiffeisen 150.0
Fortis Bank 149
Danske Bank 138
ING 133
Credit Agricole 127
Credit Suisse 124
Lloyds 123
Societe Gen 122
ABN Amro 119
Dresdner 118
Santander 117
Deutsche 114
Commerzbank 111
Standard Life Bank Ltd 108
Unicredit 95.2
HSBC 94
Nordea Bank 91
BNP 73
Svenska Handelsbanken 72.3

Wednesday, October 01, 2008

Stuart's Stories

I reviewed Stuart Maconie's book about the North some time ago and now Visit England's North West are offering a free book called "Stuart's Stories", which contains some of his writings about the North West. I'm not sure if they are straight from the book or if this is new content, but either way it can't be bad for free can it?

Sunday, September 28, 2008

Metastorm BPM 7.6 and Windows Workflow part 5 - Long running workflows

OK, I want to go back to basics for this post. What is the point of Windows Workflow? Here's the first paragraph of the preface to "Essential Windows Workflow Foundation"

"Windows Workflow Foundation is a general-purpose programming framework for creating reactive programs that act in response to stimulus from external entities. The basic characteristics of reactive programs is that they pause during their execution, for unknown amounts of time, awaiting input."

I've highlighted what I consider to be the important point here. A WF workflow may lay dormant for minutes, days or weeks. In the normal world of .NET code, implementing this kind of application can be troublesome. For a start, the application may shutdown and restart before the workflow resumes, so the application needs to cope with this possibility. This means the objects must be persisted and restarted with the correct state. A simple solution like making the executing thread sleep for the required amount of time isn't going to work, in terms of robustness or scalability (imagine 1000s of these workflows being executed at the same time).

So WF helps solves this problem. Without this feature, WF is really just a different approach to producing standard .NET code, either through the designer or generating declarative XML documents to explain how the code should execute. It might well be useful in some scenarios but it isn't really a killer reason to use the technology.

BPM WF

So with that in mind I created a very simple workflow to execute from Metastorm BPM, which you can see on the left. All it does is write to a text file, wait for ten minutes, then write to the text file again.

The first thing to ensure is to set the "Microsoft Workflow timeout (secs)" registry setting to something sensible. Unfortunately setting this to zero doesn't mean no timeout, it just uses the default 120 seconds timeout. So this needs setting to something very large. This is a DWORD value with a maximum value of 4294967295, which I calculate is approximately 49710 days, that hopefully is large enough for most workflows.

Executing the workflow worked fine in this scenario. The workflow idled for 10 minutes then resumed and completed.

So the next experiment was to start the workflow, shut down the engine and then restart the engine and see what happened. I waited... and waited... Nothing. I started another workflow just in case the workflow runtime hadn't been kicked off but still nothing. So there's a bit of a problem there, particularly if you want to run long running workflows.

As an aside, if you're running your workflow asynchronously then you won't be able to use most of the activities provided for interaction with Metastorm BPM. This is a shame. In particular the RaiseFlag activity would be exceedingly useful since it could be used to signal to the parent process that the workflow had completed. As it is, you'll need to do this with the ECL activities provided, which are more complicated to configure or write your own code.

Part 1 - The basics

Part 2 - The database tables

Part 3 - Using Visual Studio

Part 4 - Using your own activities

Part 6 - State machines

Wednesday, September 24, 2008

Autosizing row heights in a WinForms DataGrid

Autosizing column widths in a WinForms DataGrid is pretty easy, if a little hacky. Although it seems that a similar technique could be used to autosize row heights, the private RowAutoResize method doesn't do what I was expecting it to do. I found some useful code here which I have adapted somewhat so that it can handle DataGrids with IList based classes as their data source. It also looks at all cells to work out the required height.

    public void AutosizeRows()
    {
      int numRows = 0;
      if (DataSource is DataTable)
      {
        numRows = ((DataTable)DataSource).Rows.Count;
      }
      else if (DataSource is IList)
      {
        CurrencyManager manager = (CurrencyManager)BindingContext[DataSource];
        numRows = manager.List.Count;
      }

      using (Graphics g = Graphics.FromHwnd(Handle))
      {
        StringFormat sf = new StringFormat(StringFormat.GenericTypographic);

        // Since DataGridRows[] is not exposed directly by the DataGrid  
        // we use reflection to hack internally to it..
        MethodInfo mi = GetType().GetMethod("get_DataGridRows",
          BindingFlags.Instance | BindingFlags.NonPublic);

        Array dgra = (Array)mi.Invoke(this, null);

        // Convert this to an ArrayList, little bit easier to deal with 
        // that way, plus we can strip out the newrow row. 
        List<object> DataGridRows = new List<object>();
        foreach (object dgrr in dgra)
        {
          if (dgrr.ToString().EndsWith("DataGridRelationshipRow"))
            DataGridRows.Add(dgrr);
        }

        int colCount = TableStyles[0].GridColumnStyles.Count;

        // Now loop through all the rows in the grid 
        for (int i = 0; i < numRows; ++i)
        {
          int maxHeight = 0;

          for (int j = 0; j < colCount; j++)
          {
            SizeF size = g.MeasureString(this[i, j].ToString(), Font, 400, sf);
            int h = Convert.ToInt32(size.Height);

            // Little extra cellpadding space 
            h = h + 8;

            maxHeight = Math.Max(h, maxHeight);
          }

          // Now we pick that row out of the DataGridRows[] Array  
          // that we have and set it's Height property to what we 
          // think it should be. 
          PropertyInfo pi = DataGridRows[i].GetType().GetProperty("Height");
          pi.SetValue(DataGridRows[i], maxHeight, null);
        }
      }
    }

Sunday, September 21, 2008

Capital boozing - the Random Pub Finder book

It used to be called vanity publishing. An aspiring author, having been rejected by all the standard publishers, would decide the problem wasn't with themselves and their work, but with the publishers who were clearly blind to the author's genius. So the only solution was to go out and get their work published themselves and sell hundreds of thousands of copies that way. Of course what generally happened was that, after paying out thousands to the vanity publisher, the author would be left with 2472 copies of their masterpiece sat in the loft, after giving out the other 28 copies to friends and family.

Things have changed, we can now all be published authors via the wonderful lulu.com. The beauty of this system is that a forest doesn't get destroyed to sate the author's vanity. Books are only produced if somebody is actually interested in them. And because they are published on demand, books can be updated and corrected quickly and easily.

So to take advantage of this system, we've taken the reviews from the Random Pub Finder and put them all together in one convenient book shaped package. I don't know about you but even with all the latest technologies for reading on a screen, I still much prefer a book. No power problems, no waiting to boot up, no random crashes.

Anyway, if you want to buy a copy, hop over to http://www.lulu.com/content/2003276. I'm still waiting for a design for the cover, but other than that it's all done.

Saturday, September 20, 2008

Turn it up to 11

11I've just noticed the volume on the BBC media player on their website goes up to 11. I assume the developer of this code is an afficionado of Spinal Tap.

I can't say I've noticed the difference in sound levels between this and other web based apps...

Tuesday, September 16, 2008

Metastorm BPM 7.6 and Windows Workflow part 4 - Using your own activities

My previous posts on Metastorm/WF integration have had a few comments, so I thought I'd follow up on them.

First up, it is possible to reference your own assemblies containing custom activities in the WF Composer, via the Options dialog that can be found on the main menu. For me this means the Composer could potentially be used by non-technical people, although the workflow designer may still be too difficult to hand over to non-techies (this isn't Metastorm's fault BTW, the design surface is all Microsoft's work).

But before I could check out this feature, I had a problem using the Composer. I haven't used it for a few weeks or months and when I tried to do almost anything I got the following error

Could not load file or assembly 'mscorcfg, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified. (mscorlib) 

I suspect this is down to me uninstalling Visual Studio 2005 since I installed the WF Composer which I guess uninstalled the .NET 2.0 SDK which is required by the WF Composer. It's a shame the SDK is required, as it's a 354MB download... If it's just for the one assembly then it's flipping ridiculous.

Anyway, after installing the SDK and reinstalling the Metastorm WF bits, things started working again (the re-install may not be necessary but I uninstalled before I realised what my problem was).

OK, so I thought I'd try to use some of my own activities. My first problem was that any assembly I tried to reference produced the following error

No toolbox items found in assembly!

This was easily solved by adding the following attribute to my activity class definition

[ToolboxItem(true)]

My activities now appear in the toolbox but I'm unable to drag them onto the design surface. I guess I'm missing something in my activity class code but I don't know what. They work in Visual Studio and my own designer so I'm not sure why they fail here. Anyway, although the WF Composer is of passing interest, my main interest is using workflows authored in Visual Studio, so I'm not going to look at it too closely.

Part 1 - The basics

Part 2 - The database tables

Part 3 - Using Visual Studio

Part 5 - Long running workflows

Part 6 - State machines

Thursday, September 11, 2008

Complete rewrite - two years on

It's almost two years on since I wrote about one of my previous employers starting a complete rewrite of one of the core pieces of the their application suite. So how have things turned out? To tell you the truth I have no idea, nobody from the company will give me any useful information these days. But one thing that I am sure of, the new software hasn't been released yet. So my back of a fag packet estimate of three and half years development time isn't looking quite as daft as it may have done at the time.

Having said that, it is of course possible that the software is done and the company are just waiting for the right time to release it. I remember being blown away when I discovered that Ultimate Play The Game actually held back the release of the ground-breaking Knight Lore game so they could release the less impressive Sabre Wulf without it looking rubbish.

And if you do know anything about the progress of the software of which I'm talking then post a comment, you can do it anonymously...

Tuesday, September 09, 2008

Noel Gallagher pushed off stage

I have a theory why the North West has produced more than its fair share of great popular music over the past few years (The Beatles, Joy Division, New Order, The Smiths, Happy Mondays, British Sea Power (although Brighton try to claim them as their own), Elbow, The Fall, The Stone Roses to name but a few). It's the weather. Rain, rain and more rain. People are forced to stay in quite a lot and do something. So a lot of them decide to try their hand at music and some of them succeed. But I would never include Oasis on that list. Although their initial material was catchy, it never really reached the emotional heights that great pop music does. And their output afterwards has been absolutely awful. To be fair, "Wonderwall" could have been a beautiful song had it not been for the vocals of Liam.

Anyway, I'd pretty much forgotten about Oasis, but apparently they are still chugging along with their Beatles parodies. And now we have this wonderful video of Noel being pushed off stage by somebody. Although that's slightly humorous, the bit I love is self-styled tough guy Liam. First he tries to get away from the stage invader, then when the guy has been held down by two or three security guys, he suddenly remembers how hard he really is and walks across to "have a go" at him. I think it really shows how much of a show the whole tough guy image is...

Wednesday, September 03, 2008

How to kill the hits to your website

I went on holiday a few weeks ago. Unbeknownst to me the Random Pub Finder's database died the day I went away. With no web access I only found out on my return. A few days of emails to our ISP followed (yes we should have had a backup but we didn't; fortunately our ISP did have) and finally the site was back online. A few days after that I went off to Switzerland and what do you know, the database died again. A more paranoid person than myself might think somebody knew I was going away and did it deliberately. I'm not quite that paranoid but am obviously paranoid enough to consider it as a possibility. After the first data loss I'd done a backup, but I didn't have access to it whilst I was out of my office. Anyway, upon my return, the site was restored pretty quickly. So in all the Random Pub finder was offline for about three weeks.

Prior to the database crash, we were averaging about 300-400 visitors a day, now we are barely reaching 100. I guess the broken pages are still in the search engines' caches and who knows if and when we'll get our visitors back...

Friday, August 29, 2008

The wonders of e-ticketing

If you go to Rail Europe and purchase a ticket, chances are you won't actually be sent real tickets. Instead you'll be sent an itinerary with some reference numbers. Then when you get to the station, you go to a machine and pick up the real tickets. So I was trying to get to Basel In Switzerland and chose the train since I had too much baggage to go on the plane and also felt like going on the train was the right thing, environmentally, to do.

So I got to St Pancras and started typing in my reference numbers. My Eurostar tickets popped out but I was unable to get the tickets for the rest of the journey. I thought perhaps this was because I needed to pick them up from the station in Paris so didn't worry too much.

I arrived in Paris, dragged my stuff to Gare d'Est and tried to type in my reference numbers again and got nowhere. The touch screen computer helpfully had an English option with a button so small that it was almost impossible to hit the screen in the correct place to get it working. Nice touch, SNCF. So I started queuing up in the ticket office but time was running out, so I headed for the train. After all, I had an itinerary that included my seat number, so it was obvious that it was my seat. Unfortunately, the ticket inspector didn't see it that way. I didn't have a ticket. Yes, the train was fully booked and nobody was demanding to sit in my seat but I didn't have a ticket. With the man next to me translating, I tried to argue my case, but got nowhere and ended up paying for my ticket again. Ho hum. The ticket inspector did assure me I could get my money back if I went to the ticket office when I got off.

But at least I managed to get to Basel. So the next thing to do was get my ticket refund and get my ticket for the return leg of my journey. Unfortunately the ticket office in Basel informed me I was now in the wrong country to pick up my tickets for the return journey and would have to go to France. Fortunately Basel is only eight minutes from the border so this wasn't too time consuming or expensive. When I got to St Louis, the lady in the ticket office was very nice and her English wasn't too good (although clearly much better than my French) so after getting my return ticket I thought better of trying to explain why she needed to give me some money due to her jobs worth co-worker.

So lesson learned, trains may be more environmentally friendly but train companies don't seem to be in any rush to actually compete with airlines. They cost more, take longer and make life difficult for their customers. To solve one of these problems, here's a suggestion for you, how about sending the tickets along with the itinerary and then I might be tempted to use the train again in Europe?

Monday, August 18, 2008

Babysitting laptops

PCs are so fecking demanding. I've got a few laptops at home ready for some training I'll be doing and every time I switch one of them on I'm assaulted by a bunch of popups telling me I need to update this or that, Windows Update, Java, Macafee, some other crapware installed with Dell. After half an hour of updates, I can finally get round to doing whatever I need to do, which I've forgotten about by now of course.

So how can this be solved? Here's some suggestions

Windows Update - OK, this is probably a necessary evil, although I'm dubious about some of the updates pushed my way. Are they all really necessary?

Macafee - I haven't run a scan for two weeks because the laptop hasn't been switched on at all, I'm not likely to have acquired a virus in that time am I?

Sun Java - Do you really have to keep bugging me to install the Google Toolbar? And what the feck has that got do with Java anyway?

Dell - Stop installing all this crap on your PCs. Offer an option of installing a clean OS, even if it costs a little more. The problem is that all that extra crap you install hits performance and half of it is badly written trash so people start to think Windows is crap and go off and buy a Mac, even though it's actually Dell that's the problem. The irony is that the thing that made PCs successful, open hardware meaning anybody can build them and install whatever they feel like, is the thing that may well kill them off, replaced by the closed Mac...

The Coming Collapse of the Middle Class

This video may be about the US but I'm guessing it also applies to the UK. We may think we are richer than previous generations, but in fact we are spending more on housing, healthcare and have more debt, even though we generally have two parents in work. The problem with having two parents in work is we have no slack in the system. If one parent falls ill or loses their job, we pretty quickly run out of money, since the other parent can't go out to earn extra, they are already in the workforce.

Sunday, August 03, 2008

Connecting two PCs with a network cable

Wireless networks are great for home networks, unless you need to copy large amounts of data around. OK, 802.11g is probably pretty fast, but I've got 802.11b here and when I tried to copy 4Gb of data around, things got a bit slow. I had no blank DVDs and my USB drive is only 1Gb so I had to figure out another way to get the data from one machine to the other. So I figured I'd network them the old way, with wires. And here, mostly so I remember how to do it next time, is what's required. This may not all be required but I'm a software man, not hardware, and you know how many software engineers it takes to change a light-bulb right? But given that most of the information I've found on t'internet seems to be "use a cross-over cable", it might be helpful to someone.

First, make sure the two computers are part of the same workgroup. Right click on 'My Computer' and view the computer's properties to see what workgroup the PCs are a member of. If they are on a domain, I guess they need to part of the same domain.

Next, the cable needs to be a cross-over cable. I think I, er, borrowed my cross-over cable from a former employer but they are available from all good computer equipment shops.

Next, make sure TCP/IP is configured to use fixed IP addresses. More often than not TCP/IP is set up to obtain an IP address automatically since they'll get assigned by your ISP or router or some little gremlin who sits in a box on the internet somewhere - look I dunnow how it works. Here your TCP/IP connection is just between two computers, so there is no little gremlin to assign IP addresses. So look at the properties of your network connection and from there look at the properties of the TCP/IP stack. I think you can pretty much choose whatever IP address you like, so long as the two PCs have different IP addresses. I chose 100.100.100.101 and 100.100.100.102 and that worked fine.

Finally, disable your wireless connection on one PC at least. This may well make no difference at all, but it seemed like a good thing to do. If your PC can see another computer via two network connections, how does it choose which connection to use when copying files? Again I don't know, so figured disabling the slow wireless connection would be a good idea.

Friday, August 01, 2008

SonicWall VPN client and wireless networks

I've had this problem for a while. I have a couple of computers connected on my home wireless network which generally can see each other to share files. But sometimes I'm unable to connect to the other machine. I'd always put this down to some weirdness going on in my wireless network and dealt with it by copying files onto a USB drive. But I finally figured it out today. It would appear if I have the SonicWall VPN client running on one of the machines this causes the wireless network to go funny. Shutting SonicWall down fixes the issue.  No idea if this is a bug or if it is required behaviour but now I know why it happens I don't really care.