Saturday, March 27, 2010

Single Instance Application in C# (per session)

Sometimes you want to only allow for a single instance of your application and that can be accomplished by using a Mutex class. On top of that, if it’s used on a Terminal Server / Citrix, you also want to check the session id, to ensure you’re not interfering with other user’s instances or even the same user on a different connection.

EDIT: On a second read, the SID might not be needed in the mutex name, as by default the mutex is local, unless told otherwise, see note below:

http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx

* * *

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); 
 
[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
static extern bool SetForegroundWindow(IntPtr hWnd);
 
// Constants 
const int SW_RESTORE = 9;
 
/// <summary> 
/// The main entry point for the application. 
/// </summary> 
[STAThread] 
static void Main() 
{ 
  // 
  // Use a Mutex to allow only one instance per session...  
  // 
  bool   newInstance = true; 
  String mutexId     = String.Format("APP={0}; SID={1}",
                                     Assembly.GetExecutingAssembly().GetName().Name, 
                                     System.Diagnostics.Process.GetCurrentProcess().SessionId); 
 
  Mutex mutex = new System.Threading.Mutex(true, mutexId, out newInstance);  
  if (!newInstance) {
    MessageBox.Show("Application Already Running!" + mutexId);
 
    // Find the instance holding the mutex and restore...  
    Process currentProcess = Process.GetCurrentProcess();  
    foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))  
    {  
      if (process.Id        != currentProcess.Id &&  
          process.SessionId == currentProcess.SessionId)  
      { 
        // Restore window (if minimized)... 
        ShowWindow(process.MainWindowHandle, SW_RESTORE); 
 
        // Set foreground window... 
        SetForegroundWindow(process.MainWindowHandle);
 
        break;  
      }  
    }  
    return;  
  }
 
  //  
  // Run normal...  
  //  
  Application.EnableVisualStyles();  
  Application.SetCompatibleTextRenderingDefault(false);  
  Application.Run(new MainForm()); 
 
  //
  // Hold the mutex from being garbage collected... 
  //
  GC.KeepAlive(mutex);
}

Resources:

6 comments :

  1. thank you so much, this code help me to create a very good solution

    ReplyDelete
  2. You might want to cut the lines:

    // Hold the mutex from being garbage collected...
    GC.KeepAlive(mutex);

    And paste them at the bottom of this method - KeepAlive() only promises to keep the object alive *until* this particular call and not after it.

    Reference:
    http://msdn.microsoft.com/en-us/library/system.gc.keepalive.aspx

    ReplyDelete
  3. Thanks for that, I've changed the code, although I've seen it work fine before, it makes sense though to change it.

    ReplyDelete
  4. Works great but how do I restore a window from the system tray?

    ReplyDelete
  5. If your window was hidden, you might want to call ShowWindow(hwnd, SW_SHOW) instead or prior to SW_RESTORE.

    See if the comments at ShowWindow page help:
    http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx

    ReplyDelete
  6. It helped me. great work

    Navan M

    ReplyDelete