Single instance of an application in C#

In this article we will see how to prevent a user from opening multiple instances of an application and how to notify the first instance when the user launches the second instance.

Those who use VB.NET are probably wondering why this article. Indeed, just tick a box:

In C# this options is unfortunately not available. However, we can find the same behavior by adding a reference to Microsoft.VisualBasic:

class Program
{
    class App : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
    {
        public App()
        {
            IsSingleInstance = true;
        }

        protected override void OnCreateMainForm()
        {
            MainForm = new Form1();
        }
    }

    static void Main(string[] args)
    {
        var app = new App();
        app.Run(args);
    }
}

It's pretty simple, but it only works for Windows Forms applications. For other types of application, it is necessary to put the hand in the dough.

Several bad ideas exist. The first is to list processes with Process.GetProcesses to test if a process with the same name exists. Several applications may have the same process name so this method is unreliable. The second is to create a file when launching the application. If the file already exists when launching the application then an instance already exists. The problem is to make sure to delete the file when closing the application (even when it crashes lamentably).

It is therefore necessary to find a good method and as often it is enough to turn to Windows. When two or more threads must access a shared resource at the same time, the system needs a synchronization mechanism to ensure that only one thread at a time uses the resource. Mutex is a synchronization primitive that grants a single thread exclusive access to the shared resource. If one thread acquires a mutex, the other thread that wants to acquire that mutex is interrupted until the first thread releases the mutex. Mutexes can be named to be shared between processes.

The idea is to create a Mutex when launching the application and to acquire the property. If a second application launches it will not be able to take ownership of this Mutex.

const string AppId = "Local\\1DDFB948-19F1-417C-903D-BE05335DB8A4"; // Unique par application
static void Main(string[] args)
{
    using (Mutex mutex = new Mutex(false, AppId))
    {
        if (!mutex.WaitOne(0))
        {
            Console.WriteLine("2nd instance");
            return;
        }

        Console.WriteLine("Started");
        Console.ReadKey();
    }
}

The second step is to notify the first instance that the user is trying to initiate another one. For this we can use IPC (Inter-Process Communication). The first application will declare the IPC channel. Other instances will connect to it and send a message to the first instance.

static void Main(string[] args)
{
    using (Mutex mutex = new Mutex(false, AppId))
    {
        if (!mutex.WaitOne(0))
        {
           ...
        }

        IpcChannel channel = new IpcChannel(AppId);
        ChannelServices.RegisterChannel(channel, false);
        RemotingConfiguration.RegisterWellKnownServiceType(typeof(SingleInstance), "RemotingServer", WellKnownObjectMode.Singleton);
        Console.WriteLine("Started");
        Console.ReadKey();
    }
}

// Objet used by the client and the server
private class SingleInstance : MarshalByRefObject
{
    public void Execute(string[] args)
    {
        Console.WriteLine("Second instance: " + string.Join(" ", args));
    }
}

Now the other instances will send a message to the server (first instance):

if (!mutex.WaitOne(0))
{
    IpcChannel channel = new IpcChannel();
    ChannelServices.RegisterChannel(channel, false);
    SingleInstance app = (SingleInstance)Activator.GetObject(typeof(SingleInstance), string.Format("ipc://{0}/RemotingServer", AppId));
    app.Execute(args);
    return;
}

There are other mechanisms for communicating between applications such as Windows Message (RegisterWindowMessage, PostMessage) or RPC. A fairly complete list is available on MSDN (https://docs.microsoft.com/en-us/windows/desktop/ipc/interprocess-communications).

The complete code: https://gist.github.com/meziantou/84b46cee16e9b565675e

Enjoy this blog? Buy Me A Coffee Donate with PayPal

Leave a reply