Problem
I wanted to make sure that only one instance of a program is running on my machine, and when a second instance would be started, it would pass its command line arguments to the original instance and then terminate. Thus, the original instance can handle everything else, such as opening a file, bringing itself to the foreground, etc. Also, the solution should not employ outdated techniques such as DDE, and should not use any unmanaged code as I had seen in so many other solutions to this problem.
Solution
Part 1: Many solutions I have seen walk through the process list in order to identify a previous instance. Others use Mutex, which I found appealing as it is a lot faster and is completely managed code. This solution uses the full path of the executing assembly as the Mutex name, so it is definitely unique.
Part 2: Other solutions use DDE to communicate with the previous instance. I chose .NET remoting because, again, it is fully managed code and not a Windows legacy, and it also works with console applications, while DDE would require a window.
The whole functionality is encapsulated in a class, written in C# using .NET 2.0. To make this work with .NET 1.1, you would have to change some namespaces, otherwise it is fully compatible.
To demonstrate the principle, I created a simple console application. Of course, you'd need to enhance the class a little for general use. This example has, for instance, a fixed port number, and certainly other flaws for generic usage. But it should only demonstrate the principle.
Using the Code
The class that handles the whole thing is named SingletonController, and it has some static methods that would be used by the calling program (your main program).
if(SingletonController.IamFirst(new
SingletonController.ReceiveDelegate(myReceive)))
{
}
else
{
SingletonController.Send(args);
}
SingletonController.Cleanup();
The test whether this is the first instance, as you can see above, creates a delegate (callback) function. This function will be called whenever a second instance is opened.
The else branch handles the second instance, which will pass its arguments before terminating.
The SingletonController class has a couple of building blocks:
- it defines a
ReceiveDelegate, which will be set to the original instance's callback function
- the
IamFirst() function, which returns true if this is the first instance of your application
- the
CreateInstanceChannel() function, which creates a small remoting listener; this receives the arguments from any subsequent instances
- the
Send() function, which sends all arguments from a second instance to the initial instance before it terminates itself.
public static bool IamFirst()
{
string m_UniqueIdentifier;
string assemblyName =
System.Reflection.Assembly.GetExecutingAssembly().GetName(false).CodeBase;
m_UniqueIdentifier = assemblyName.Replace("\\", "_");
m_Mutex = new Mutex(false, m_UniqueIdentifier);
if (m_Mutex.WaitOne(1, true))
{
//We locked it! We are the first instance!!!
CreateInstanceChannel();
return true;
}
else
{
//Not the first instance!!!
m_Mutex.Close();
m_Mutex = null;
return false;
}
}
This function creates a Mutex based on the full path name of the executing assembly, then tries to lock it. If successful, it calls CreateInstanceChannel(), which will create a small remoting listener. This listener is later responsible for calling your main program's callback function.
private static void CreateInstanceChannel()
{
m_TCPChannel = new TcpChannel(1234);
ChannelServices.RegisterChannel(m_TCPChannel, false);
RemotingConfiguration.RegisterWellKnownServiceType(
Type.GetType("SingletonApp.SingletonController"),
"SingletonController",
WellKnownObjectMode.SingleCall);
}
This function creates the remoting listener. I hard-coded the port as 1234, this is what you want to put into your configuration file.
public static void Send(string[] s)
{
SingletonController ctrl;
TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel, false);
try
{
ctrl = (SingletonController)Activator.GetObject(
typeof(SingletonController),
"tcp://localhost:1234/SingletonController");
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.Message);
throw;
}
ctrl.Receive(s);
}
This function needs to be called by your main program in case it is the second instance (IamFirst() == false). It will send the arguments supplied to the first instance (the remoting listener), which in turn will call your callback function of the first instance. You will notice that this is the only call to a non-static function (ctrl.Receive()), your own code never needs to instantiate the SingletonController class.
The Receive() function finally calls your callback using the delegate member:
public void Receive(string[] s)
{
if (m_Receive != null)
{
m_Receive(s);
}
}
Running the Sample Code
After you compile the sample code, do this:
- open two command windows.
- run SingletonApp.exe in one of the windows; it will start saying "Hi: 0, Hi: 1, ..." every second and will terminate after 10 iterations.
- within 10 seconds after the above application runs, start SingletonApp.exe in a second command window and supply some command line arguments; watch the first window printing out the arguments supplied in the second window.
If you supply more than one argument, you will notice that the "Hi: 0, Hi: 1, ..." loop and the loop that prints the arguments run in separate threads, the "Hi" messages and arguments will be printed in an alternating fashion.
Points of Interest
None, this solution has been put together from different solutions I had found on the net, none of which combined it the way I wanted it to be. So, this is basically nothing new, just a new combination.
History
No changes (yet).
| You must Sign In to use this message board. |
|
|
 |
|
 |
Since this could become a pain its easier to use the StartupNextInstance event and then e.CommandLine to retrieve the commandline arguments. The event is fired everytime the so-called "Single Instance Application" is executed a second time.
|
| Sign In·View Thread·PermaLink | 3.00/5 |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
Hi,
Good article I like it 
I have implemented your Singleton Controller into my code and it seems to work a charm sending back the arguments that we sent when a second version was run. However I've hit a small problem and was wondering if you or anybody would have a clue.
In the static method myReceive, I have the following:
static void myReceive(string[] args) { frmBlah frmTest = new frmBlah(); frmTest.Show(); }
frmBlah is a test form that has two labels and a button on it. Does nothing else and is basically just a dummy to see if it was other forms that we causing the problem.
I run the application and then get a second copyto run. It does what it is suppossed to do and doesn't run the second copy but passes the arguments on and attempts to show frmTest. However whilst I get the window of frmTest appear it doesn't display any labels or the button and it acts like it's not responding. The rest of the app still does what it normally does and if I shut it down all closes fine.
Any ideas?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I rather like this artikle! 
In addition for all who want to show the first instance if hidden or minimized I added a function:
private delegate void VoidDelegate(); static public void ShowFirstInstance() { if (Application.OpenForms.Count > 0) { if (Application.OpenForms[0].InvokeRequired) Application.OpenForms[0].BeginInvoke(new VoidDelegate(ShowFirstInstance)); else { if (Application.OpenForms[Application.OpenForms.Count - 1].WindowState == FormWindowState.Minimized) Application.OpenForms[Application.OpenForms.Count - 1].WindowState = FormWindowState.Normal; Application.OpenForms[Application.OpenForms.Count - 1].Activate(); } } } You just have to call the function in the receive handler in Main().
Cheers
Daniel Rühmer Application engineer for Measurement Software
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi,
When using the SingletonController.cs in my project I get a runtime error when trying to open a second instance of the same application, and in the debugger I see the following message
"An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
Additional information: Only one usage of each socket address (protocol/network address/port) is normally permitted"
And the debugger point me to the following piece of code:
private static void CreateInstanceChannel() { => m_TCPChannel = new TcpChannel(1234);
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi,
extend the line
m_UniqueIdentifier = assemblyName.Replace("\\", "_"); with
m_UniqueIdentifier = assemblyName.Replace("\\", "_").ToLower(); and it should work...
Daniel Rühmer Application engineer for Measurement Software
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
First of all: using a Mutex does work for multiple logins on the same XP system. If there were any problems you could still add a session identifier to the Mutex name.
Picking a unique port can be done automatically, you can write the URI for your remoting host to the registry so that the next instance can find out which port the first instance is listening on.
If you use Assembly.GetExecutingAssembly() to get a path for your mutex name, you'll have to be careful: You get the assembly your SingletonController is contained in, NOT the application you want to make a singleton. This can become an issue if the assembly is put into the GAC. One solution could be to use Application.GetEntryAssembly() or another way to get the application path.
Two things should be noted, though (I'm using a similar mechanism in my applications and stumbled over them...): - If you want to use the path of your executable in the mutex name you should consider different casing (MYAPP.EXE vs. MyApp.exe would give different mutex names) and calling conventions (long/short filenames - MyApplication.exe vs. MyAppl~1.exe). One solution could be to determine the long path name and convert it to lowercase...
- I didn't look at your solution in detail, but when using remoting to signal that a bunch of command line arguments have to be processed, chances are that your command line argument handling methods are running in a different thread, so you could get problems with resulting UI updates. Just don't forget to check.
Regards, mav
-- Black holes are the places where god divided by 0...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I missed this at firs too, but Application class actually lives in the System.Windows.Forms assembly so not the best choice. I would use the AppDomain.CurrentDomain.ApplicationIdentity.CodeBase object, ApplicationIdentity is manifest based so it still allows for multiple instances, if your deployment scenario calls for it.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Do not use mutexes for this! They block all instances on one system. Try imagining running your application on a terminal server with 20 desktops and you understand the problem.
So.. maybe terminal server support is not that neccesary but you have the same problem with XP when 2 persons are logged in.
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
Sorry to correct you, but these mutexes only are unique for a single session, so if you have more than 1 user logged into the same XP system, each one will have his own mutex. Just try it out, you'll see what I mean.
Regards, mav
-- Black holes are the places where god divided by 0...
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
You are right, my implementation, which was only meant as a sample, works this way, and this is exactly what I wanted: one instance per user, possibly multiple instances per machine.
If you want it per machine, use "GLOBAL\" as the prefix for the Mutex and there you go.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
since you're coding this in .net 2.0, why don't you use an ipc channel? it's exactly what you need and it's also faster.
i skimmed quickly through the article, but, still, it didn't look like you were doing cross-system single-instance program blah blah
... so, just change it to an ipc channel (ipc = interprocess communication)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Good suggestion, thanks! I'll look into this when I have time, however I will lose backwards compatibility to .NET 1.1.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
i tried redoing the code to incorporate ICP instead of TCP and it wouldnt work, i must be missing something. any thoughts on how this can be correctly incorporated into the current code? -- modified at 17:34 Monday 11th September, 2006 I solved this problem already, it was rather easy to implement. i am working on the mutex issue too.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi, I do the same: use Mutex to be sure I have only one instance running. But I would be weary about using remoting to send to the running instance parameters. There are many problems with .Net remoting. Mainly the channel port may already be used by another application and it is pain to reset it to a different port even if using config files. With COM we did not have to handle the port problem. We suffered other security settings problems though. I believe in .Net 2 there is a new .Net remoting channel called IPX or something like this that may solve the problem mentioned before. It may be worth looking at it. regards, MM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I made things a little easier in my code...
Instead of using Remoting, I kept things simple by using both the FileSystem Watcher and Isolated Storage for my Smart Client Application.
To pass the value from the second instance to the first intstance I do the following:
1. First Instance starts the FileSystem Watcher for a special file in the Documents and Settings directory.
2. Second Instance writes to a special file using Isolated Storage and then exists.
3. First Instance automatically wakes up and reads the information directly from the special file in the Isolated Storage directory.
It's very fast and easy to support and implement.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
|