Chapter 18. Ensuring a Single Instance of an Application

 

Never allow the same bug to bite you twice.

 
 --Steve Maguire

With most modern operating systems, multiple instances or processes of an application can be launched, each with its own internal state and memory. Some tools are not affected by multiple instances being launched, but other tools are. Imagine a tool that, when launched, creates a network socket and binds it to a specific port through which to receive data. If a second instance of that tool were launched, the initialization would fail because the network port would already be in use. As another example, look at Adobe Photoshop or any other fully featured image editing suite. How frustrating do you think it would be if, every time you double-clicked an image on your desktop, a new instance of Adobe Photoshop would launch? When a file associated with a specific application is launched from Windows Explorer, the file name is not passed to a current running process if there is one. The application that handles the file is determined and a new instance is launched with the file name as a parameter.

A solution to this problem would be a system that could determine whether there are any running instances of a particular application, redirect launch parameters to the running instance, and abort the launching of any additional instances.

This chapter presents a couple of ways to determine whether there is a running instance of the application, pass command line arguments to an existing instance, and bring its main window to the foreground.

Early Solutions

I have used a few strategies in the past to implement single application instances. One method is to create a threading Mutex with a unique name that identifies the application set to the full path to the executing assembly. The Mutex class can be used to protect a shared resource from simultaneous access by multiple threads and processes. A Mutex has two states: signaled or non-signaled. When the state is signaled, the Mutex is not owned by any thread. When the state is non-signaled, that means there is a thread that currently possesses ownership of the Mutex. The ownership of a Mutex is only available to a single thread, so when two threads try to write to the shared memory at the same time, the first thread to do so acquires ownership, while the second thread waits for ownership to be released.

The first time an application starts up, it will perform a check for a uniquely named Mutex to see if there are any other running instances. It will not find any, so the next step will be to create a Mutex with a unique name so that other application instances can see that another instance is already running; the unique name will be the full system path to the executing assembly’s location. With the Mutex created, we must now call the WaitOne() method so that we set the state to non-signaled and grant ownership to the thread of this application. At this point, the application instance can completely load and make itself available to the end user.

All subsequent application instances that start up will perform the same check that the first instance did, except they will fail. Each new instance will create a Mutex class using the same name that the first instance used, but all further calls to WaitOne() will return false because the ownership is currently bound to the thread of the first instance.

The following code shows a simplified implementation using this approach.

using System.Threading;

[STAThread]
static void Main()
{
    Mutex mutex = new Mutex(false, Assembly.GetExecutingAssembly().Location);

    if (!mutex.WaitOne(1, true))
    {
         MessageBox.Show("There is already an instance of this executable " +
                         "running as a process");
     }
     else
     {
         // Keep the mutex alive until the process terminates
         GC.KeepAlive(mutex);

         Application.Run(new MainForm());
     }
}

Another method is to use Windows Management Instrumentation (WMI) to query the operating system for a listing of active processes filtered by name. Some developers may favor this approach to the other solutions, so the following code has been included to show an implementation using WMI. The Companion Web site has the full source code with additional comments.

public sealed class ProcessCountManager
{

    public static int Query(string applicationName)
    {
        return QueryRunningProcessCount(applicationName);
    }

    public static int Query(System.Reflection.Assembly assembly)
    {
        // Break the full path to the executing assembly into
        // a string array of parts
        string[] locationParts = assembly.Location.Split("\".ToCharArray());

        // Retrieve the application name from the last element
        // of the location array
        string applicationName = locationParts[locationParts.Length - 1];

        // Return the running process count for the specified application name
        return QueryRunningProcessCount(applicationName);
    }

    public static bool IsRunning(string applicationName)
    {
        // Add 1 to account for the application doing the check
        return Query(applicationName) > 1;
     }

     public static bool IsRunning(System.Reflection.Assembly assembly)
     {
         // Add 1 to account for the application doing the check
         return Query(assembly) > 1;
     }

     private static int QueryRunningProcessCount(string applicationName)
     {
         // Build a formatted WMI management query to select all
         // processes matching a specific name
         string query = String.Format("SELECT Name FROM CIM_Process " +
                                     "WHERE Name = '{0}'",
                                      applicationName);

         // Build an enumerator for the management query results
         ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

         // Return the number of results (process count) in the management query
         return searcher.Get().Count;
    }
}

The WMI implementation has a class composed of static methods that can determine the number of running processes filtered by name. The easiest and most maintainable way to use this implementation is to pass the executing assembly object into the manager. This way, if the assembly name changes, you do not have to update the code to reflect these changes. The following code shows the proper usage of the WMI approach.

[STAThread]
static void Main()
{
    if (ProcessCountManager.IsRunning(Assembly.GetExecutingAssembly()))
    {
        MessageBox.Show("There is already an instance of this " +
                        "executable running as a process");
    }
    else
         Application.Run(new MainForm());
}

Finally, another approach, though a simplified version of the WMI implementation, is to use the Process object from the System.Diagnostics namespace. While the WMI version is extremely extensible and robust, the following code using the Process object is better suited to our needs because it is lightweight.

using System.Diagnostics;

[STAThread]
static void Main()
{
    Process process = Process.GetCurrentProcess();

    if (Process.GetProcessesByName(process.ProcessName).Count > 1)
    {
        MessageBox.Show("There is already an instance of this " +
                        "executable running as a process");
    }
    else
        Application.Run(new MainForm());
}

So far we have discussed a couple of the earlier ways to implement single instance applications, but with the advent of .NET 2.0, a new integrated approach was provided that takes care of all the ugly details behind the scenes.

Journey to the Dark Side

Every CLR-compliant language for the .NET platform can reproduce identical functionality by sharing a common set of framework components. This is because CLR-compliant languages must support the interoperability with other assemblies that can be written in a variety of managed languages, meaning that an assembly written in Visual Basic .NET must be accessible from within a C# application without any performance overhead related to data conversion or “thunking.”

Legacy versions of Visual Basic provided a variety of pre-built components that aided in the development of applications. Visual Basic .NET exposes a similar library of components that are generally accessed through the property pages of the project. One component in particular is the ability to restrict an application so that it may only be launched once, and redirect the command line parameters from subsequent instances to the initial one. Visual Basic .NET has a checkbox in the project properties that enables this functionality, but Visual C# .NET does not provide it at this time.

Upon closer inspection of a single instance VB.NET application with a disassembler, Microsoft.VisualBasic.dll is referenced by the runtime. This assembly exposes the WindowsFormsApplicationBase class that exists in the Microsoft.VisualBasic. ApplicationServices namespace. This class provides a mechanism to restrict an application so that it may only be launched once, and this mechanism supports the redirection of command line parameters to itself. Microsoft.VisualBasic.dll is a common framework component, and it is accessible from any managed language because it is merely a library of compiled MSIL byte code.

The Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase class will be used to implement the solution, as presented in the next section.

The Solution

The following namespaces are used by the solution, and the only other namespace below worth mentioning is System.Collections.ObjectModel. This namespace provides a generic ReadOnlyCollection that wraps a data type into a strongly typed, read-only list.

using System;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using Microsoft.VisualBasic.ApplicationServices;

The solution in this chapter will require a way to send a notification to the main application instance when another instance attempts to launch. The following class describes the event arguments that will be passed with the notification. It merely stores a reference to the main form of the application and a collection of string parameters that were passed by the command line.

internal class SingleInstanceEventArgs : EventArgs
{
    private ReadOnlyCollection<string> commandLine;

    private Form mainForm;

    internal ReadOnlyCollection<string> CommandLine
    {
        get { return commandLine; }
    }

    internal Form MainForm
    {
        get { return mainForm; }
    }

    internal SingleInstanceEventArgs(ReadOnlyCollection<string> commandLine,
                                     Form mainForm)
    {
        this.commandLine = commandLine;
        this.mainForm = mainForm;
    }
}

An important goal of this solution is to transition easily from the standard launching approach for an application to our new single instance version. This goal warranted the design of a static class that exposes a simple Run method, much like the Application class. The Run method takes in a reference to the main form of the application and a delegate to the method that will handle subsequent instance notifications. The WindowsFormsApplicationBase has a StartupNextInstance event that is fired when another instance is launched. This solution handles this event behind the scenes and redirects the event arguments with additional information to the SingleInstanceEvent delegate.

internal class SingleInstanceApplication : WindowsFormsApplicationBase
{
    private SingleInstanceApplication()
    {
        base.IsSingleInstance = true;
    }

    private static EventHandler<SingleInstanceEventArgs> SingleInstanceEvent;

    private static SingleInstanceApplication applicationBase;

    internal static void Run(Form form,
                             EventHandler<SingleInstanceEventArgs> handler)
    {
        SingleInstanceEvent += handler;

        applicationBase = new SingleInstanceApplication();
        applicationBase.MainForm = form;
        applicationBase.StartupNextInstance += StartupNextInstanceEventHandler;
        applicationBase.Run(Environment.GetCommandLineArgs());
    }

    private static void StartupNextInstanceEventHandler(object sender,
                                                   StartupNextInstanceEventArgs e)
    {
        if (SingleInstanceEvent != null)
        {
            SingleInstanceEvent(applicationBase,
            new SingleInstanceEventArgs(e.CommandLine, applicationBase.MainForm));
        }
    }
}

The following code shows how a WinForms application is generally launched.

[STAThread]
static void Main()
{
    // The old way to launch the application
    Application.EnableVisualStyles();
    Application.Run(new MainForm());
}

The following code shows the new way a WinForms application will be launched using the single instance component.

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    SingleInstanceApplication.Run(new MainForm(), StartupNextInstanceEventHandler);
}

You should have noticed the StartupNextInstanceEventHandler. This parameter is a delegate that will be fired when a subsequent instance is launched, and the main instance should be notified and given the command line parameters. The following code shows how to implement this delegate, activate the main form of the application, and pass command line parameters to the form.

private static void StartupNextInstanceEventHandler(object sender,
                                                    SingleInstanceEventArgs e)
{
    // Restore the window if it is currently minimized
    if (e.MainForm.WindowState == FormWindowState.Minimized)

    {
        e.MainForm.WindowState = FormWindowState.Normal;
    }

    // Activate the main form
    e.MainForm.Activate();

    ((MainForm)e.MainForm).HandleCommandLine(e.CommandLine);
}

The MainForm class is a simple form that has a method called HandleCommandLine. This method takes in a ReadOnlyCollection<string> instance that contains the command line parameters. It is now up to you how to determine how these parameters are handled!

Conclusion

In this chapter I began by discussing the necessity of the singleton pattern for application instances, and then later I detailed a variety of ways to implement such a pattern. Each method is better suited to a different situation, though the best approach when at all possible is to use the ApplicationServices component. This approach offers the least amount of work to implement, and is trivial to maintain.

Perhaps in the future, this functionality will be refactored into a more general component that is “natively” supported by Visual C# .NET, but at the moment, it seems to be the best way to handle single application instances with the least amount of code and effort to maintain it.

Aside from the ApplicationServices component, the other approaches did not show how to pass command line parameters to the initial instance. This can be done using .NET Remoting, a TCPIP loopback channel, or even the WM_COPYDATA event and the Win32 message pump. This functionality is beyond the scope of this chapter, but is covered in Parts IV and V of this book.

The Companion Web site contains the full source code and examples to the solutions presented in this chapter.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.78.136