Overview

This post shows how to create a single instance WinForm C# application using a Mutex. It also demonstrates how to send any object between instances with Named Pipes.

While writing OSD Background I wanted an application that would only allow a single instance to run at any given time. In addition I wanted it so that if another instance was executed with different parameters – or a quit request – then those parameters would be sent to the original instance so that it could process them. After sending the parameters the second instance would terminate.

The final solution I came up with uses a Mutex to handle the single instancing. The information I wanted to send between instances is wrapped into a ‘payload’ object that is serialized into XML and sent over a Named Pipe.

Downloads

A fully working example can be downloaded from GitHub at https://github.com/AutoItConsulting/examples-csharp/tree/master/MutexSingleInstanceAndNamedPipe
Download ZIP Icon @2x

On first run the application shows a simple WinForm application with an empty text box. When another attempt is made to run the application the command-line parameters are sent via a Named Pipe to the first instance and shown in the text box.

Single Instance Using a Mutex

Mutex is a simple object, you just pick a unique instance name and create a new object. A bool is returned that allows you to test if this mutex is owned by just the current process or if it is already in use. As long as the mutex is referenced other processes that request a new Mutex object will be able to see that another instance is running. In the code below I request the Mutex in the form Load event, and release it in the form Close event.

Note: It is critical to reference the Mutex object right at the end of your program’s execution rather than relying on the garbage collector to clean up after your program exits. Otherwise in a release build the compiler is clever enough to see that after your initial creation of the Mutex object you never use it again and will signal for it to be disposed before your program has exited. Of course, Mutex implements the IDisposable interface so it’s good practice to explicitly dispose it anyway. This explicit dispose in the Form Close event is enough to prevent the compiler from automatically disposing the Mutex earlier.

Here are the relevant functions:

public partial class FormMain : Form
{
    private const string MutexName = "MUTEX_SINGLEINSTANCEANDNAMEDPIPE";
    private bool _firstApplicationInstance;
    private Mutex _mutexApplication;
 
    private bool IsApplicationFirstInstance()
    {
        // Allow for multiple runs but only try and get the mutex once
        if (_mutexApplication == null)
        {
            _mutexApplication = new Mutex(true, MutexName, out _firstApplicationInstance);
        }
 
        return _firstApplicationInstance;
    }
    
    private void FormMain_Load(object sender, EventArgs e)
    {
        // First instance
        if (IsApplicationFirstInstance())
        {
            // Yes
            // Do something
        }
        else
        {
            // Close down
            Close();
        }
    }
    
    private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
    {
        // Close and dispose our mutex.
        if (_mutexApplication != null)
        {
            _mutexApplication.Dispose();
        }
    }
}
public partial class FormMain : Form
{
    private const string MutexName = "MUTEX_SINGLEINSTANCEANDNAMEDPIPE";
    private bool _firstApplicationInstance;
    private Mutex _mutexApplication;

    private bool IsApplicationFirstInstance()
    {
        // Allow for multiple runs but only try and get the mutex once
        if (_mutexApplication == null)
        {
            _mutexApplication = new Mutex(true, MutexName, out _firstApplicationInstance);
        }

        return _firstApplicationInstance;
    }
    
    private void FormMain_Load(object sender, EventArgs e)
    {
        // First instance
        if (IsApplicationFirstInstance())
        {
            // Yes
            // Do something
        }
        else
        {
            // Close down
            Close();
        }
    }
    
    private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
    {
        // Close and dispose our mutex.
        if (_mutexApplication != null)
        {
            _mutexApplication.Dispose();
        }
    }
}

Communication Payload

The ‘payload’ is the data I wanted to send between instances. In this example I used a custom object that has a copy of the command-line parameters used in the form of a List<string>. I am using the XmlSerializer so I’ve decorated the class with XML attributes. The BinaryFormatter (or any other serializer) could also be used in the same way. I chose XmlSerializer because in my OSD Background application I use it to send a copy of an XML options file.

public class NamedPipeXmlPayload
{
    /// <summary>
    ///     A list of command line arguments.
    /// </summary>
    [XmlElement("CommandLineArguments")]
    public List<string> CommandLineArguments { get; set; } = new List<string>();
}
public class NamedPipeXmlPayload
{
    /// <summary>
    ///     A list of command line arguments.
    /// </summary>
    [XmlElement("CommandLineArguments")]
    public List<string> CommandLineArguments { get; set; } = new List<string>();
}

Named Pipe Server

We create a single Named Pipe server which does the following.

  • Creates a new Named Pipe server and asynchronously waits for a client to connect ( NamedPipeCreateServer() )
  • On client connection reads payload sent from the client ( NamedPipeServerConnectionCallback() )
  • De-serializes the payload into our custom NamedPipeXmlPayload object ( NamedPipeServerConnectionCallback() )
  • Create a new Named Pipe server and repeats ( NamedPipeCreateServer() )

By default a Named Pipe can be accessed from the network as well as local machine. I don’t want that so I explicitly create a Named Pipe that denies network access.

Note: The NamedPipeServerConnectionCallback method takes place on a new thread. If you want to interact with the UI thread you will need to use the appropriate methods. In this example I update the text box in the UI thread using the Invoke functionality.

The code for all the relevant functions is below:

public partial class FormMain : Form
{
    private const string PipeName = "PIPE_SINGLEINSTANCEANDNAMEDPIPE";
    private readonly object _namedPiperServerThreadLock = new object();
    private NamedPipeServerStream _namedPipeServerStream;
    private NamedPipeXmlPayload _namedPipeXmlPayload;
 
    private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
    {
        // Dispose the named pipe steam
        if (_namedPipeServerStream != null)
        {
            _namedPipeServerStream.Dispose();
        }
    }
 
    private void FormMain_Load(object sender, EventArgs e)
    {
        // If are the first instance then we start the named pipe server listening and allow the form to load
        if (IsApplicationFirstInstance())
        {
            // Create a new pipe - it will return immediately and async wait for connections
            NamedPipeServerCreateServer();
        }
    }
 
    /// <summary>
    ///     Starts a new pipe server if one isn't already active.
    /// </summary>
    private void NamedPipeServerCreateServer()
    {
        // Create a new pipe accessible by local authenticated users, disallow network
        var sidNetworkService = new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null);
        var sidWorld = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
 
        var pipeSecurity = new PipeSecurity();
 
        // Deny network access to the pipe
        var accessRule = new PipeAccessRule(sidNetworkService, PipeAccessRights.ReadWrite, AccessControlType.Deny);
        pipeSecurity.AddAccessRule(accessRule);
 
        // Alow Everyone to read/write
        accessRule = new PipeAccessRule(sidWorld, PipeAccessRights.ReadWrite, AccessControlType.Allow);
        pipeSecurity.AddAccessRule(accessRule);
 
        // Current user is the owner
        SecurityIdentifier sidOwner = WindowsIdentity.GetCurrent().Owner;
        if (sidOwner != null)
        {
            accessRule = new PipeAccessRule(sidOwner, PipeAccessRights.FullControl, AccessControlType.Allow);
            pipeSecurity.AddAccessRule(accessRule);
        }
 
        // Create pipe and start the async connection wait
        _namedPipeServerStream = new NamedPipeServerStream(
            PipeName,
            PipeDirection.In,
            1,
            PipeTransmissionMode.Byte,
            PipeOptions.Asynchronous,
            0,
            0,
            pipeSecurity);
 
        // Begin async wait for connections
        _namedPipeServerStream.BeginWaitForConnection(NamedPipeServerConnectionCallback, _namedPipeServerStream);
    }
 
    /// <summary>
    ///     The function called when a client connects to the named pipe. Note: This method is called on a non-UI thread.
    /// </summary>
    /// <param name="iAsyncResult"></param>
    private void NamedPipeServerConnectionCallback(IAsyncResult iAsyncResult)
    {
        try
        {
            // End waiting for the connection
            _namedPipeServerStream.EndWaitForConnection(iAsyncResult);
 
            // Read data and prevent access to _namedPipeXmlPayload during threaded operations
            lock (_namedPiperServerThreadLock)
            {
                // Read data from client
                var xmlSerializer = new XmlSerializer(typeof(NamedPipeXmlPayload));
                _namedPipeXmlPayload = (NamedPipeXmlPayload)xmlSerializer.Deserialize(_namedPipeServerStream);
 
                // _namedPipeXmlPayload contains the data sent from the other instance
                // As an example output it to the textbox
                // In more complicated cases would need to do some processing here and possibly pass to UI thread
                TextBoxAppend(_namedPipeXmlPayload);
            }
        }
        catch (ObjectDisposedException)
        {
            // EndWaitForConnection will exception when someone closes the pipe before connection made
            // In that case we dont create any more pipes and just return
            // This will happen when app is closing and our pipe is closed/disposed
            return;
        }
        catch (Exception)
        {
            // ignored
        }
        finally
        {
            // Close the original pipe (we will create a new one each time)
            _namedPipeServerStream.Dispose();
        }
 
        // Create a new pipe for next connection
        NamedPipeServerCreateServer();
    }
    
    /// <summary>
    ///     Appends string version of the payload to the end of the text box. Handles being called from a non UI thread.
    /// </summary>
    private void TextBoxAppend(NamedPipeXmlPayload namedPipeXmlPayload)
    {
        if (textBoxOutput.InvokeRequired)
        {
            textBoxOutput.Invoke((MethodInvoker)delegate { TextBoxAppend(namedPipeXmlPayload); });
            return;
        }
 
        foreach (string commandLine in namedPipeXmlPayload.CommandLineArguments)
        {
            message += "\r\nCommandLine: " + commandLine;
        }
 
        textBoxOutput.Text += message + "\r\n\r\n";
    }
}
public partial class FormMain : Form
{
	private const string PipeName = "PIPE_SINGLEINSTANCEANDNAMEDPIPE";
	private readonly object _namedPiperServerThreadLock = new object();
	private NamedPipeServerStream _namedPipeServerStream;
	private NamedPipeXmlPayload _namedPipeXmlPayload;

	private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
	{
		// Dispose the named pipe steam
		if (_namedPipeServerStream != null)
		{
			_namedPipeServerStream.Dispose();
		}
	}

	private void FormMain_Load(object sender, EventArgs e)
	{
		// If are the first instance then we start the named pipe server listening and allow the form to load
		if (IsApplicationFirstInstance())
		{
			// Create a new pipe - it will return immediately and async wait for connections
			NamedPipeServerCreateServer();
		}
	}

	/// <summary>
	///     Starts a new pipe server if one isn't already active.
	/// </summary>
	private void NamedPipeServerCreateServer()
	{
		// Create a new pipe accessible by local authenticated users, disallow network
		var sidNetworkService = new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null);
		var sidWorld = new SecurityIdentifier(WellKnownSidType.WorldSid, null);

		var pipeSecurity = new PipeSecurity();

		// Deny network access to the pipe
		var accessRule = new PipeAccessRule(sidNetworkService, PipeAccessRights.ReadWrite, AccessControlType.Deny);
		pipeSecurity.AddAccessRule(accessRule);

		// Alow Everyone to read/write
		accessRule = new PipeAccessRule(sidWorld, PipeAccessRights.ReadWrite, AccessControlType.Allow);
		pipeSecurity.AddAccessRule(accessRule);

		// Current user is the owner
		SecurityIdentifier sidOwner = WindowsIdentity.GetCurrent().Owner;
		if (sidOwner != null)
		{
			accessRule = new PipeAccessRule(sidOwner, PipeAccessRights.FullControl, AccessControlType.Allow);
			pipeSecurity.AddAccessRule(accessRule);
		}

		// Create pipe and start the async connection wait
		_namedPipeServerStream = new NamedPipeServerStream(
			PipeName,
			PipeDirection.In,
			1,
			PipeTransmissionMode.Byte,
			PipeOptions.Asynchronous,
			0,
			0,
			pipeSecurity);

		// Begin async wait for connections
		_namedPipeServerStream.BeginWaitForConnection(NamedPipeServerConnectionCallback, _namedPipeServerStream);
	}

	/// <summary>
	///     The function called when a client connects to the named pipe. Note: This method is called on a non-UI thread.
	/// </summary>
	/// <param name="iAsyncResult"></param>
	private void NamedPipeServerConnectionCallback(IAsyncResult iAsyncResult)
	{
		try
		{
			// End waiting for the connection
			_namedPipeServerStream.EndWaitForConnection(iAsyncResult);

			// Read data and prevent access to _namedPipeXmlPayload during threaded operations
			lock (_namedPiperServerThreadLock)
			{
				// Read data from client
				var xmlSerializer = new XmlSerializer(typeof(NamedPipeXmlPayload));
				_namedPipeXmlPayload = (NamedPipeXmlPayload)xmlSerializer.Deserialize(_namedPipeServerStream);

				// _namedPipeXmlPayload contains the data sent from the other instance
				// As an example output it to the textbox
				// In more complicated cases would need to do some processing here and possibly pass to UI thread
				TextBoxAppend(_namedPipeXmlPayload);
			}
		}
		catch (ObjectDisposedException)
		{
			// EndWaitForConnection will exception when someone closes the pipe before connection made
			// In that case we dont create any more pipes and just return
			// This will happen when app is closing and our pipe is closed/disposed
			return;
		}
		catch (Exception)
		{
			// ignored
		}
		finally
		{
			// Close the original pipe (we will create a new one each time)
			_namedPipeServerStream.Dispose();
		}

		// Create a new pipe for next connection
		NamedPipeServerCreateServer();
	}
	
	/// <summary>
	///     Appends string version of the payload to the end of the text box. Handles being called from a non UI thread.
	/// </summary>
	private void TextBoxAppend(NamedPipeXmlPayload namedPipeXmlPayload)
	{
		if (textBoxOutput.InvokeRequired)
		{
			textBoxOutput.Invoke((MethodInvoker)delegate { TextBoxAppend(namedPipeXmlPayload); });
			return;
		}

		foreach (string commandLine in namedPipeXmlPayload.CommandLineArguments)
		{
			message += "\r\nCommandLine: " + commandLine;
		}

		textBoxOutput.Text += message + "\r\n\r\n";
	}
}

Named Pipe Client

The code for the Named Pipe client operations are much more simple:

  • Check if first instance of application, if not prepare client message ( FormMain_Load() )
  • Add command-line parameters to our custom payload object ( FormMain_Load() )
  • Connect to the Named Pipe and send the payload ( NamedPipeClientSendOptions() )
  • Close application

The code is below:

public partial class FormMain : Form
{
    private const string PipeName = "PIPE_SINGLEINSTANCEANDNAMEDPIPE";
    private NamedPipeServerStream _namedPipeServerStream;
    private NamedPipeXmlPayload _namedPipeXmlPayload;
 
    private void FormMain_Load(object sender, EventArgs e)
    {
        // If are the first instance then we start the named pipe server listening and allow the form to load
        if (IsApplicationFirstInstance())
        {
            // Create a new pipe - it will return immediately and async wait for connections
            NamedPipeServerCreateServer();
        }
        else
        {
            // We are not the first instance, send the named pipe message with our payload and stop loading
            var namedPipeXmlPayload = new NamedPipeXmlPayload
            {
                CommandLineArguments = Environment.GetCommandLineArgs().ToList()
            };
 
            // Send the message
            NamedPipeClientSendOptions(namedPipeXmlPayload);
 
            // Stop loading form and quit
            Close();
        }
    }
 
    /// <summary>
    ///     Uses a named pipe to send the currently parsed options to an already running instance.
    /// </summary>
    /// <param name="namedPipePayload"></param>
    private void NamedPipeClientSendOptions(NamedPipeXmlPayload namedPipePayload)
    {
        try
        {
            using (var namedPipeClientStream = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
            {
                namedPipeClientStream.Connect(3000); // Maximum wait 3 seconds
 
                var xmlSerializer = new XmlSerializer(typeof(NamedPipeXmlPayload));
                xmlSerializer.Serialize(namedPipeClientStream, namedPipePayload);
            }
        }
        catch (Exception)
        {
            // Error connecting or sending
        }
    }
}
public partial class FormMain : Form
{
    private const string PipeName = "PIPE_SINGLEINSTANCEANDNAMEDPIPE";
    private NamedPipeServerStream _namedPipeServerStream;
    private NamedPipeXmlPayload _namedPipeXmlPayload;

    private void FormMain_Load(object sender, EventArgs e)
    {
        // If are the first instance then we start the named pipe server listening and allow the form to load
        if (IsApplicationFirstInstance())
        {
            // Create a new pipe - it will return immediately and async wait for connections
            NamedPipeServerCreateServer();
        }
        else
        {
            // We are not the first instance, send the named pipe message with our payload and stop loading
            var namedPipeXmlPayload = new NamedPipeXmlPayload
            {
                CommandLineArguments = Environment.GetCommandLineArgs().ToList()
            };

            // Send the message
            NamedPipeClientSendOptions(namedPipeXmlPayload);

            // Stop loading form and quit
            Close();
        }
    }

    /// <summary>
    ///     Uses a named pipe to send the currently parsed options to an already running instance.
    /// </summary>
    /// <param name="namedPipePayload"></param>
    private void NamedPipeClientSendOptions(NamedPipeXmlPayload namedPipePayload)
    {
        try
        {
            using (var namedPipeClientStream = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
            {
                namedPipeClientStream.Connect(3000); // Maximum wait 3 seconds

                var xmlSerializer = new XmlSerializer(typeof(NamedPipeXmlPayload));
                xmlSerializer.Serialize(namedPipeClientStream, namedPipePayload);
            }
        }
        catch (Exception)
        {
            // Error connecting or sending
        }
    }
}

Downloads

A fully working example can be downloaded from GitHub at https://github.com/AutoItConsulting/examples-csharp/tree/master/MutexSingleInstanceAndNamedPipe
Download ZIP Icon @2x

On first run the application shows a simple WinForm application with an empty text box. When another attempt is made to run the application the command-line parameters are sent via a Named Pipe to the first instance and shown in the text box.