Click here to Skip to main content
15,867,885 members
Articles / Programming Languages / C#
Article

Single Instance Application (With a Twist)

Rate me:
Please Sign up or sign in to vote.
4.56/5 (12 votes)
14 Mar 2007CPOL5 min read 47.5K   431   43   9
A reusable class to support single instance applications
Screenshot - SingleInstance.png

Introduction

This article shows yet another technique on how to include support for activating an already running instance of your application when a user tries to start a new instance.

The differences between this technique and others I've found on The Code Project are:

  1. Support for scenarios where you sometimes want to start a new instance even if an existing instance is already running
  2. Send data to the existing application when a new instance tries to start

To understand the implementation, the article discusses the following subjects:

  1. Using a mutex to determine if the application is already running
  2. Using .NET NativeWindow class to implement a Windows message loop without requiring a Form
  3. Sending messages between applications using WM_COPYDATA.

Background

The class described here is used in an open source project called Terminals that me and a friend are working on. The project is a multi-tabbed RDP client that can be found here.

The Terminals application allows you to pass a server name on the command line so it can connect to it as soon as the application starts (i.e. Terminals.exe Server1). One of our users requested that if a new instance is started with a command line (i.e. Terminals.exe server2) that the new connection will be opened in a new tab instead of starting a new process. We also wanted to still support a scenario where a user starts a new instance.

Note: This works exactly the way Internet Explorer 7 does. If you enter the URL of a site in the Start->Run command, it will open in a new tab if Internet Explorer is already running, but you can still start new instances if you start Internet Explorer directly from its shortcut.

Using the Code

The class is implemented as a singleton with static methods for ease of use. Start by calling SingleInstanceApplication.NotifyExistingInstance(). This will check if another instance already exists and if so, it will send it the data passed to the function. The data can be any .NET object that is serializable.

C#
if (SingleInstanceApplication.NotifyExistingInstance("Some data"))
   return;

Next, if another instance does not already exist, you should call the Initialize() method:

C#
SingleInstanceApplication.Initialize(); 

If you want to get notifications when a new instance is started, subscribe to the NewInstanceMessage event:

C#
{
  SingleInstanceApplication.NewInstanceMessage += new NewInstanceMessageEventHandler(
    SingleInstanceApplication_NewInstanceMessage);
}

void SingleInstanceApplication_NewInstanceMessage(object sender, 
  object message)
{
  //cast the message object to its real type and use it
}

The Details

Ok, let's dive into the implementation details, taking one step at a time.

Determining If Another Instance is Already Running

The easiest way to determine if another instance is already running is simply to create a Mutex object when the application starts. The nice thing about Mutexes is that when you create them, you can:

  1. Give them a name so they will be accessible from more than 1 process.
  2. Provide an out parameter to their constructor that will be true if this is the first instance if the mutex.

So basically, we do this:

C#
_instanceCounter = new Mutex(false, _id, out _firstInstance);

_Id is the name of the Mutex which is actually the name of the application EXE. _firstInstance will be set to true if this is the first instance of the Mutex. Notice that the first parameter to the Mutex constructor determines whether to lock the mutex. We don't want to lock it - we only care whether it already exists or not.

Notifying the Existing Instance that Another Instance was Started

Great. So now whenever a new instance starts, it checks the _firstInstance variable and knows if another instance was already started. If so, we need to notify the other instance and possibly provide it with some information. (In the case of the Terminals project mentioned above, we needed to pass the command line parameters we received).

So how do we notify the other window? I've seen some solutions using Remoting, some using shared memory and some ignoring the problem altogether. I decided to use the simplest Windows mechanism to communicate between processes: Windows Messages.

The idea is to create our own hidden window that will have a title with a unique id that we can identify and send a message to that window. How do you create a hidden window in .NET without creating a Form? Simple: Use the NativeWindow class.

What you need to do is create a subclass of NativeWindow and override its WndProc() method:

C#
class SIANativeWindow : NativeWindow
{
  public SIANativeWindow()
  {
    CreateParams cp = new CreateParams();
    cp.Caption = _theInstance._id; //The window title is the same as the Id
    CreateHandle(cp);
  }

  //The window procedure that handles notifications from new application instances
  protected override void WndProc(ref Message m)
  {
    //handle window messages here
  }
}

So, we have a hidden window that can accept messages. Which message should we send it? Since we need to pass data and not the usual Int, Long parameters that are usually passed with messages, the perfect fit is WM_COPYDATA. This message was created exactly for that purpose - to pass data between applications.

Sending Data between Applications using WM_COPYDATA

Let's get to work then. The first thing we need to do is find our hidden window:

C#
IntPtr handle = NativeMethods.FindWindow(null, _id);

We serialize the object we want to send to a byte array and now we need to fill the WM_COPYDATA structures with the correct information. Since we are using native Windows APIs to send the message and since our byte array is a .NET object, we first need to Pin our object in memory and get a "native" pointer to it:

C#
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
...
//get the address of the pinned buffer
data.lpData = bufferHandle.AddrOfPinnedObject();

And now we just need to send the data to our window:

C#
NativeMethods.SendMessage
    (handle, NativeMethods.WM_COPYDATA, IntPtr.Zero, dataHandle.AddrOfPinnedObject());

Handling the WM_COPYDATA on the Other End of the Pipe

Ok now - The application was found, the window was found, the message was sent. The last thing we have to do is actually handle that message in the hidden window we created above.
Again, since the message arrives as a native WM_COPYDATA structure, we first have to convert it to a .NET byte array and then deserialize the original object.

C#
//convert the message LParam to the WM_COPYDATA structure
COPYDATASTRUCT data = (COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, 
  typeof(COPYDATASTRUCT));

//copy the native byte array to a .NET byte array
byte[] buffer = new byte[data.cbData];
Marshal.Copy(data.lpData, buffer, 0, buffer.Length);

//deserialize the buffer to a new object
obj = Deserialize(buffer);

We now have a copy of the object that was sent from the new application instance. We can activate our window, do something with the information passed to us (e.g. create a new tab and connect to another server as in the Terminals application).

Conclusion

This class allows you create a "Singleton" application and pass information between new instances of the application and existing instances. It uses very simple mechanisms to determine if the application is already running (Mutex) and to communicate between applications (Windows messages).

Links

History

  • 14th March, 2007: Initial post

License

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


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

Comments and Discussions

 
GeneralMinor suggestions for GetAppId() Pin
kknight4718-Apr-07 5:34
kknight4718-Apr-07 5:34 
GeneralI used it, Very Nice! Thanks! Pin
wulang17-Mar-07 4:04
wulang17-Mar-07 4:04 
QuestionWindowsFormsApplicationBase, easier? Pin
Stephen Brannan14-Mar-07 16:12
Stephen Brannan14-Mar-07 16:12 
AnswerRe: WindowsFormsApplicationBase, easier? Pin
Eyal Post14-Mar-07 20:41
Eyal Post14-Mar-07 20:41 
GeneralRe: WindowsFormsApplicationBase, easier? Pin
Stephen Brannan15-Mar-07 7:02
Stephen Brannan15-Mar-07 7:02 
I've used it in several production applications without any such issues. As for using the tcp channel, overkill or not, the slight performance lost is outweighed by the library's ease of use.
GeneralRe: WindowsFormsApplicationBase, easier? Pin
Kenrae13-May-08 23:50
Kenrae13-May-08 23:50 
GeneralRe: WindowsFormsApplicationBase, easier? Pin
M.Lansdaal19-Mar-07 10:04
M.Lansdaal19-Mar-07 10:04 
GeneralRe: WindowsFormsApplicationBase, easier? Pin
Eyal Post19-Mar-07 11:15
Eyal Post19-Mar-07 11:15 
GeneralRe: WindowsFormsApplicationBase, easier? Pin
Richard Deeming21-Mar-07 9:57
mveRichard Deeming21-Mar-07 9:57 

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.