C# WPF Tutorial - Writing a Single Instance Application [Intermediate]


Today we are going to be taking a look at how to build a single instance application in WPF. Not a single instance in this sense, but in the sense that you can only run one instance of the application at a time. Generally, you can run as many instances of an app at once (at least until you run out of resources). For instance, Notepad. You can run Notepad a dozen times, and you will get a dozen separate Notepad windows, and a dozen separate lines in "Task Manager" that read "notepad.exe". Killing one of those lines just kills one of those Notepad windows, and the rest live on happily.

On the other hand, you have an application like Firefox. At any given time, there should only be one line in Task Manager that reads "firefox.exe". This is because every time you hit the Firefox shortcut, or double click the executable, instead of running a new instance of the app, the running instance gets a message (which is how Firefox knows to open a new browser window).

So why would you as a developer write an app that behaved in this way? The most common reason has to do with resources - your application needs an exclusive lock on some resource. That resource could be anything from a hardware device to a file on disk. But if your app needs an exclusive lock, you better not let other instances run, because those other instances will fail.

Ok, so how do we do this in .NET (and, more specifically, WPF)? It actually isn't that bad (.NET fortunately has a useful built-in class that we get to use), but it does take some drastic changes to the default structure of a WPF application.

To get started, go and create a new WPF Visual Studio project. By default, it comes up with two main items in the solution - "Window1.xaml" (which I renamed to ExampleWindow.xaml") and "App.xaml". Both of these also have their respective code behind files. So first off, do something you have probably never done before - delete "App.xaml" and "App.xaml.cs". We won't be needing them, because we will be doing our own Application creation.

Now create a new class (I called it "ExampleApplcation"). This will be our application. The two main pieces of logic that this class needs to have are for showing the main window and for processing command line arguments. The first piece will only happen once - the initial application start up. The second, however, will happen every time a user tries to run the app (and we will see how that works in a moment). Take a look at the code:
using System.Windows;

namespace SingleInstanceExample
{
  public class ExampleApplication : Application
  {
    public ExampleWindow MyWindow { get; private set; }

    public ExampleApplication()
      : base()
    { }

    protected override void OnStartup(StartupEventArgs e)
    {
      base.OnStartup(e);

      MyWindow = new ExampleWindow();
      ProcessArgs(e.Args, true);

      MyWindow.Show();
    }

    public void ProcessArgs(string[] args, bool firstInstance)
    {
      //Process Command Line Arguments Here
    }
  }
}
 
OnStartup will only get called once - the very initial application startup. So it is here that we make a new window and show it. We also process the command line arguments, and add a flag saying that these are the arguments to the first instance. We will be calling ProcessArgs from somewhere else when the user tries to start other instances of the app.

Ok, so that code would get a window off the ground - but what calls that code? Well, for that, we need another class (and this class will actually hold the entry point for the application). The important thing for this class is that it has to derive from WindowsFormsApplicationBase. To get that class, you actually need to add a special reference to your Visual Studio project - "Microsoft.VisualBasic.dll". Don't ask me why this class is stuck in that dll - that is just where it happens to be.
using System;
using System.Linq;
using Microsoft.VisualBasic.ApplicationServices;

namespace SingleInstanceExample
{
  public sealed class SingleInstanceManager : WindowsFormsApplicationBase
  {
    [STAThread]
    public static void Main(string[] args)
    { (new SingleInstanceManager()).Run(args); }

    public SingleInstanceManager()
    { IsSingleInstance = true; }

    public ExampleApplication App { get; private set; }

    protected override bool OnStartup(StartupEventArgs e)
    {
      App = new ExampleApplication();
      App.Run();
      return false;
    }

    protected override void OnStartupNextInstance(
      StartupNextInstanceEventArgs eventArgs)
    {
      base.OnStartupNextInstance(eventArgs);
      App.MyWindow.Activate();
      App.ProcessArgs(eventArgs.CommandLine.ToArray(), false);
    }
  }
}
 
It is in this class that all the magic happens. Every time the application is run, it enters the Main method, creates a new instance of this class, and calls Run. If it is the first instance, this will cause OnStartup to get called, and everything goes from there. If it is a not the first, OnStartupNextInstance gets called on the already running instance, and the instance that was just started shuts down.

It really is as simple as that. The command line arguments for subsequent instances are even right there in the handy StartupNextInstanceEventArgs.

By default, this will only work for multiple instances of the exact same build of an application. If you need this to work across multiple builds, there is one more step you have to take. In the AssemblyInfo.cs file of your project (generally under the "Properties" folder") you have to add a GUID for your assembly. This GUID is what will be checked against when Windows sees if it is allowed to start another instance of the app. When there is no GUID explicitly set, Visual Studio generates a new one every time you build (which is why without this change, the single instance manager will only work for other instances of the exact same build). You will want your AssemblyInfo.cs to look something like this (of course, you want to use your own GUID):
using System.Reflection;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("SingleInstanceExample")]
[assembly: AssemblyProduct("SingleInstanceExample")]
[assembly: GuidAttribute("1A6236B4-8CD1-4c76-86FD-F5352330D190")]
 
That is it for writing a single instance application in .NET and WPF. The code for the example (and the associated Visual Studio solution) can be found in the zip file below. 


Source Files:

Comments