In my intoductory post I mentioned another way to implement a single instance WPF application by making use of Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
class. This scenario is also covered in the great book on WPF by Matthew MacDonald and I thought my story wouldn’t be complete without looking at this option.
Microsoft.VisualBasic.ApplicationServices
namespace lives in Microsoft.VisualBasic assembly and contains classes to facilitate Visual Basic Application Model, which is essentially a bunch of useful helpers that allow you to conveniently perform various tasks at the startup and shutdown of a Windows Forms application.
_- dude! I thought we were talking about WPF?
- yeah, I remember, just hold on a sec._
One of those handy classes is WindowsFormsApplicationBase
that (among other features) provides a way to enable a single instance of your Windows Forms application as easy as setting its IsSingleInstance
property to True before running your application and overriding a couple of virtual methods (or subscribing to corresponding events if you prefer that model): OnStartup
and OnStartupNextInstance
. The 1st will get called when the 1st instance of an application starts up and the second will get called each time another instance starts up. Those other instances will be shut down for you automatically.
The beauty of this model is that we get the command line arguments in both of these events so we don’t have to worry how to pass them across process boundaries ourselves.
Under the hood WindowsFormsApplicationBase
uses Remoting over the TCP channel to pass the credentials. I was trying to avoid this design as I didn’t want to mess with ports.
- what about WPF??
Ah! You’re still there!. Good, the idea is to wrap a WPF application with WindowsFormsApplicationBase
. If it doesn’t hurt your feelings, read on. Otherwise take a practice to implement a standalone Remoting solution similar to my WCF-based one.
Usage
If you use Visual Studio to create a WPF application skeleton, delete App.xaml and App.xaml.cs files first. Instead you’re going to add your own entry point to your application. Just add a new class and type the Main method like this:
class Program
{
[STAThread]
public static void Main(string[] args)
{
WindowsFormsApp wrapper = new WindowsFormsApp();
wrapper.Run(args);
}
}
That’s it. By calling ‘Run
’ you start the Windows Forms application.
The wrapper
Our WindowsFormsApp
derives from WindowsFormsApplicationBase
and encapsulates the WPF application. It contains all the logic necessary to drive a single instance behavior:
class WindowsFormsApp : WindowsFormsApplicationBase
{
private App _wpfApp;
public WindowsFormsApp()
{
// enable Single Instance behavior
IsSingleInstance = true;
}
protected override bool OnStartup(StartupEventArgs e)
{
// we are here when the 1st instance starts up
// start the WPF app
_wpfApp = new App();
_wpfApp.Run();
return false;
}
protected override void OnStartupNextInstance(
StartupNextInstanceEventArgs e)
{
if (e.CommandLine.Count > 0)
{
_wpfApp.ProcessArguments(e.CommandLine.ToArray());
}
}
}
It’s that simple.
The WPF app
The App
class you saw in the previous code snippet is a regular WPF application class that derives from System.Windows.Application
.
class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
window.Show();
}
public void ProcessArguments(string[] args)
{
// process command line arguments
// of other instances
}
}
What about performance? It passed my test within 25 seconds. Almost as fast as the WM_COPYDATA solution and considerably faster than the WCF one that uses named pipes.