Super Simple Single Instance Application in Windows using C#

There are cases in the Windows world where you only want a single instance of an application.  So, if we launch the application either from the Start menu or command line, or we launch the process from another application, we only ever want one instance – the same one.

If an instance of the application already exists, we want the application to come to the foreground.  Not only that but we want the view in that single instance to change based on how the second instance was invoked.

Much has been written on this including using .NET remoting.  I chose a far simpler way involving memory mapped files, passing command line information from the second instance to the first.  (I freely acknowledge that there is a hack component to it.)

We will begin with the essence of the operation in the main function of your application.

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
  // The helper object for single instances
  ApplicationManagement applicationManagement = new     ApplicationManagement();

  // If we have arguments, pass them to the original instance.
  // If we are creating the first instance, the same memory mapped file mechanism is used for    simplicity.
  if (args.Length > 0)
    applicationManagement.SetArguments(args);

  // if a previous instance exists..
  if (applicationManagement.DoesPreviousInstanceExist())
  {
    // Exit stage left
    applicationManagement.ShowPreviousInstance();
    return;
  }

  // in the case where we are the first instance and we don't have a previous one, then
  // default to the default view
  if (args.Length == 0)
    applicationManagement.SetArguments(new string [] {   frmMain.commandLineFlagForm1 });

  Application.EnableVisualStyles();
  Application.SetCompatibleTextRenderingDefault(false);

  // pass the application management object to the main form
  frmMain main = new frmMain();
  main.applicationManagement = applicationManagement;
  Application.Run(main);
}

Here is the operation truth table with this code.

command line new instance created previous instance exists
no arguments show default view do nothing (but set to top of Z-order)
has arguments set view to the argument  set view to the argument

Notice that if a previous instance exists, we want to bring the previous instance window to the top of the Z-order.  The previous instance itself can not go to the foreground (without attaching to thread input of the top order window but we are not doing this).  While a new process is being created, until the first window of the application is created, the new process can send any other window of any other process to the top of the Z-order.  This is a peculiarity of Windows.

Now a hacky part…  In the main form, there is a timer that periodically checks for new arguments to appear.

private void tmrArguments_Tick(object sender, EventArgs e)
{
 string[] arguments = applicationManagement.GetArguments();
 if (arguments.Length > 0)
 {
 switch (arguments[0])
 {
 case commandLineFlagForm1:
 Form frm = ViewChildForm(typeof(frmForm1));
 if (arguments.Length == 3)
 (frm as frmForm1).SetSelectedProperty(arguments[1], arguments[2]);
 frm.WindowState = FormWindowState.Maximized;
 break;
 case commandLineFlagForm2:
 ViewChildForm(typeof(frmForm2)).WindowState = FormWindowState.Maximized;
 break;
 }
 }
}

Essentially, the form’s timer will periodically check to see if there are new arguments.  In this example, the command line can specify a form 1 or a form 2.  If form 1, it can pass additional information in as well.

So now we will get to the handling of the previous instance and bringing it to life and then we will deal with passing command line information.

For single instance, the applicationManagement class that you see handles the single instance.  The constructor determines if a previous instance exists.

public ApplicationManagement()
{
  Process currentProcess = Process.GetCurrentProcess();
  m_previousInstance = (from process in Process.GetProcesses()
                         where
                         process.Id != currentProcess.Id &&
                         process.ProcessName.Equals(
                         currentProcess.ProcessName,
                         StringComparison.Ordinal)
                         select process).FirstOrDefault();
  CreateArgumentStream();
}

/// <summary>
/// Does the previous instance exists?
/// </summary>
/// <returns></returns>
public bool DoesPreviousInstanceExist()
{
  return m_previousInstance != null;
}

/// <summary>
/// Shows the instance of the specified window handle
/// </summary>
/// <param name="windowHandle">The window handle.</param>
private void ShowInstance(IntPtr windowHandle)
{
  ShowWindow(windowHandle, WindowShowStyle.ShowNormal);
  EnableWindow(windowHandle, true);
  SetForegroundWindow(windowHandle);
}

/// <summary>
/// Shows the previous instance.
/// </summary>
public void ShowPreviousInstance()
{
  ShowInstance(m_previousInstance.MainWindowHandle);
}

You can see the methods where we determine if a previous instance exists and also showing the previous instance.  The ShowWindow, EnableWindow, and SetForegroundWindow API calls can be optained here:  http://www.pinvoke.net/.

Finally, the here is the code in the applicationManagement class that handles passing through the memory mapped buffer.

/// <summary>
/// The map name used for passing arguments through the MMF
/// </summary>
private const string mapName = "MySecretMapName";

/// <summary>
/// The argument stream length in bytes
/// </summary>
private const int argumentStreamLength = 512;

/// <summary>
/// The argument reference that will be used to pass arguments through a memory mapped file.
/// </summary>
private MemoryMappedFile m_arguments = null;

/// <summary>
/// The accessor to pass arguments through the memory mapped file
/// </summary>
private MemoryMappedViewAccessor m_argumentViewAccessor = null;
/// <summary>
/// Creates the argument stream.
/// </summary>
private void CreateArgumentStream()
{
  m_arguments = MemoryMappedFile.CreateOrOpen(mapName, argumentStreamLength, MemoryMappedFileAccess.ReadWrite);
  m_argumentViewAccessor = m_arguments.CreateViewAccessor();
  // Set up the buffer with nulls
  ClearArguments();
}

/// <summary>
/// Clears the arguments by setting everything to nulls which is still a valid string but has no meaning
/// </summary>
private void ClearArguments()
{
  byte[] buffer = new byte[argumentStreamLength];
  m_argumentViewAccessor.WriteArray<byte>(0, buffer, 0, buffer.Length);
}

/// <summary>
/// Gets the arguments from the memory mapped file and convert back to a string array.
/// We are using a less than pristine synchronization method of looking for any nulls in the string.
/// This method does however work as we are looking for the last null to be removed which should happen
/// in SetArguments.
/// </summary>
/// <returns></returns>
public string[] GetArguments()
{
  byte[] buffer = new byte[argumentStreamLength];

  m_argumentViewAccessor.ReadArray<byte>(0, buffer, 0, buffer.Length);
  // get the string of arguments from the buffer
  string args = Encoding.Unicode.GetString(buffer);
  // if there are any nulls, then we do nothing but return an empty string
  if (args.IndexOf('\0') != -1)
    return new string[0];

  // if there are no more nulls the we have something to evaluate
  args = args.Trim(); // remove the spaces
  ClearArguments();
  // split the string based on our marvelous separator
  return args.Split(new string[] { argumentSeparator }, StringSplitOptions.RemoveEmptyEntries);
}

/// <summary>
/// Sets the arguments from the command line. This is a poor mans way of doing this. A more "pristine" way would be to
/// serialize and deserialize the command line argument string array.
/// </summary>
/// <param name="args">The arguments.</param>
public void SetArguments(string[] args)
{
  // puts the nulls into the MMF
  ClearArguments();

  // Removes all the null when it is finally written to the MMF
  string arguments = String.Join(argumentSeparator, args).PadRight(argumentStreamLength, ' ');
  byte[] buffer = Encoding.Unicode.GetBytes(arguments);
  m_argumentViewAccessor.WriteArray<byte>(0, buffer, 0, buffer.Length);
}

This is a really simple information passing with some inherent synchronization although it is a bit hacky.  Essentially, the memory mapped buffer is full of nulls.  When the previous instance is going to put the command line arguments into the MMF, it copies all the command line arguments converting from a string array to a joined single string with a clever separator (lame).  Then it pads to the length of the buffer with spaces.

The primary instance will wait until there are no more nulls in the buffer before evaluating.  In this way, we achieve some form of synchronization.  After the complete string is read and split to the original string array, the buffer is cleared out.  (This is also a clear last one in wins scenario.)

So there it is.  If there are questions, post a comment.  I would love to see something other than “buy Nike shoes written in Chinese”.