Click here to Skip to main content
15,879,535 members
Articles / Desktop Programming / Windows Forms

C# Single Instance App With the Ability To Restore From System Tray (Using Mutex)

Rate me:
Please Sign up or sign in to vote.
4.80/5 (64 votes)
9 Aug 2009CPOL7 min read 255.3K   185   59
A way to prevent multiple instances of your application from opening, and focusing/activating the first instance window.

Introduction 

I have noticed that although there are other articles about Single Instance applications, there seems to be one particular niche that has not been served. What I've done here is tried to assemble wisdom from others, with gratitude for their contributions, in a way that I hope is helpful.

Background

Creating a "Single Instance" application must be a very common need because there are tons of articles about it, and not just here on The Code Project. Google for "C# single instance" and you'll find pages of links on the topic.

I wanted to implement a solution that was as SIMPLE and ELEGANT as possible, with as little code as possible. I wanted the following functionality:

  1. When the user tries to open the application a second time, a second instance does not open on the screen.
  2. Instead the first instance comes to the foreground. If it is minimized, it is restored.
  3. If the application is minimized to the SYSTEM TRAY, then it is restored from there.

(Also, I wanted multiple applications to be able to use the exact same code without having strange behaviors arise.)

I found many solutions that could meet the first two requirements, but not the third. The solutions that did meet my third requirement seemed convoluted. Do I really want my application to listen on a port or write a registry key just for this tiny bit of functionality? Seems overkill.

Finally I was able to piece together a solution, drawing ideas from a variety of sources, that is simple enough for my tastes but still meets all my requirements.  

Goal #1: Prevent a Second Instance from Opening

It seems that there are three basic approaches to accomplishing this:

  • Use a Mutex
  • Cycle through the process list to see if a process with the same name is already running
  • Use the Visual Basic system for single instance apps (which you can access from C#)

I like the MUTEX approach.

Cycling through processes seems slow, clunky, and error prone. While many articles I found advocate this approach, the real experts seem to agree that using a Mutex is better.  (For a description of the problems with using Process.GetProcessesByName() see this article: The Misunderstood Mutex.)

"Mutex" stands for Mutual Exclusion. In a nutshell, a Mutex is a program object that prevents the concurrent use of a common resource.

There are a few different ways of implementing a Mutex-based approach. Here is the way I like the best:

C#
[STAThread]
static void Main()
{
	bool onlyInstance = false;
	Mutex mutex = new Mutex(true, "UniqueApplicationName", out onlyInstance);
	if (!onlyInstance) {
		return;
	}
	Application.Run(new MainForm);
	GC.KeepAlive(mutex);
}

This approach is discussed in the article, Ensuring that only a single instance of a .NET application is running

I like this approach because it doesn't require much code. It doesn't use the WaitOne() method, because that is unnecessary

Notice the last line of code:  GC.KeepAlive()

The purpose of this is to protect the Mutex from garbage collection until the program closes. You need to somehow protect the Mutex from garbage collection, or else after your program has been running for a while the Mutex will disappear and then the user will be able to open a second instance. 

There are other ways of protecting the Mutex from garbage collection. You can use a static variable for the Mutex. Or you can do as the following article suggests, and put the Mutex in a "using" statement:  

Side note #1: The problem with "UniqueApplicationName" 

Using a Mutex does present one potential security risk. A malicious hacker could conceivably prevent your application from ever opening (a "denial of service" attack of sorts) by figuring out the name of your Mutex and creating an application that takes the Mutex first. This doesn't seem like a huge concern to me, but it is possible. Moreover, it's possible that two applications could accidentally have the same name, or that a developer could re-use Mutex code without changing the Mutex name. Using a GUID for the Mutex name will help prevent this sort of thing from happening. My attached sample code shows how to do so.

Side note #2: Mutex scope 

To prevent multiple instances across sessions on the same machine, preface the Mutex name with Global\ (e.g. mutexName = @"Global\" + myGuid; )  

Goal #2: Activate the First Instance  

You can use WinAPI functions to activate the window and bring it to the foreground.

First you need to get the handle of the window. I've seen a few different approaches to this (like cycling through the process list or enumerating windows). In a previous version of this article I used the FindWindow() API function, but I've since learned there are drawbacks to using that function. 

For a discussion of why FindWindow() is evil, see the following article:

The example code below still uses FindWindow(), but later in the article I'll show a better way.

C#
[DllImportAttribute ("user32.dll")]
public static extern IntPtr FindWindow (string lpClassName, string lpWindowName);
	
[DllImportAttribute ("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
	
[DllImportAttribute ("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
	
public static void ShowToFront(string windowName)
{
	IntPtr firstInstance = FindWindow(null, windowName);
	ShowWindow(firstInstance, 1);
	SetForegroundWindow(firstInstance);
}

Then just insert a call to ShowToFront() in the following code:

C#
if (!onlyInstance) {
	ShowToFront(applicationName);
	return;
} 

Goal #3. If the First Instance is Minimized to the System Tray (aka "Notification Area"), Restore It

This is where it gets tricky. Rather than using WinAPI functions in the second instance to grab the first instance and force it out of hiding, what I really wanted was a way for the second instance to signal the first instance that it should show itself.

How can one instance of my application communicate with another? The solutions I found seemed too complex. One used Memory Mapped Files (a way to share memory between applications). Another used Remoting (a way for applications to communicate via a socket, your standard network client/server stuff).

Then I found the article, C# .NET Single Instance Application.

I don't like the author's way of doing the Mutex, but his idea of using PostMessage is brilliant. It requires far less code and seems far more elegant than Remoting.

Some care must be made when using HWND_BROADCAST. If you broadcast a message code that other applications use internally for their own purposes, then wacky behavior could ensue. One way to avoid this is to use RegisterWindowMessage(). This will generate a message code that is guaranteed to be unique.

We want to create code that can be re-used in multiple applications. Therefore, the call to RegisterWindowMessage() must include application specific text. One way to accomplish this is to append the assembly GUID to the name of the message.

Putting It All Together

I put all my WinAPI stuff in a separate class called WinApi.

C#
static public class WinApi
{
	[DllImport("user32")]
	public static extern int RegisterWindowMessage(string message);
	
	public static int RegisterWindowMessage(string format, params object[] args)
	{
		string message = String.Format(format, args);
		return RegisterWindowMessage(message);
	}

	public const int HWND_BROADCAST = 0xffff;
        public const int SW_SHOWNORMAL = 1;
	
	[DllImport("user32")]
        public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

	[DllImportAttribute ("user32.dll")]
	public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
	
	[DllImportAttribute ("user32.dll")]
	public static extern bool SetForegroundWindow(IntPtr hWnd);
	
	public static void ShowToFront(IntPtr window)
	{
		ShowWindow(window, SW_SHOWNORMAL);
		SetForegroundWindow(window);
	}
}

And I put all the Mutex code in a separate class called SingleInstance.

static public class SingleInstance
{
    public static readonly int WM_SHOWFIRSTINSTANCE =
        WinApi.RegisterWindowMessage("WM_SHOWFIRSTINSTANCE|{0}", ProgramInfo.AssemblyGuid);
    static Mutex mutex;
    static public bool Start()
    {
        bool onlyInstance = false;
        string mutexName = String.Format("Local\\{0}", ProgramInfo.AssemblyGuid);

        // if you want your app to be limited to a single instance
        // across ALL SESSIONS (multiple users & terminal services), then use the following line instead:
        // string mutexName = String.Format("Global\\{0}", ProgramInfo.AssemblyGuid);
        
        mutex = new Mutex(true, mutexName, out onlyInstance);
        return onlyInstance;
    }
    static public void ShowFirstInstance()
    {
        WinApi.PostMessage(
            (IntPtr)WinApi.HWND_BROADCAST,
            WM_SHOWFIRSTINSTANCE,
            IntPtr.Zero,
            IntPtr.Zero);
    }
    static public void Stop()
    {
        mutex.ReleaseMutex();
    }
}

The Mutex name and the call to RegisterWindowMessage() both require an application-specific GUID. I use a separate class called ProgramInfo for this. ProgramInfo.AssemblyGuid gets the GUID that is automatically associated with the assembly.

static public class ProgramInfo
{
      static public string AssemblyGuid
      {
        get
        {
            object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Runtime.InteropServices.GuidAttribute), false);
            if (attributes.Length == 0) {
                return String.Empty;
            }
            return ((System.Runtime.InteropServices.GuidAttribute)attributes[0]).Value;
        }
      }
} 

To make all this code work in your application, you just need to do two things.

Firstly, in your Main() function, you must call the functions from SingleInstance ... Start(), ShowFirstInstance(), and Stop().

static class Program
{
	[STAThread]
	static void Main()
	{
		if (!SingleInstance.Start()) {
			SingleInstance.ShowFirstInstance();
			return;
		}
		
		Application.EnableVisualStyles();
		Application.SetCompatibleTextRenderingDefault(false);

		try {
			MainForm mainForm = new MainForm();
			Application.Run(mainForm);
		} catch (Exception e) {
			MessageBox.Show(e.Message);
		}
		
		SingleInstance.Stop();
	}
} 

And secondly within your main form, the following code must be added:

C#
protected override void WndProc(ref Message message)
{
	if (message.Msg == SingleInstance.WM_SHOWFIRSTINSTANCE) {
		ShowWindow();
	}
	base.WndProc(ref message);
} 
C#
public void ShowWindow()
{
	// Insert code here to make your form show itself.
	WinApi.ShowToFront(this.Handle);
}
 

See what's happening here? Instead of having Instance #2 use FindWindow() to find and show the window for Instance #1, it broadcasts a message that says "show yourself." Instance #1 hears that message, and then shows itself.

If your application is minimized to the system tray, ShowWindow is where you put the code to restore it. This sample code shows one way to accomplish that.

Going Further

Some people need to go a step further and pass command line arguments from the second instance to the first instance. If you need to accomplish that, then it seems you have two options:

  1. You can use the Visual Basic system for single instance apps (which is accessible from C#) as described in this article.
  2. Or you can use a strictly C# solution that uses something like Remoting to communication between the instances. The best article I've found on that is this article.

History 

  • 29th January, 2009: Initial post
  • 9th August, 2009: Major revision, got rid of FindWindow()

License

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


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionomgsh thankful!!! Pin
iman noavar23-Jan-22 21:19
iman noavar23-Jan-22 21:19 
QuestionThank you Pin
Member 1529173616-Jul-21 4:52
Member 1529173616-Jul-21 4:52 
QuestionTKS! Pin
Ronye Lago Rocha10-Mar-21 14:45
Ronye Lago Rocha10-Mar-21 14:45 
QuestionGC.KeepAlive for Mutex. Pin
Danny Hutch4-Jul-19 14:24
Danny Hutch4-Jul-19 14:24 
AnswerRe: GC.KeepAlive for Mutex. Pin
Danny Hutch4-Jul-19 14:42
Danny Hutch4-Jul-19 14:42 
PraiseBest solution I've found for this scenario Pin
Tom Toolman10-Feb-19 9:35
Tom Toolman10-Feb-19 9:35 
GeneralMy vote of 5 Pin
Sylwester Filaber14-Dec-17 6:28
Sylwester Filaber14-Dec-17 6:28 
QuestionRunning multiple instances with same GUID Pin
Member 443327429-May-17 21:42
Member 443327429-May-17 21:42 
QuestionA Big 5 Pin
ricmil4211-Mar-16 8:41
ricmil4211-Mar-16 8:41 
Questionnice job Pin
srilekhamenon10-Mar-16 1:28
professionalsrilekhamenon10-Mar-16 1:28 
PraiseThanks Pin
Ali Bagherzadeh17-Nov-15 21:09
Ali Bagherzadeh17-Nov-15 21:09 
QuestionGreat job Pin
JazzIsBack11-Jun-15 4:02
JazzIsBack11-Jun-15 4:02 
QuestionPostMessage(), return value, EnumWindows Pin
JulianY11-Aug-14 7:36
JulianY11-Aug-14 7:36 
QuestionExcellent code, however SendMessage() worked better than PostMessage() in my case. Pin
Sebastian Sośnik20-Jun-14 5:02
Sebastian Sośnik20-Jun-14 5:02 
AnswerRe: Excellent code, however SendMessage() worked better than PostMessage() in my case. Pin
tiggergreen12-Aug-20 8:54
tiggergreen12-Aug-20 8:54 
QuestionWin7 and Tray Pin
jjborella10-May-14 9:40
jjborella10-May-14 9:40 
AnswerRe: Win7 and Tray Pin
Member 1177708630-Jul-15 22:09
Member 1177708630-Jul-15 22:09 
Questionquestion about this code Pin
Member 107195802-Apr-14 9:13
Member 107195802-Apr-14 9:13 
AnswerRe: question about this code Pin
tiggergreen12-Aug-20 8:55
tiggergreen12-Aug-20 8:55 
GeneralMy vote of 5 Pin
cnaquoc16-Feb-14 20:12
cnaquoc16-Feb-14 20:12 
QuestionPass arguments Pin
watruba14-Feb-14 18:42
watruba14-Feb-14 18:42 
GeneralMy vote of 5 Pin
CaaSiOn5-Aug-13 23:59
CaaSiOn5-Aug-13 23:59 
SuggestionWPF Pin
Şafak Gür9-Jul-13 21:34
Şafak Gür9-Jul-13 21:34 
GeneralMy vote of 5 Pin
CDFaux13-Jun-13 9:54
CDFaux13-Jun-13 9:54 
AnswerPerfect! Pin
Member 977887320-Mar-13 4:13
Member 977887320-Mar-13 4:13 

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.