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.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace SingleInstanceApp
{
static class Program
{
[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.
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
{
private static String SingleAppComEventName = "Make up some unique name here, suggestion: use a GUID";
private static BackgroundWorker singleAppComThread = null;
private static EventWaitHandle threadComEvent = null;
[STAThread]
static void Main()
{
try
{
threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName);
threadComEvent.Set(); threadComEvent.Close();
return; }
catch { }
threadComEvent = new EventWaitHandle(false, EventResetMode.AutoReset, SingleAppComEventName);
CreateInterAppComThread();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
singleAppComThread.CancelAsync();
while (singleAppComThread.IsBusy)
Thread.Sleep(50);
threadComEvent.Close();
}
}
}
What has changed and why?
There are three extra using lines, for obvious reasons.
using System.Threading;
using System.ComponentModel;
using System.Runtime.InteropServices;
In the program class these three properties are added:
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.
try
{
threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName);
threadComEvent.Set(); threadComEvent.Close();
return; }
catch { }
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.
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
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
static private void singleAppComThread_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
WaitHandle[] waitHandles = new WaitHandle[] { threadComEvent };
while (!worker.CancellationPending)
{
if (WaitHandle.WaitAny(waitHandles, 1000) == 0)
{
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
static private void ThreadFormVisable(Form frm)
{
if (frm != null)
{
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:
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
{
private static String SingleAppComEventName = "Make up some unique name here, suggestion: use a GUID";
private static BackgroundWorker singleAppComThread = null;
private static EventWaitHandle threadComEvent = null;
[STAThread]
static void Main()
{
try
{
threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName);
threadComEvent.Set(); threadComEvent.Close();
return; }
catch { }
threadComEvent = new EventWaitHandle(false, EventResetMode.AutoReset, SingleAppComEventName);
CreateInterAppComThread();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
singleAppComThread.CancelAsync();
while (singleAppComThread.IsBusy)
Thread.Sleep(50);
threadComEvent.Close();
}
static private void CreateInterAppComThread()
{
singleAppComThread = new BackgroundWorker();
singleAppComThread.WorkerReportsProgress = false;
singleAppComThread.WorkerSupportsCancellation = true;
singleAppComThread.DoWork += new DoWorkEventHandler(singleAppComThread_DoWork);
singleAppComThread.RunWorkerAsync();
}
static private void singleAppComThread_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
WaitHandle[] waitHandles = new WaitHandle[] { threadComEvent };
while (!worker.CancellationPending)
{
if (WaitHandle.WaitAny(waitHandles, 1000) == 0)
{
if (Application.OpenForms.Count > 0)
{
Form mainForm = Application.OpenForms[0];
mainForm.Invoke(new SetFormVisableDelegate(ThreadFormVisable), mainForm);
}
}
}
}
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private delegate void SetFormVisableDelegate(Form frm);
static private void ThreadFormVisable(Form frm)
{
if (frm != null)
{
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:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace SingleInstanceApp2
{
static class Program
{
[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:
private static CleanupCode cleanupCode = null;
private class CleanupCode
{
~CleanupCode()
{
SingleInstanceClass.Cleanup();
}
}
static private void Cleanup()
{
if (singleAppComThread != null)
singleAppComThread.CancelAsync();
if (threadComEvent != null)
threadComEvent.Close();
}
static public Boolean CheckForOtherApp(String uniqueName)
{
SingleAppComEventName = uniqueName;
try
{
threadComEvent = EventWaitHandle.OpenExisting(SingleAppComEventName);
threadComEvent.Set(); threadComEvent.Close();
return true; }
catch { }
threadComEvent = new EventWaitHandle(false, EventResetMode.AutoReset, SingleAppComEventName);
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.