September 6, 2008

Single instance Calculator.NET

Everyone has their set of utility programs that they need to be productive. I've got lots of them. Whenever I change computers or reinstall windows I've got a list of about 10-15 essential apps that needs to be installed before I do anything else. One of these programs is Calculator.NET by Paul Welter

It's one of those small apps that does exactly what you expect of it and nothing more. It's got a clean nice history and some built in conversion utilities but other that that it's just a calculator. It has a built in option (menu item toggle) for setting it to be the default calculator for windows. Simple, and without any hassle.

There's one tiny thing that bothers me though. At work I've got a MS Wireless Desktop 7000 keyboard with one of those neat calculator shortcut buttons right above the numpad and whenever I press it a new instance of Calculator.NET starts up. Ideally though I'd like for my existing Calculator.NET instance (if any) to be focused and restored (if minimized).

Open source to the rescue

Paul was kind enough to provide the source to Calculator.NET and being a curious developer I loaded it up to see if I perhaps could add my little feature myself instead of bugging Paul with a feature request. About half an hour later I was done.

Enter Calculator.NET single instance patch

Calculator.NET with single instance patch

It ensures that only one instance of the calculator is active at any one time but you can override that behavior by passing -force as the first argument or by pressing CTRL+N or by just clicking on the new window icon in the toolbar/menu.

And here is the code

It uses a named mutex to ensure that only one instance can run at any given moment.

static void Main(string[] args)
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    string optStr = string.Empty;

    if(args.Length > 0)
        optStr = args[0].Trim().ToLower();

    if (optStr != "-force" && Settings.Default["SingleInstanceMode"] != null &&  Settings.Default.SingleInstanceMode)
    {
        using (Mutex m = new Mutex(false, "Calulator.NET single instance"))
        {
            if (!m.WaitOne(0, false))
            {
                // There is already an instance of Calculator.NET running.
                // Attempt to locate the window and bring it to front
                IntPtr hWnd = FindWindow(null, "Calculator.NET");

                if (hWnd.ToInt32() != 0)
                {
                    ShowWindow(hWnd, SW_RESTORE);
                    SetForegroundWindow(hWnd);
                }
            }
            else
            {
                RunApp();
            }
        }
    }
    else
    {
        RunApp();
    }
}

private static void RunApp()
{
    Application.Run(new CalculatorForm());
}

const int SW_RESTORE = 9;

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

If you want to apply it to your build begin by downloading the source to Calculator.NET and then go get my patch Calculator.NET-singleinstance-patch.zip.

Just unpack the Calculator.NET zip file and then copy the contents of my zip into that directory and you're good to go. I'll drop a comment over at Paul's weblog and then we'll see if it's something he'd consider adding to the official version.

If you don't want the build hassle you can simply download the binary Calculator.NET-singleinstance.exe

Licensing information

kick it on DotNetKicks.com