Click here to Skip to main content
15,895,772 members
Articles / Desktop Programming / WPF

WPF Application Wrapper with Single Instance Support

Rate me:
Please Sign up or sign in to vote.
4.50/5 (3 votes)
17 Dec 2009CPOL5 min read 31.4K   374   24   4
An Application class wrapper which provides common features and single instance support to WPF programs.

Introduction

This post describes a class that can be used to reduce the amount of redundant code needed to write a standard WPF desktop application. The class adds single instance support, easier unhandled exception handling, and can handle missing assemblies. The class is an extension of the App.xaml file, so can still be edited using Visual Studio's XAML editor.

This article is also described at my site.

Background

With the types of applications that I develop, I usually prefer to make my programs single instance applications. Essentially, this means ensuring that only one instance of the application is running at any given time, and all subsequent application opening requests are handled by the single instance already running in memory.

I also use many third party libraries which require external DLLs to be present. Although tools such as ILMerge are a great convenience to remove dependencies of these required external files by combining them all into a single file, due to some license restrictions and other factors, it is not always possible to do so. However, relying on other external library files raises additional problems when a file is missing or is invalid. This is another issue which I must address in most of the applications that I write.

Although single instance application support and handling of missing third party libraries is not a difficult problem to solve, it is something which I find myself writing time and time again. For this reason, I set out to create a generic application wrapper class for WPF to help reduce the amount of redundant code I write.

The current version of WPF doesn’t have any built in support for single instance applications, but there are many different ways to rectify the problem. Some examples are by using GetProcessesByName, by using the Microsoft.VisualBasic namespace, or by using mutexes.

For my application class, I chose to use the Microsoft.VisualBasic method, as described in the book "Pro WPF in C# 2008". This solution seemed to be the most reliable and easiest method to implement in a generic manner.

.NET applications already support missing assembly issues by the use of the AppDomain.CurrentDomain.AssemblyResolve event, so it was just a simple matter of registering to the event and providing a wrapper for the function in the new Application class.

In WPF applications, the App.xaml file provides an easy way to control program settings.

XML
<Application x:Class="WpfApplicationSample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Window1.xaml">
        <Application.Resources>

        </Application.Resources>
</Application>

Because I wanted this new Application class to be added to any WPF project with ease, it was a requirement for the new class to be compatible with the XAML editor. I wanted the end result to look something like the following:

XML
<local:Application x:Class=" MyNamespace.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyNamespace"
    StartupUri="Window1.xaml">
    <local:WpfApplication.Resources>

    </local:WpfApplication.Resources>
</local:WpfApplication>

To achieve this functionality, I decided to create a wrapper for the System.Windows.Application class which had the exact same functionality, plus the extra features. I named this class WpfApplication. I created an empty class which derives from System.Windows.DependencyObject, and holds a System.Windows.Application instance in memory in order to perform all of its required functions. This System.Windows.Application instance lives for the entire duration of the application. An event handler is attached to each event of the System.Windows.Application instance in order to raise WpfApplication’s events and to execute the new custom functionality for some events such as exception handling.

The WpfApplication’s Run methods are where the real logic starts. I created another application wrapper that derives from the single instance supporting the Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase object. This object is created on each call to a Run method, and then calls the System.Windows.Application Run methods.

C#
/// <summary>
/// Starts the single instance application wrapper
/// </summary>
/// <param name="window">The Window which is to be started. This can be null</param>
/// <returns>The return code returned from the WPF Application instance</returns>
private int Start(Window window)
{
    var wrapper = new 
        SingleInstanceApplicationWrapper(this, IsSingleInstance) {Window = window};

    // Although an empty array of string can be given
    // to the application wrapper, if done so then subsequent
    // instances will not be provided command line arguments in the
    // Microsoft.VisualBasic.ApplicationServices.StartupEventArgs argument
    wrapper.Run(Environment.GetCommandLineArgs());
    return wrapper.ReturnCode;
}

Once System.Windows.Application terminates, so does the Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase wrapper. The Microsoft.VisualBasic.ApplicationServices. WindowsFormsApplicationBase.OnStartupNextInstance method is overridden in the SingleInstanceApplicationWrapper class so that it can then call the new event of the WpfApplication instance.

C#
/// <summary>
/// Calls the appropriate method of the WPF application
/// when the next instance of the application is
/// started
/// </summary>
/// <param name="eventArgs">The arguments that describe this event</param>
protected override void OnStartupNextInstance(
     Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs eventArgs)
{
    // Convert the ReadOnlyCollection<string>
    // arguments to a string[] collection of arguments and remove
    // the first argument which will always be the application path
    ReadOnlyCollection<string> argsCollection = eventArgs.CommandLine;
    int count = argsCollection.Count - 1;
    string[] args;

    if (count > 0)
    {
        args = new string[eventArgs.CommandLine.Count - 1];
        for (int i = 0; i < count; ++i)
        {
            args[i] = argsCollection[i + 1];
        }
    }
    else
    {
        args = new string[0];
    }

    _app.OnStartupNextInstance(new StartupNextInstanceEventArgs(args));
}

The end class looks like the following:

Class diagram

Using the code

In order to use the new class, we just need to copy the WpfApplication class into our project, and change the App.xaml to use the new class instead of the default. Options can then be set as usual using Visual Studio’s XAML editor.

XML
<local:WpfApplication x:Class="WpfApplicationSample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplicationSample"
      StartupUri="Window1.xaml" IsSingleInstance="True" 
      HandleUnhandledException="True" 
      HandleUnresolvedAssembly="True">
    <local:WpfApplication.Resources>

    </local:WpfApplication.Resources>
</local:WpfApplication>

The Microsoft.VisualBasic reference will also need to be added to the project.

Add reference

In order to complete the process, the App.xaml code will need to be changed to derive from WpfApplication. So change from:

C#
// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}

to this:

C#
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
}

Since App.xaml is a partial class whose base type is declared in the XAML file, it does not need to be declared again in the C# file (although it is perfectly acceptable to do so).

And that’s all there is to it. Just copy the new file to your project, change the App.xaml files, and you now have an easy way to mange a single instance application which has better support for runtime errors. See the demo project for more information.

Known bugs

There is a bug in Visual Studio 2008 where the editor displays an error such as "Could not create an instance of type 'WpfApplication'". I have been unable to determine why this bug occurs, and have come to the conclusion that it is a bug with the Visual Studio IDE, not the code itself. The error can be safely ignored without any concern (it is not a compile or runtime error, just a design time error). If anybody knows of a workaround for this, or why this occurs, then please let me know.

History

  • Version 1.0 (18/12/2009) - Added to CodeProject.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionOn my app I receive an error on .NET 4.0 Pin
Fabrizio Stellato5-Dec-12 11:14
Fabrizio Stellato5-Dec-12 11:14 
AnswerRe: On my app I receive an error on .NET 4.0 Pin
Asniper8-Apr-13 6:27
Asniper8-Apr-13 6:27 
GeneralMy general solution for this problem Pin
Member 293394822-Feb-10 6:18
Member 293394822-Feb-10 6:18 
GeneralRe: My general solution for this problem Pin
summatix22-Feb-10 10:11
summatix22-Feb-10 10:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.