A single-instance application is one that allows the user to open only one instance at a time. Strictly speaking, a second instance is started, but it detects the presence of the first instance, and shuts itself down.
Visual Studio 2005 makes single-instance applications very easy. In VB, one simply checks a box on the project settings dialog, and then handles the appropriate events. The code that accomplishes this is in the
Microsoft.VisualBasic namespace, but it can be used in a C# application. This article will show you how to do this, and show how to transfer command-line arguments from the second instance to the original one.
Single-instance applications have been around even before .NET, and were accomplished in various ways. The key is the communication between instances. In .NET, this is typically done with remoting; the framework classes used here take this approach. Before .NET 2.0, you were on your own with single-instance apps. One excellent description is this code project article by Detlef Grohs. If you're stuck in .NET 1.1, or just interested in what's going on "under the hood", then that's a great place to start.
Using the code
The source code for this article consists of two files: a main Windows Form named (appropriately) MainForm, and a Main.cs, which holds the entry point function
Main(), and the application class
If starting from a blank C# Windows project, the first step is to add a reference to the Microsoft.VisualBasic assembly. Right click on references in the Solution Explorer, choose "add reference", and scroll down in the .NET tab to "Microsoft.VisualBasic".
Main() function creates an instance of the
App class, and runs it, passing the command-line arguments:
static void Main(string args)
App myApp = new App();
App is derived from
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase, which contains all of the remoting code necessary to check for an existing instance of the application. This functionality just needs to be turned on in the constructor:
this.IsSingleInstance = true;
this.EnableVisualStyles = true;
The last line of the constructor adds a handler for the
StartupNextInstance event, which is fired if an existing instance is detected. It is used here to transfer command-line arguments, but if those aren't needed, then the event doesn't need to be handled.
The next thing to do is override the
OnCreateMainForm() method. This is called only for the first instance of the application. An instance of the
MainForm class is created, and saved to a property of
Command-line parameters are passed to the form by copying the
ReadonlyCollection to a string array, and saving to a member variable of the form:
protected override void OnCreateMainForm()
this.MainForm = new MainForm();
Additional instances of the application may want to pass command-line arguments to the first; for instance, a file viewer may want to open a new document window when a second file is opened, rather than a new instance of the application. This is done in the
protected void SIApp_StartupNextInstance(object sender,
string args = new string[eventArgs.CommandLine.Count];
object parameters = new object;
parameters = this.MainForm;
parameters = args;
((MainForm)this.MainForm).ProcessParameters), parameters );
The new instance is of course on a different thread, so delegates and the
Invoke function are used. The
MainForm.ProcessParameters method just adds the arguments to a textbox on the form.
Points of interest
My particular need for single-instance functionality is opening groups of files from the context menu in the explorer. This fires off multiple instances of the application at essentially the same time. The remoting communication and loading of additional instances require a lot of overhead; on my system, this demo application opens about five files per second.
At about 50 files, an exception is thrown; it appears that the remoting connection to the initial instance is timing out. I'll be on the lookout for a bombproof (and faster) method of implementing a single-instance application in C# - write an article for us if you know a better way!
- 2006-01-30 - Initial release.