DCSIMG
Single Instance Application Manager - Maxim

Single Instance Application Manager

Some .Net applications are required to run as single instance process (at startup, each process “ensures” that he’s unique). This requirement may come from licensing issues, technical and/or other reasons. Quick search in Google will provide a variety of solutions, most of them are based on WindowsFormsApplicationBase object from VB.Net, usage of Mutex object and other techniques. I don’t like mixing VB with C#, and also Win-Forms solution isn’t “pure” enough for WPF applications. So I wrote my version of “Single Instance Manager”…

This post will provide short description about this small project and links to download the sources.

How to Create Single Instance Application for WPF, Win-Forms and Console Applications

The manager uses well-known ThreadPool object with WaitEventHandle object and IpcClientChannel to pass messages (objects) between processes (.Net Remoting).

Diagram: Pass object cross processes

[In Diagram: Instance A passes Console Arguments to Process B, before it closes it self]

Each application will use “ApplicationInstanceManager.CreateSingleInstance(…)” to register it self as single instance application.

image

[In Diagram: Description of “CreateSingleInstance(…)” Function]

The manager ensures that application has only one instance and also passes console arguments from new instances to running process, before “killing” them.

   1:  using System;
   2:  using System.Diagnostics;
   3:  using System.Runtime.Remoting;
   4:  using System.Runtime.Remoting.Channels;
   5:  using System.Runtime.Remoting.Channels.Ipc;
   6:  using System.Threading;
   7:   
   8:  namespace SingleInstanceApplication
   9:  {
  10:      /// <summary>
  11:      /// Application Instance Manager
  12:      /// </summary>
  13:      public static class ApplicationInstanceManager
  14:      {
  15:          /// <summary>
  16:          /// Creates the single instance.
  17:          /// </summary>
  18:          /// <param name="name">The name.</param>
  19:          /// <param name="callback">The callback.</param>
  20:          /// <returns></returns>
  21:          public static bool CreateSingleInstance(string name, EventHandler<InstanceCallbackEventArgs> callback)
  22:          {
  23:              EventWaitHandle eventWaitHandle = null;
  24:              string eventName = string.Format("{0}-{1}", Environment.MachineName, name);
  25:   
  26:              InstanceProxy.IsFirstInstance = false;
  27:              InstanceProxy.CommandLineArgs = Environment.GetCommandLineArgs();
  28:   
  29:              try
  30:              {
  31:                  // try opening existing wait handle
  32:                  eventWaitHandle = EventWaitHandle.OpenExisting(eventName);
  33:              }
  34:              catch
  35:              {
  36:                  // got exception = handle wasn't created yet
  37:                  InstanceProxy.IsFirstInstance = true;
  38:              }
  39:   
  40:              if (InstanceProxy.IsFirstInstance)
  41:              {
  42:                  // init handle
  43:                  eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
  44:   
  45:                  // register wait handle for this instance (process)
  46:                  ThreadPool.RegisterWaitForSingleObject(eventWaitHandle, WaitOrTimerCallback, callback, Timeout.Infinite, false);
  47:                  eventWaitHandle.Close();
  48:   
  49:                  // register shared type (used to pass data between processes)
  50:                  RegisterRemoteType(name);
  51:              }
  52:              else
  53:              {
  54:                  // pass console arguments to shared object
  55:                  UpdateRemoteObject(name);
  56:   
  57:                  // invoke (signal) wait handle on other process
  58:                  if (eventWaitHandle != null) eventWaitHandle.Set();
  59:   
  60:   
  61:                  // kill current process
  62:                  Environment.Exit(0);
  63:              }
  64:   
  65:              return InstanceProxy.IsFirstInstance;
  66:          }
  67:   
  68:          /// <summary>
  69:          /// Updates the remote object.
  70:          /// </summary>
  71:          /// <param name="uri">The remote URI.</param>
  72:          private static void UpdateRemoteObject(string uri)
  73:          {
  74:              // register net-pipe channel
  75:              var clientChannel = new IpcClientChannel();
  76:              ChannelServices.RegisterChannel(clientChannel, true);
  77:   
  78:              // get shared object from other process
  79:              var proxy =
  80:                  Activator.GetObject(typeof(InstanceProxy), 
  81:                  string.Format("ipc://{0}{1}/{1}", Environment.MachineName, uri)) as InstanceProxy;
  82:   
  83:              // pass current command line args to proxy
  84:              if (proxy != null)
  85:                  proxy.SetCommandLineArgs(InstanceProxy.IsFirstInstance, InstanceProxy.CommandLineArgs);
  86:   
  87:              // close current client channel
  88:              ChannelServices.UnregisterChannel(clientChannel);
  89:          }
  90:   
  91:          /// <summary>
  92:          /// Registers the remote type.
  93:          /// </summary>
  94:          /// <param name="uri">The URI.</param>
  95:          private static void RegisterRemoteType(string uri)
  96:          {
  97:              // register remote channel (net-pipes)
  98:              var serverChannel = new IpcServerChannel(Environment.MachineName + uri);
  99:              ChannelServices.RegisterChannel(serverChannel, true);
 100:   
 101:              // register shared type
 102:              RemotingConfiguration.RegisterWellKnownServiceType(
 103:                  typeof(InstanceProxy), uri, WellKnownObjectMode.Singleton);
 104:   
 105:              // close channel, on process exit
 106:              Process process = Process.GetCurrentProcess();
 107:              process.Exited += delegate { ChannelServices.UnregisterChannel(serverChannel); };
 108:          }
 109:   
 110:          /// <summary>
 111:          /// Wait Or Timer Callback Handler
 112:          /// </summary>
 113:          /// <param name="state">The state.</param>
 114:          /// <param name="timedOut">if set to <c>true</c> [timed out].</param>
 115:          private static void WaitOrTimerCallback(object state, bool timedOut)
 116:          {
 117:              // cast to event handler
 118:              var callback = state as EventHandler<InstanceCallbackEventArgs>;
 119:              if (callback == null) return;
 120:   
 121:              // invoke event handler on other process
 122:              callback(state,
 123:                       new InstanceCallbackEventArgs(InstanceProxy.IsFirstInstance,
 124:                                                     InstanceProxy.CommandLineArgs));
 125:          }
 126:      }
 127:  }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

For testing purposes I added form with grid that allows starting processes with console arguments:

single instance manager

How can we use this manager (for example in WPF app):

   1:  using System;
   2:  using System.Reflection;
   3:  using System.Windows;
   4:   
   5:  namespace SingleInstanceApplication.WpfApp
   6:  {
   7:      /// <summary>
   8:      /// Interaction logic for App.xaml
   9:      /// </summary>
  10:      public partial class App
  11:      {
  12:          /// <summary>
  13:          /// Raises the <see cref="E:System.Windows.Application.Startup"/> event.
  14:          /// </summary>
  15:          /// <param name="e">A <see cref="T:System.Windows.StartupEventArgs"/> that contains the event data.</param>
  16:          protected override void OnStartup(StartupEventArgs e)
  17:          {
  18:              // register single instance app. and check for existence of other process
  19:              if (!ApplicationInstanceManager.CreateSingleInstance(
  20:                      Assembly.GetExecutingAssembly().GetName().Name,
  21:                      SingleInstanceCallback)) return; // exit, if same app. is running
  22:   
  23:              base.OnStartup(e);
  24:          }
  25:   
  26:          /// <summary>
  27:          /// Raises the <see cref="E:System.Windows.Application.Activated"/> event.
  28:          /// </summary>
  29:          /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
  30:          protected override void OnActivated(EventArgs e)
  31:          {
  32:              base.OnActivated(e);
  33:   
  34:              var win = MainWindow as MainWindow;
  35:              if (win == null) return;
  36:   
  37:              // add 1st args
  38:              win.ApendArgs(Environment.GetCommandLineArgs());
  39:          }
  40:   
  41:          /// <summary>
  42:          /// Single instance callback handler.
  43:          /// </summary>
  44:          /// <param name="sender">The sender.</param>
  45:          /// <param name="args">The <see cref="SingleInstanceApplication.InstanceCallbackEventArgs"/> instance containing the event data.</param>
  46:          private void SingleInstanceCallback(object sender, InstanceCallbackEventArgs args)
  47:          {
  48:              if (args == null || Dispatcher == null) return;
  49:              Action<bool> d = (bool x) => 
  50:              {
  51:                  var win = MainWindow as MainWindow;
  52:                  if (win == null) return;
  53:   
  54:                  win.ApendArgs(args.CommandLineArgs);
  55:                  win.Activate(x);
  56:              };
  57:              Dispatcher.Invoke(d, true);
  58:          }
  59:      }
  60:  }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

[As you can see “App” has “SingleInstanceCallback” that is fired by manager, also you can see function overload for “Window.Actvate” (see this post about this extention)]

VS2010 Sources for this project can be downloaded from here (blog) or here (codeplex).
DLL + PDB Assembly with Library can be downloaded from here (blog) or here (codeplex).
image Project’s site at Codeplex: …

 Remarks: Any comments/improvements will be accepted with pleasure. Of course, you must understand that this code is a contribution and I’m not responsible for any damage that may be caused by using it. All rights reserved ®.

Comments

# re: Single Instance Application Manager

Monday, February 22, 2010 8:41 PM by BladeWise

This solution does not take into account multiple instances on multiple sessions.

If you try to start a second instance of the same application (I tried the WPF one) on another session, the application crashes on

private static void RegisterRemoteType(string uri)

when trying to create the IpcServerChannel.

It 'thinks' to be the first instance, while it is not, and since the IpcServerChannel is already present, it thows a RemotingException.

I even think that a bit more code for synchronization is needed to avoid race conditions, and some more try/finally blocks are required to avoid that a ThreadAbortException causes remoting channel server to be not closed.

# re: Single Instance Application Manager

Tuesday, February 23, 2010 11:18 PM by Maxim

Thanks. I'll fix it :)

# re: Single Instance Application Manager

Monday, March 15, 2010 5:11 PM by Chris

Hi, good article. I liked the pretty diagrams, it looks very professional :-) .

On an unrelated subject: Did you fix the error?

# Writing a single instance application in WPF -we are righteous

Pingback from  Writing a single instance application in WPF -we are righteous

# re: Single Instance Application Manager

Tuesday, April 06, 2010 5:00 AM by César F. Qüeb

Awesome article!...

Could you share with the developer community the fixed solution (with the second instance on other session), please?...

Thank you...!, very, very useful...

Regards

# re: Single Instance Application Manager

Monday, May 03, 2010 11:15 AM by saurabh

I want to open a other instance when i double click on some exe. So how to do it.?

# re: Single Instance Application Manager

Thursday, January 27, 2011 5:46 PM by glenax

A simple and quick solution:

OnStart {

Try

-OpenPort(1234)

Catch

-App is running

-Terminate

End Try

}

OnFirstInstance_Terminate {

-ClosePort(1234)

}

# re: Single Instance Application Manager

Thursday, August 04, 2011 8:13 PM by Hex

Unfortunately it doesn't work on linux (mono):

Could not attach to process. If your uid matches the uid of the target

process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try

again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf

ptrace: Operation not allowed.

at (wrapper managed-to-native) System.Threading.WaitHandle.WaitAny_internal (System.Threading.WaitHandle[],int,bool) <0x00004>

at (wrapper managed-to-native) System.Threading.WaitHandle.WaitAny_internal (System.Threading.WaitHandle[],int,bool) <0x00004>

at System.Threading.WaitHandle.WaitAny (System.Threading.WaitHandle[],System.TimeSpan,bool) <0x000fe>

at System.Threading.RegisteredWaitHandle.Wait (object) <0x00094>

at (wrapper runtime-invoke) object.runtime_invoke_void__this___object (object,intptr,intptr,intptr) <0x00046>

Any ideas?

# re: Single Instance Application Manager

Wednesday, November 23, 2011 10:21 PM by dklawson

I think you have a race condition in your code.

In CreateSingleInstance(), you are testing for the existence of the event handle and then creating it a couple statements later. This is non-atomic. Two different processes could get the same answer to EventWaitHandle.OpenExisting() and think they are both the first instance.

Instead you should use the EventWaitHandle constructor with the createdNew output parameter:

eventWaitHandle = EventWaitHandle(false, EventResetMode.AutoReset, eventName, InstanceProxy.IsFirstInstance);

This will create/retrieve the event handle and tell if you were first or not in a single operation. No try/catch necessary.

# re: Single Instance Application Manager

Thursday, December 01, 2011 11:47 PM by Brian J

Awesome. This worked like a charm for me and was exactly what I was looking for. I was going crazy trying to figure out how to pass on the arguments from the second app.

Thanks!!

# re: Single Instance Application Manager

Sunday, May 20, 2012 1:20 PM by 1

-1'

# re: Single Instance Application Manager

Sunday, July 08, 2012 5:46 AM by 1

-1'

# re: Single Instance Application Manager

Thursday, July 19, 2012 10:59 AM by 1

-1'

# re: Single Instance Application Manager

Monday, December 31, 2012 4:52 AM by yisman

excellent!

# re: Single Instance Application Manager

Tuesday, January 01, 2013 1:26 AM by Hembree

This is my first time pay a quick visit at here and

i am genuinely happy to read all at single place.

# re: Single Instance Application Manager

Friday, March 15, 2013 10:32 AM by Saša

I have used this pattern in my application and it works generaly, but sometimes I get this exception:

Unhandled exception!

System.Runtime.Remoting.RemotingException: Failed to read from an IPC Port: The pipe has been ended.

Server stack trace:

at System.Runtime.Remoting.Channels.Ipc.IpcPort.Read(Byte[] data, Int32 offset, Int32 length)

at System.Runtime.Remoting.Channels.Ipc.Pipe5tream.Read(Byte[] buffer, Int32 offset, Int32 size)

at System.Runtime.Remoting.Channels.5ocketHandler.ReadFromSocket(Byte[] buffer, Int32 offset, Int32 count)

at System.Runtime.Remoting.Channels.SocketHandler.Read(Byte[] buffer, Int32 offset, Int32 count)

at System.Runtime.Remoting.Channels.SocketHandler.ReadAndMatchFourBytes(Byte[] buffer)

at System.Runtime.Remoting.Channels.Tcp.Tcp5ocketHandler.ReadAndMatchPreamble()

at System.Runtime.Remoting.Channels.Tcp.Tcp5ocketHandler.ReadVersionAnd0peration(UInt16& operation)

at System.Runtime.Remoting.Channels.Ipc.IpcClientHandler.ReadHeaders()

at System.Runtime.Remoting.Channels.Ipc.IpcClientTransport5ink.ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream

request5tream, ITransportHeaders& responseHeaders, 5tream& response5tream)

at System.Runtime.Remoting.Channels.BinaryClientFormatter5ink.SyncProcessMessage(IMessage msg)

Exception rethrown at [U]:

at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)

at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)

at SinglelnstanceApplication.InstanceProxy.SetCommandLineArgs(Boolean isFirstInstance, String[] commandLineArgs)

at SinglelnstanceApplication.ApplicationInstanceManager.UpdateRemoteObject(5tring uri)

at SinglelnstanceApplication.ApplicationInstanceManager.CreateSingleInstance(5tring name, EventHandler‘ 1 callback)

Can you please help me?

# re: Single Instance Application Manager

Saturday, March 23, 2013 5:12 PM by Hyatt

Thank you for sharing your info. I truly appreciate your efforts and I am waiting for your next write ups

thank you once again.

# re: Single Instance Application Manager

Sunday, March 24, 2013 7:37 PM by Hackney

If some one needs to be updated with newest technologies then he must be visit this web site and be up to

date every day.

# re: Single Instance Application Manager

Monday, March 25, 2013 10:26 PM by Marcus

When someone writes an piece of writing he/she keeps the idea of a user in his/her mind that how a user can understand

it. So that's why this piece of writing is great. Thanks!

# re: Single Instance Application Manager

Sunday, March 31, 2013 1:13 PM by Foltz

It's very simple to find out any topic on net as compared to books, as I found this article at this website.

# re: Single Instance Application Manager

Tuesday, April 02, 2013 9:16 PM by Lincoln

Quality articles is the crucial to attract the users to

visit the website, that's what this website is providing.

# re: Single Instance Application Manager

Sunday, April 21, 2013 10:22 AM by Vargas

You really make it seem so easy with your presentation but I find

this matter to be really something which I think I would never understand.

It seems too complicated and very broad for me.

I am looking forward for your next post, I'll try to get the hang of it!

# re: Single Instance Application Manager

Monday, April 22, 2013 2:11 AM by Dees

Greetings from Colorado! I'm bored at work so I decided to browse your site on my iphone during lunch break. I really like the info you present here and can't wait to take a look

when I get home. I'm surprised at how quick your blog loaded on my phone .. I'm not even

using WIFI, just 3G .. Anyways, superb site!

# re: Single Instance Application Manager

Wednesday, May 08, 2013 10:57 PM by Laster

Hello There. I found your blog using msn. This is a really well written

article. I'll make sure to bookmark it and return to read more of your useful info. Thanks for the post. I will definitely return.

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above: