Click here to Skip to main content
15,887,319 members
Articles / Programming Languages / C#
Tip/Trick

Single Instance Form Application in C#

Rate me:
Please Sign up or sign in to vote.
4.48/5 (12 votes)
27 Dec 2013CPOL5 min read 53K   1.1K   17   12
Make a single instance form application that redisplays the one and only interface of the other instance.

Introduction

I needed a single instance form application. That is no problem, create a named Mutex and check if that named mutex already exists. There were two other wishes that are not so easy to implement:

  • The original form application must be shown whenever the user tries to start another.
  • Nothing of the form must start or be shown when a second instance is started.

The second wish immediately sets one on track to look in the program.cs file that the Visual Studio wizard creates. Preventing the calling of the Application.Run method is the trick here. But what remains is the question how to reshow and enable the original form. And I want to use as less of the WinAPI as possible.

The solution

I decided to use an named auto reset event. The named part makes sure that there is only one such event in the system, just like the named Mutex. And with the event part I can signal the original instance that it should show itself. So it is simple, check if a named event already exists, if not then start the application as normal. If it does exists then signal the event. Now only we need a method to react on the signalling of the event, that means creating a thread.

Implementation

If a form application is created with the Visual Studio Wizard then the program.cs file looks like this.

C#
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace SingleInstanceApp
{
  static class Program
  {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());
    }
  }
}

To enable the single instance application we change that to this:

Note that the original three lines of code in the Main() method stay the same.

C#
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace SingleInstanceApp
{
  static class Program
  {
    // Add "global\" in front of the EventName, then only one instance is allowed on the
    // whole system, including other users. But the application can not be brought
    // into view, of course. 
    private static String SingleAppComEventName = "Make up some unique name here, suggestion: use a GUID";
    private static BackgroundWorker singleAppComThread = null;
    private static EventWaitHandle threadComEvent = null;

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      try
      {
        // another instance is already running
        threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName);
        threadComEvent.Set();  // signal the other instance.
        threadComEvent.Close();
        return;    // return immediatly.
      }
      catch { /* don't care about errors */     }
      // Create the Event handle
      threadComEvent = new EventWaitHandle(false, EventResetMode.AutoReset, SingleAppComEventName);
      CreateInterAppComThread();

      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());

      // End the communication thread.
      singleAppComThread.CancelAsync();
      while (singleAppComThread.IsBusy)
        Thread.Sleep(50);
      threadComEvent.Close();
    }
  }
}

What has changed and why?

There are three extra using lines, for obvious reasons.

C#
using System.Threading;
using System.ComponentModel;
using System.Runtime.InteropServices;

In the program class these three properties are added:

C#
private static String SingleAppComEventName = "Make up some unique name here, suggestion: use a GUID";
private static BackgroundWorker singleAppComThread = null;
private static EventWaitHandle threadComEvent = null;

The SingleAppComEventName can be best a GUID, use the "Create GUID" command in the tools of Visual Studio.

In the Main() method, before the original three lines, this code block is inserted.

C#
try
{
  // another instance is already running if OpenExsting succeeds.
  threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName);
  threadComEvent.Set();  // signal the other instance.
  threadComEvent.Close();
  return;    // return immediatly.
}
catch { /* don't care about errors */     }
// Create the Event handle
threadComEvent = new EventWaitHandle(false, EventResetMode.AutoReset, SingleAppComEventName);
CreateInterAppComThread();

The most important code is the OpenExisting() call. If the application succeeds in opening this named EventWaitHandle then another instance of the application is already running. If it doesn't succeeds an exception is thrown. We don't care about the exceptions, what is important that if another instance is not found then the named event is created as soon as possible, which is done with the new EventWaitHandle() call.

After that a thread is created through which a "show yourself" event is received.

After the original three lines the next block of code is inserted to clean up the mess.

C#
  // End the communication thread.
singleAppComThread.CancelAsync();
while (singleAppComThread.IsBusy)
    Thread.Sleep(50);
threadComEvent.Close();

Then the following methods are added to the Program class.

  • static private void CreateInterAppComThread().
    This is a helper method to setup a listening thread.
  • static private void singleAppComThread_DoWork(object sender, DoWorkEventArgs e)
    .
    This is the worker part of the listening thread.
  • static private void ThreadFormVisable(Form frm).
    This is part of the Invoke delegate method so the action needed to show the application form is preformed in the same thread as that created the form.

In this code block also the SetForegroundWindow() WinAPI method is imported and the Invoke delegate is declared.

CreateInterAppComThread

C#
static private void CreateInterAppComThread()
{
  singleAppComThread = new BackgroundWorker();
  singleAppComThread.WorkerReportsProgress = false;
  singleAppComThread.WorkerSupportsCancellation = true;
  singleAppComThread.DoWork += new DoWorkEventHandler(singleAppComThread_DoWork);
  singleAppComThread.RunWorkerAsync();
}

This code needs little explanation. A simple BackgroundWorker instance creation.

singleAppComThread_DoWork

C#
static private void singleAppComThread_DoWork(object sender, DoWorkEventArgs e)
{
  BackgroundWorker worker = sender as BackgroundWorker;
  WaitHandle[] waitHandles = new WaitHandle[] { threadComEvent };

  while (!worker.CancellationPending)
  {
    // check every second for a signal.
    if (WaitHandle.WaitAny(waitHandles, 1000) == 0)
    {
      // The user tried to start another instance. We can't allow that, 
      // so bring the other instance back into view and enable that one. 
      // That form is created in another thread, so we need some thread sync magic.
      if (Application.OpenForms.Count > 0)
      {
        Form mainForm = Application.OpenForms[0];
        mainForm.Invoke(new SetFormVisableDelegate(ThreadFormVisable), mainForm);
      }
    }
  }
}

Here the magic happens that enables the application to be notified that it is suspose to show itself to the user. This is signalled by the signalling of the threadComEvent becoming set. That happens in the Main method. When the event gets signalled the code checks if the application has any forms and if so then the first form is activated and brought to the foreground. Because that form is created in a different thread then this one, the Invoke method is used on the form.

ThreadFormVisable

C#
static private void ThreadFormVisable(Form frm)
{
  if (frm != null)
  {
    // display the form and bring to foreground.
    frm.Visible = true;
    frm.WindowState = FormWindowState.Normal;
    frm.Show();
    SetForegroundWindow(frm.Handle);
  }
}

This method is called using the Invoke method on the primary form of th application. The form is made visible, the state is changed to normal (in case it was iconized) and the form is made visible in the foreground.

That's it. The completed program.cs file now looks like this:

C#
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace SingleInstanceApp
{
  static class Program
  {
    // Add "global\" in front of the EventName, then only one instance is allowed on the
    // whole system, including other users. But the application can not be brought
    // into view, of course. 
    private static String SingleAppComEventName = "Make up some unique name here, suggestion: use a GUID";
    private static BackgroundWorker singleAppComThread = null;
    private static EventWaitHandle threadComEvent = null;

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      try
      {
        // another instance is already running if OpenExsting succeeds.
        threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName);
        threadComEvent.Set();  // signal the other instance.
        threadComEvent.Close();
        return;    // return immediatly.
      }
      catch { /* don't care about errors */     }
      // Create the Event handle
      threadComEvent = new EventWaitHandle(false, EventResetMode.AutoReset, SingleAppComEventName);
      CreateInterAppComThread();

      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());

      // End the communication thread.
      singleAppComThread.CancelAsync();
      while (singleAppComThread.IsBusy)
        Thread.Sleep(50);
      threadComEvent.Close();
    }
    /// <summary>
    /// 
    /// </summary>
    static private void CreateInterAppComThread()
    {
      singleAppComThread = new BackgroundWorker();
      singleAppComThread.WorkerReportsProgress = false;
      singleAppComThread.WorkerSupportsCancellation = true;
      singleAppComThread.DoWork += new DoWorkEventHandler(singleAppComThread_DoWork);
      singleAppComThread.RunWorkerAsync();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    static private void singleAppComThread_DoWork(object sender, DoWorkEventArgs e)
    {
      BackgroundWorker worker = sender as BackgroundWorker;
      WaitHandle[] waitHandles = new WaitHandle[] { threadComEvent };

      while (!worker.CancellationPending)
      {
        // check every second for a signal.
        if (WaitHandle.WaitAny(waitHandles, 1000) == 0)
        {
          // The user tried to start another instance. We can't allow that, 
          // so bring the other instance back into view and enable that one. 
          // That form is created in another thread, so we need some thread sync magic.
          if (Application.OpenForms.Count > 0)
          {
            Form mainForm = Application.OpenForms[0];
            mainForm.Invoke(new SetFormVisableDelegate(ThreadFormVisable), mainForm);
          }
        }
      }
    }
    // Activate an application window.
    [DllImport("USER32.DLL")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);

    /// <summary>
    /// When this method is called using a Invoke then this runs in the thread
    /// that created the form, which is nice. 
    /// </summary>
    /// <param name="frm"></param>
    private delegate void SetFormVisableDelegate(Form frm);
    static private void ThreadFormVisable(Form frm)
    {
      if (frm != null)
      {
        // display the form and bring to foreground.
        frm.Visible = true;
        frm.WindowState = FormWindowState.Normal;
        frm.Show();
        SetForegroundWindow(frm.Handle);
      }
    }
  }
}

Cleanup of the code.

We don't want all that code in the program.cs file. So to cleanup I moved the code into a separate code file, SingleInstCode.cs. Now the program.cs file can look like this:

C#
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace SingleInstanceApp2
{
  static class Program
  {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      if (SingleInstanceCode.SingleInstanceClass.CheckForOtherApp("Make up some unique ID here; use a GUID"))
        return;

      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());
    }
  }
}

This looks much better. The difference with the original Visual Studio created file is the addition of the  SingleInstanceClass.CheckForOtherApp() call. The magic happens in the SingleInstanceClass class. The source code of which you can find in the download. It is not much different then the code that was earlier in the program.cs but has now a helper class with a destructor that closes the unique event handle and terminates the synchronization thread when the application terminates. It's pretty simple:

C#
private static CleanupCode cleanupCode = null;

private class CleanupCode
{
  ~CleanupCode()
  {
    SingleInstanceClass.Cleanup();
  }
}
/// <summary>
/// Cleanup the sources that were allocated in CheckForOtherApp().
/// </summary>
static private void Cleanup()
{
  // stop the thread.
  if (singleAppComThread != null)
    singleAppComThread.CancelAsync();

  // close the event so another instance can start again.
  if (threadComEvent != null)
    threadComEvent.Close();
}
/// <summary>
///
/// </summary>
/// <param name="uniqueName"></param>
/// <returns>True is another instance is already running. False otherwise</returns>
static public Boolean CheckForOtherApp(String uniqueName)
{
  SingleAppComEventName = uniqueName;
  try
  {
    // another instance is already running
    threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName);
    threadComEvent.Set();  // signal the other instance.
    threadComEvent.Close();
    return true;    // return immediatly.
  }
  catch { /* don't care about errors */     }
  // Create the Event handle
  threadComEvent = new EventWaitHandle(false, EventResetMode.AutoReset, SingleAppComEventName);
  // make sure the resources are cleaned up afterwards.
  cleanupCode = new CleanupCode();   // <<<<<<-------
  CreateInterAppComThread();

  return false;
}

Considerations

Of course, using a thread just to check for a signal from another instance is a waste. Therefore I would suggest to integrate a call to the event checking in another thread or use this thread also for other purposes.

There is another method for signalling an application that it should show itself, and that is by using the RegisterWindowMessage() and BroadcastSystemMessage() WinAPI functions. But that is messy because one also has to add the WndProc method to the main form of the application and check there for the arrival of that message. Also, when the application is in some time consuming loop then it doesn't respond a time to that message, leaving the user clicking away to try to start a application.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
South Africa South Africa
Started in 1988 programming in Pascal, making a 68000 emulator on a DOS platform. Then from Pascal to Clipper, to C++ and now using C#. I've been programming for all walks of businesses; opticians, opthomologist, carbage collectors, reinforcement steel producers, retail chains, and more.
I'm now travelling through Africa with a home-build expedition truck.

Comments and Discussions

 
QuestionExactly what I was looking for, without adding too much code in the main method, works great! Thank you! Pin
Beerman198216-Dec-19 15:01
Beerman198216-Dec-19 15:01 
PraiseGreatm Pin
yuzaihuan27-Mar-18 17:18
yuzaihuan27-Mar-18 17:18 
QuestionThanks - works great. Pin
srand23-Nov-17 2:35
srand23-Nov-17 2:35 
AnswerMutex is another way in managing single instance of a program. Pin
Naveen Prabu ClaySys22-Sep-15 3:00
Naveen Prabu ClaySys22-Sep-15 3:00 
AnswerMutex is another way in managing single instance of a program. Pin
Naveen Prabu ClaySys22-Sep-15 3:00
Naveen Prabu ClaySys22-Sep-15 3:00 
QuestionAccept new command arguments Pin
llarose24-Sep-14 6:44
llarose24-Sep-14 6:44 
AnswerRe: Accept new command arguments Pin
AdventureDriver10-Oct-14 23:28
AdventureDriver10-Oct-14 23:28 
GeneralRe: Accept new command arguments Pin
AdventureDriver10-Oct-14 23:40
AdventureDriver10-Oct-14 23:40 
Questionhow? Pin
Umesh Kaushik29-Aug-14 22:06
Umesh Kaushik29-Aug-14 22:06 
QuestionSimpler way? Pin
Jahmal234-Apr-14 10:03
Jahmal234-Apr-14 10:03 
QuestionCan not read English . Pin
laterequalsnever30-Dec-13 14:38
laterequalsnever30-Dec-13 14:38 
GeneralI suppose that's one way to do it Pin
Kawaoneechan27-Dec-13 7:47
Kawaoneechan27-Dec-13 7:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.