Buyers Beware

In the build versus buy debate, which has many different considerations for software development companies, I generally lean towards the buy option. The cost to develop, support and maintain your own component code is often far greater than buying the component if available. Purchasing the code for a complete application with the intent to extend has completely different arguments and implications.

One of the first tasks was to write a new installer. The reason for the new installer was to implement it using WiX so that we can have full control over the installation and bring it in line with the rest of our application installers. I have built many installers and once I had the information I needed, this task went smoothly. I will talk more about installation creation with WiX soon.

This particular application is an Excel Add-in, written in VB.NET. One of the issues that was raised has been is the poor performance of the add-in. Doing a refresh from the data source takes almost 10 seconds. Some VBA code was written by the consultant that raised the issue to attempt to determine if the bottleneck was with the add-in or the data source. The VBA code returned the same data set almost instantaneously. Of course, the add-in performs more on a refresh than just what the VBA code did, but difference is unacceptable.

It was time to dive into the code. Running performance analysis over the application quickly pointed to the violating method. Unfortunately, the method was over 700 functional lines long! Before I could make performance improvements, I needed to understand what the code was doing. Before I could understand what the code was doing, I needed to refactor the method into manageable-sized methods. With the wide scope and reuse of variables, and the lack of unit tests, this became a problematic task. Performing a complexity analysis over the method, gave it the lowest maintainability index possible. However today, it has ended well, with the refresh now taking under 2 seconds!

In following posts, I will be talking about the Installation Creation, Refactoring and Performance Improvements undertaken on this code.

Parse TimeSpan String

On one of my projects on the side, I had the need to for a user to enter a time estimate. The Parse method from the TimeSpan object is limited in usefulness to convert a string to a TimeSpan due to the strict requirements of the string format. From the documentation:

The s parameter contains a time interval specification of the form:

[ws][-]{ d | [d.]hh:mm[:ss[.ff]] }[ws]

Items in square brackets ([ and ]) are optional; one selection from the list of alternatives enclosed in braces ({ and }) and separated by vertical bars (|) is required; colons and periods (: and .) are literal characters and required; other items are as follows.

Item

Description

ws

optional white space

"-"

optional minus sign indicating a negative TimeSpan

d

days, ranging from 0 to 10675199

hh

hours, ranging from 0 to 23

mm

minutes, ranging from 0 to 59

ss

optional seconds, ranging from 0 to 59

ff

optional fractional seconds, consisting of 1 to 7 decimal digits

The components of s must collectively specify a time interval greater than or equal to MinValue and less than or equal to MaxValue.

This is fine, but not easy to train a user to use. What I require is a user to be able to enter an estimated time, in a simple free form way that makes sense to them. I would like automatic conversion between units, so that if the user enters 180 minutes, it is parsed to 3 hours. I would like to be able configure whether 1 day is equal to 24 hours or an 8 hour work day and configure what the default unit is, if none is specified by the user. Input should be of the format:

\s*(?<quantity>\d+)\s*(?<unit>((d(ays?)?)|(h((ours?)|(rs?))?)|(m((inutes?)|(ins?))?)|(s((econds?)|(ecs?))?)|\Z))+

Using values (removing milliseconds) from the TimeSpan Parse examples:

String to Parse

TimeSpan

0

00:00:00

1h2m3s

01:02:03

180mins

03:00:00

10 days 20 hours 30 minutes 40 seconds

10.20:30:40

99 d 23 h 59 m 59 s

99.23:59:59

23hrs59mins59secs

23:59:59

24 hours

1.00:00:00

60 min

01:00:00

60 sec

00:01:00

10

10:00:00 (if hours is default unit)

If .NET 3.5 Extension Methods supported static extension methods I would add the method public static TimeSpan ParseFreeForm(static TimeSpan timeSpan, string s) to the TimeSpan class. This would allow a TimeSpan.ParseFreeForm to be seen in the intellisense next to the inbuilt Parse method, which I think is a logical place with higher visibility than a utility class. There is obviously arguments for and against this, but I’m not going to get into that now. Since extension methods only allow new instance methods it does not make sense to create a new instance of a TimeSpan to parse a string to return a new TimeSpan. Therefore I created the utility method:

public static TimeSpan ParseTimeSpan(string s)
{
    const string Quantity = "quantity";
    const string Unit = "unit";

    const string Days = @"(d(ays?)?)";
    const string Hours = @"(h((ours?)|(rs?))?)";
    const string Minutes = @"(m((inutes?)|(ins?))?)";
    const string Seconds = @"(s((econds?)|(ecs?))?)";

    Regex timeSpanRegex = new Regex(
        string.Format(@"\s*(?<{0}>\d+)\s*(?<{1}>({2}|{3}|{4}|{5}|\Z))",
                      Quantity, Unit, Days, Hours, Minutes, Seconds), 
                      RegexOptions.IgnoreCase);
    MatchCollection matches = timeSpanRegex.Matches(s);

    TimeSpan ts = new TimeSpan();
    foreach (Match match in matches)
    {
        if (Regex.IsMatch(match.Groups[Unit].Value, @"\A" + Days))
        {
            ts = ts.Add(TimeSpan.FromDays(double.Parse(match.Groups[Quantity].Value)));
        }
        else if (Regex.IsMatch(match.Groups[Unit].Value, Hours))
        {
            ts = ts.Add(TimeSpan.FromHours(double.Parse(match.Groups[Quantity].Value)));
        }
        else if (Regex.IsMatch(match.Groups[Unit].Value, Minutes))
        {
            ts = ts.Add(TimeSpan.FromMinutes(double.Parse(match.Groups[Quantity].Value)));
        }
        else if (Regex.IsMatch(match.Groups[Unit].Value, Seconds))
        {
            ts = ts.Add(TimeSpan.FromSeconds(double.Parse(match.Groups[Quantity].Value)));
        }
        else
        {
            // Quantity given but no unit, default to Hours
            ts = ts.Add(TimeSpan.FromHours(double.Parse(match.Groups[Quantity].Value)));
        }
    }
    return ts;
}

To modify the hours in a day, when a match is made on the Day unit TimeSpan.FromHours(Quantity * hoursInDay) is all that is required. The value hoursInDay could be passed as a parameter or set as an Application Configuration value. The structure of this solution also provides the ability to easily extend for other units, such as weeks.

Vista Sidebar Gadget Competition

Recently I entered a Gadget Competition hosted by MSOZACADEMIC and Si-Mi. The competition was announced on the MSOZACADEMIC blog and on the MSDN Flash. I do subscribe to the Flash but I did not see this competition during my skim of the newsletter. Fortunately, my work mate highlighted this for me. I thought it would be a good way for me to try out Sidebar Gadget programming and motivation to actually finish a home project. The competition closed at 23.59pm 31st October 2007 and to my surprise I had completely finished and upload my gadget  a whole 4 days ahead of the closing date. To my delight my Auto Lock gadget was name Judges Choice. In the post Nick Ellery mentioned that he had seen a tutorial on this before. My prizes arrived today, so I thought it was about time to detail how I put together the gadget.

The tutorial that I was inspired by was on the MSDN Coding4Fun blog. The post was Bluetooth Screen Lock and demonstrated the use of the managed wrappers for the Coding4Fun Developer Toolkit which was still in Beta. This gave me a huge base to start off. I pulled open the toolkit and extracted just the relevant Bluetooth communications wrapper. Guided by the Bluetooth Screen Lock code, I then built a single .NET DLL exposing just the functions required to communicated to Bluetooth devices and Lock the computer. Then came what I thought would be the simpler task. Build an interface as a Vista Sidebar gadget and call the functions. Building the interface was fine, but calling the functions wasn’t so straight forward. Two steps are required to make this happen.

First, the DLL must be made COM visible. This enables the use of the DLL as an ActiveX object and is easily used from JavaScript in the gadget. This part is simply done by adding the ProgId attribute to your class which contains the public methods.

[ProgId("AutoLockActiveX.AutoLock")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class AutoLock

Second, the DLL must be registered before it can be used. This can be done by using regasm, but being a Vista Gadget, there is no installation to do this at it is just an unzipping of the files to the gadget directory. After a searching for a solution I came across a post by Jonathan Abbott, Using ActiveX DLL’s in Gadgets. This very nicely explains what needs to be done to automatically to register a DLL on the gadget start up, and also be a good citizen and remove the registration when closing the gadget. I cleaned up the code a bit, removing global variables and contained it all into it’s own file, RegisterActiveX.js. One thing the code was missing was the declaration of oShell, which is, var oShell = new ActiveXObject("WScript.Shell");. With further refactoring this could be made into a object oriented JavaScript file. Armed with RegisterActiveX.js I can easily write any .NET code for use within a Sidebar Gadget.

A little bit more tinkering and the JavaScript for the program control is put together seamlessly accessing the .NET DLL from a Vista Sidebar Gadget.

Now to go play with the new toys.

Technorati Tags: ,,