Introduction
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.
Background
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 App
.
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".
The 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();
myApp.Run(args);
}
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:
public App()
{
this.IsSingleInstance = true;
this.EnableVisualStyles = true;
this.ShutdownStyle =
Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses;
this.StartupNextInstance +=
new StartupNextInstanceEventHandler(this.SIApp_StartupNextInstance);
}
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 WindowsFormsApplicationBase
.
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();
((MainForm)this.MainForm).Args =
new string[this.CommandLineArgs.Count];
this.CommandLineArgs.CopyTo(((MainForm)this.MainForm).Args, 0);
}
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 StartupNextInstance
handler:
protected void SIApp_StartupNextInstance(object sender,
StartupNextInstanceEventArgs eventArgs)
{
string[] args = new string[eventArgs.CommandLine.Count];
eventArgs.CommandLine.CopyTo(args, 0);
object[] parameters = new object[2];
parameters[0] = this.MainForm;
parameters[1] = args;
this.MainForm.Invoke(new MainForm.ProcessParametersDelegate(
((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!
History
- 2006-01-30 - Initial release.