How to ensure that your user only runs one instance of your C# program

There are times when we want to limit our users to run an instance of our C# program at any one time. Such situations can be applicable for network monitoring tools, messaging applications, web server monitoring application and etc. In this post, I discuss how we can achieve that via the mutual exclusive (Mutex) mechanism available in C#.

What happens when users run our C# program?

Whenever an instance of our C# program is executed, a new process will be created to run our C# program binary.

Hence, to ensure that only one instance of our C# program is running at any one time, all program instances need to perform two of three main tasks right at the point when they get to run:

  • Check with the operating system if there is already a process is running our program binary.
  • Continue to run when there is no process running our program binary.
  • Stop running when there is already a process created to run our program binary.

Why is the Mutex ideal for our scenario?

The characteristic of the Mutex is such that for any Mutex, there can only be one acquisition made on the Mutex, at the process level. Unless the previous acquirer of the Mutex relinquish ownership of the Mutex, no other process can acquire the same Mutex. This is why the Mutex is an ideal choice when it comes to ensuring that a user only runs one instance of our C# program.

Allow only one program instance to run with System.Threading.Mutex

using System;
using System.Threading;
using System.Text;

public class Program
{

    public static void Main(string[] args)
    {
 
        bool successAquisition;
        Mutex programMutex = new Mutex(true,
            AppDomain.CurrentDomain.SetupInformation.ApplicationName,
            out successAquisition);
 
        if (successAquisition)
        {
 
            Console.WriteLine("This is the only program instance that " +
                "is running now. Press <enter> to exit");
            Console.Read();
            programMutex.ReleaseMutex();
            GC.KeepAlive(programMutex);
        }
        else
        {
            Console.WriteLine("An instance of this program is already running.");
        } // end if
 
        Console.WriteLine("Complete mutex acquisition. Press Enter.");
        Console.Read();
 
    } // end public
} // end public class Program

Check with the operating system if there are already a process is running our program binary.

At the start of our program run, we create an instance of System.Threading.Mutex in order to acquire a Mutex from the operating system. We pass three parameters to one of its constructor:

  1. A boolean value of true, which indicates that the program instance that gets to run will have ownership to the Mutex object. This is necessary to avoid any exceptions from being thrown when the running program releases ownership of the Mutex object when it terminates at a later time.
  2. AppDomain.CurrentDomain.SetupInformation.ApplicationName provides the name of our C# program for the operating system to maintain a single Mutex for all our program instances in a user session.
  3. A boolean variable, successAquisition, for the operating system to tell our program whether it had successfully acquired the Mutex.

Continue to run when there is no process running our program binary

When our program instance is able to acquire the Mutex successfully, successAquisition will be true, hence triggering the codes inside the if statement block.

Inside the if statement block, we write a message to console to denote success acquisition of the Mutex. We then stall the program via a Console.Read. In real applications, these two statements will be the point where we initiate the core components of our application.

After that, we call programMutex.ReleaseMutex() so that when our program instance terminates, another instance can be started at a later point in time.

At the end of the if statement block, we call GC.KeepAlive(programMutex) so that the garbage collector will not have any chances to delete and release programMutex, thereby preventing the possibility that additional instances of our program are created while there is still one running.

Terminate the process when there is already a process created to run our program binary

When our program instance is unable to acquire the Mutex successfully, successAquisition will be false, hence triggering the codes inside the else statement block. Inside the else statement block, we write a message to console, telling the user that an instance of our program is already running. After that we allow our program to terminate by itself.

Halt the process so that we can see whether our implementation works as intended

Outside the conditional blocks, we write a message to show that we have reached the end of the program. We then stall our program via a Console.Read so that we can see the messages from the sample implementation before our program exits.

Other posts that may interest you

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.