WPF Single Instance Best Practices

56,341

Solution 1

1) It looks like a standard Dispose implementation to me. It is not really necessary (see point 6) but it does not do any harm. (Cleanup on closing it's a bit like cleaning the house before burning it down, IMHO, but opinions on the matter differs..)

Anyway, why not using "Dispose" as the name of the cleanup method, even if it does not get called directly? You could have called it "Cleanup", but remember you also write code for humans, and Dispose looks familiar and anyone on .NET understands what is it for. So, go for "Dispose".

2) I have always seen m_Mutex = new Mutex(false, mutexName); I think it's more a convention that a technical advantage, however.

3) From MSDN:

If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.

So I would not worry. Usually, for this class of functions, UInt is not used for "it does not fit in Int, let's use UInt so we have something more" but to clarify a contract "function never returns a negative value".

4) I would avoid calling it if you will shutdown, same reason as #1

5) There are a couple of ways of doing it. The easiest way in Win32 is simply to have the second instance make the call to SetForegroundWindow (Look here: http://blogs.msdn.com/b/oldnewthing/archive/2009/02/20/9435239.aspx); however, I don't know if there is an equivalent WPF functionality or if you need to PInvoke it.

6)

For example... what happens if my application crashes between OnStartup and OnExit?

It's OK: when a process terminates, all handles owned by the process are released; the mutex is released as well.

In short, my recommendations:

  • I would used an approach based on named synchronization objects: it is the more established on the windows platform(s). (Be careful when considering a multi-user system, like terminal server! Name the synchronization object as a combination of, maybe, user name/SID and application name)
  • Use the Windows API to raise the previous instance (see my link at point #5), or the WPF equivalent.
  • You probably do not have to worry about crashes (kernel will decrease the ref counter for the kernel object for you; do a little test anyway), BUT If I may suggest an improvement: what if your first application instance does not crash but hangs? (Happens with Firefox.. I'm sure it happened to you too! No window, ff process, you cannot open a new one). In that case it may be good to combine another technique or two, to a) test if the application/window responds; b) find the hung instance and terminate it

For example, you can use your technique (trying to send/post a message to the window - if does not answer back it is stuck), plus MSK technique, to find and terminate the old process. Then start normally.

Solution 2

There are Several choices,

  • Mutex
  • Process manager
  • Named Semaphore
  • Use a listener socket

Mutex

Mutex myMutex ;

private void Application_Startup(object sender, StartupEventArgs e)
{
    bool aIsNewInstance = false;
    myMutex = new Mutex(true, "MyWPFApplication", out aIsNewInstance);  
    if (!aIsNewInstance)
    {
        MessageBox.Show("Already an instance is running...");
        App.Current.Shutdown();  
    }
}

Process manager

private void Application_Startup(object sender, StartupEventArgs e)
{
    Process proc = Process.GetCurrentProcess();
    int count = Process.GetProcesses().Where(p=> 
        p.ProcessName == proc.ProcessName).Count();

    if (count > 1)
    {
        MessageBox.Show("Already an instance is running...");
        App.Current.Shutdown(); 
    }
}

Use a listener socket

One way to signal another application is to open a Tcp connection to it. Create a socket, bind to a port, and listen on a background thread for connections. If this succeeds, run normally. If not, make a connection to that port, which signals the other instance that a second application launch attempt has been made. The original instance can then bring its main window to the front, if appropriate.

“Security” software / firewalls might be an issue.

Single Instance Application C#.Net along with Win32

Solution 3

I wanted to have a bit better user experience - if another instance is already running let's activate it rather than showing an error about the second instance. Here is my implementation.

I use named Mutex for making sure that only one instance is running and named EventWaitHandle to pass notification from one instance to another.

App.xaml.cs:

/// <summary>Interaction logic for App.xaml</summary>
public partial class App
{
    #region Constants and Fields

    /// <summary>The event mutex name.</summary>
    private const string UniqueEventName = "{GUID}";

    /// <summary>The unique mutex name.</summary>
    private const string UniqueMutexName = "{GUID}";

    /// <summary>The event wait handle.</summary>
    private EventWaitHandle eventWaitHandle;

    /// <summary>The mutex.</summary>
    private Mutex mutex;

    #endregion

    #region Methods

    /// <summary>The app on startup.</summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The e.</param>
    private void AppOnStartup(object sender, StartupEventArgs e)
    {
        bool isOwned;
        this.mutex = new Mutex(true, UniqueMutexName, out isOwned);
        this.eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);

        // So, R# would not give a warning that this variable is not used.
        GC.KeepAlive(this.mutex);

        if (isOwned)
        {
            // Spawn a thread which will be waiting for our event
            var thread = new Thread(
                () =>
                {
                    while (this.eventWaitHandle.WaitOne())
                    {
                        Current.Dispatcher.BeginInvoke(
                            (Action)(() => ((MainWindow)Current.MainWindow).BringToForeground()));
                    }
                });

            // It is important mark it as background otherwise it will prevent app from exiting.
            thread.IsBackground = true;

            thread.Start();
            return;
        }

        // Notify other instance so it could bring itself to foreground.
        this.eventWaitHandle.Set();

        // Terminate this instance.
        this.Shutdown();
    }

    #endregion
}

And BringToForeground in MainWindow.cs:

    /// <summary>Brings main window to foreground.</summary>
    public void BringToForeground()
    {
        if (this.WindowState == WindowState.Minimized || this.Visibility == Visibility.Hidden)
        {
            this.Show();
            this.WindowState = WindowState.Normal;
        }

        // According to some sources these steps gurantee that an app will be brought to foreground.
        this.Activate();
        this.Topmost = true;
        this.Topmost = false;
        this.Focus();
    }

And add Startup="AppOnStartup" (thanks vhanla!):

<Application x:Class="MyClass.App"  
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Startup="AppOnStartup">
    <Application.Resources>
    </Application.Resources>
</Application>

Works for me :)

Solution 4

For WPF just use:

public partial class App : Application
{
    private static Mutex _mutex = null;

    protected override void OnStartup(StartupEventArgs e)
    {
        const string appName = "MyAppName";
        bool createdNew;

        _mutex = new Mutex(true, appName, out createdNew);

        if (!createdNew)
        {
            //app is already running! Exiting the application  
            Application.Current.Shutdown();
        }

        base.OnStartup(e);
    }          
}

Solution 5

to prevent a second instance (and signal the existing),

  • using EventWaitHandle (since we are talking about an event),
  • using Task,
  • no Mutex code required,
  • no TCP,
  • no Pinvokes,
  • no GarbageCollection stuff,
  • thread save
  • simple

it could be done like this (this for an WPF app (see ref to App()), but works on WinForms as well):

public partial class App : Application
{
    public App()
    {
        // initiate it. Call it first.
        preventSecond();
    }

    private const string UniqueEventName = "{GENERATE-YOUR-OWN-GUID}";

    private void preventSecond()
    {
        try
        {
            EventWaitHandle.OpenExisting(UniqueEventName); // check if it exists
            this.Shutdown();
        }
        catch (WaitHandleCannotBeOpenedException)
        {
            new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName); // register
        }
    }
}

Second version: above plus signaling the other instance to show the window (change the MainWindow part for WinForms):

public partial class App : Application
{
    public App()
    {
        // initiate it. Call it first.
        //preventSecond();
        SingleInstanceWatcher();
    }

    private const string UniqueEventName = "{GENERATE-YOUR-OWN-GUID}";
    private EventWaitHandle eventWaitHandle;

    /// <summary>prevent a second instance and signal it to bring its mainwindow to foreground</summary>
    /// <seealso cref="https://stackoverflow.com/a/23730146/1644202"/>
    private void SingleInstanceWatcher()
    {
        // check if it is already open.
        try
        {
            // try to open it - if another instance is running, it will exist , if not it will throw
            this.eventWaitHandle = EventWaitHandle.OpenExisting(UniqueEventName);

            // Notify other instance so it could bring itself to foreground.
            this.eventWaitHandle.Set();

            // Terminate this instance.
            this.Shutdown();
        }
        catch (WaitHandleCannotBeOpenedException)
        {
            // listen to a new event (this app instance will be the new "master")
            this.eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);
        }

        // if this instance gets the signal to show the main window
        new Task(() =>
        {
            while (this.eventWaitHandle.WaitOne())
            {
                Current.Dispatcher.BeginInvoke((Action)(() =>
                {
                    // could be set or removed anytime
                    if (!Current.MainWindow.Equals(null))
                    {
                        var mw = Current.MainWindow;

                        if (mw.WindowState == WindowState.Minimized || mw.Visibility != Visibility.Visible)
                        {
                            mw.Show();
                            mw.WindowState = WindowState.Normal;
                        }

                        // According to some sources these steps are required to be sure it went to foreground.
                        mw.Activate();
                        mw.Topmost = true;
                        mw.Topmost = false;
                        mw.Focus();
                    }
                }));
            }
        })
        .Start();
    }
}

This code as a drop in class, will be @ Selfcontained-C-Sharp-WPF-compatible-utility-classes / Utils.SingleInstance.cs

Share:
56,341

Related videos on Youtube

Tommaso Belluzzo
Author by

Tommaso Belluzzo

Updated on July 09, 2022

Comments

  • Tommaso Belluzzo
    Tommaso Belluzzo almost 2 years

    This is the code I implemented so far to create a single instance WPF application:

    #region Using Directives
    using System;
    using System.Globalization;
    using System.Reflection;
    using System.Threading;
    using System.Windows;
    using System.Windows.Interop;
    #endregion
    
    namespace MyWPF
    {
        public partial class MainApplication : Application, IDisposable
        {
            #region Members
            private Int32 m_Message;
            private Mutex m_Mutex;
            #endregion
    
            #region Methods: Functions
            private IntPtr HandleMessages(IntPtr handle, Int32 message, IntPtr wParameter, IntPtr lParameter, ref Boolean handled)
            {
                if (message == m_Message)
                {
                    if (MainWindow.WindowState == WindowState.Minimized)
                        MainWindow.WindowState = WindowState.Normal;
    
                    Boolean topmost = MainWindow.Topmost;
    
                    MainWindow.Topmost = true;
                    MainWindow.Topmost = topmost;
                }
    
                return IntPtr.Zero;
            }
    
            private void Dispose(Boolean disposing)
            {
                if (disposing && (m_Mutex != null))
                {
                    m_Mutex.ReleaseMutex();
                    m_Mutex.Close();
                    m_Mutex = null;
                }
            }
    
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
            #endregion
    
            #region Methods: Overrides
            protected override void OnStartup(StartupEventArgs e)
            {
                Assembly assembly = Assembly.GetExecutingAssembly();
                Boolean mutexCreated;
                String mutexName = String.Format(CultureInfo.InvariantCulture, "Local\\{{{0}}}{{{1}}}", assembly.GetType().GUID, assembly.GetName().Name);
    
                m_Mutex = new Mutex(true, mutexName, out mutexCreated);
                m_Message = NativeMethods.RegisterWindowMessage(mutexName);
    
                if (!mutexCreated)
                {
                    m_Mutex = null;
    
                    NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, m_Message, IntPtr.Zero, IntPtr.Zero);
    
                    Current.Shutdown();
    
                    return;
                }
    
                base.OnStartup(e);
    
                MainWindow window = new MainWindow();
                MainWindow = window;
                window.Show(); 
    
                HwndSource.FromHwnd((new WindowInteropHelper(window)).Handle).AddHook(new HwndSourceHook(HandleMessages));
            }
    
            protected override void OnExit(ExitEventArgs e)
            {
                Dispose();
                base.OnExit(e);
            }
            #endregion
        }
    }
    

    Everything works perfectly... but I have some doubts about it and I would like to receive your suggestions about how my approach could be improved.

    1) I was asked by Code Analysis to implement IDisposable interface because I was using IDisposable members (the Mutex). Is my Dispose() implementation good enough? Should I avoid it because it's never going to be called?

    2) It's better to use m_Mutex = new Mutex(true, mutexName, out mutexCreated); and check for the result or to use m_Mutex = new Mutex(false, mutexName); and then check for m_Mutex.WaitOne(TimeSpan.Zero, false); ? In case of multithreading I mean...

    3) RegisterWindowMessage API call should return UInt32... but HwndSourceHook is only accepting Int32 as message value... should I be worried about unexpected behaviors (like a result bigger than Int32.MaxValue)?

    4) In OnStartup override... should I execute base.OnStartup(e); even if another instance is already running and I'm going to shutdown the application?

    5) Is there a better way to bring the existing instance to the top that doesn't need to set Topmost value? Maybe Activate()?

    6) Can you see any flaw in my approach? Something concerning multithreading, bad exceptions handling and something like that? For example... what happens if my application crashes between OnStartup and OnExit?

  • invalidusername
    invalidusername over 11 years
    if you do so, use a lock file instead of a port
  • Lorenzo Dematté
    Lorenzo Dematté over 11 years
    Umm... what's the difference from the OP code? He uses a named mutex, why a semaphore should be better?
  • Lorenzo Dematté
    Lorenzo Dematté over 11 years
    @invalidusername I personally dislike lock files: unlike other resources (handles, sockets) they are not "deleted" automatically by the OS if the process crash. Some programs have this (Early versions of Firefox?) and they were incredibly annoying..
  • Lorenzo Dematté
    Lorenzo Dematté over 11 years
    Computers with no NIC/IPAddress -> not a problem, you are on the same machine/will use the loopback adapter
  • invalidusername
    invalidusername over 11 years
    true, but opening an extra port also seems a strange way to solve this problem.
  • Shannon Cook
    Shannon Cook about 11 years
    As an added touch, you may want to add calls to SetForegroundWindow(IntPtr hWnd) and ShowWindowAsync(IntPtr hWnd, int nCmdShow) to bring the currently running instance to the foreground instead of showing a message box. You'll need to use [DllImport("user32.dll")] for this.
  • NoWar
    NoWar over 10 years
    Is "MyWPFApplication" name of EXE?
  • C-va
    C-va over 10 years
    @Clark - Name of the Mutex. Could be any unique name. So, the mutex allows only one instance under that unique name.
  • vhanla
    vhanla over 8 years
    You forgot to mention that we need to add AppOnStartup to the App.Xaml file: Startup="AppOnStartup"
  • toster-cx
    toster-cx about 8 years
    It may be strange, but it works, and it works on almost any system. The only thing I'm concerned about is, I've sometimes seen listening ports stay "open" for a short while even after the application has crashed.
  • Andrew Truckle
    Andrew Truckle about 8 years
    @vhanla Thanks for that clarification ... :)
  • BadmintonCat
    BadmintonCat over 7 years
    You mean, every time we close an app we're burning down a house? ZOMG! X-P
  • bluekushal
    bluekushal almost 7 years
    This is the perfect answer.
  • luka
    luka almost 7 years
    that is the best answer in code shortly , working without use bloody external dll and a large deprecated code.
  • thehelix
    thehelix over 6 years
    This is a fantastic answer. I was able to plug it into my app almost word for word, and then easily extend it to support my requirements. Works like a charm!
  • Altiano Gerung
    Altiano Gerung about 6 years
    The only downfall is that this use a thread. Probably not good for performance
  • Alexandru Dicu
    Alexandru Dicu over 5 years
    Thank you. Clean and easy to understand.
  • BananaAcid
    BananaAcid over 5 years
    Solution for .NET 2 and win XP ... But my problem with it is going after titles. I see a lot of potential in getting this broken, hitting security restrictions, not working on future windows environments ...
  • Mark A. Donohoe
    Mark A. Donohoe over 5 years
    Great solution. I like the way you use the EventWaitHandle instead of relying on a mutex or something similar. Only changes I made was to specifically catch WaitHandleCannotBeOpenedException, and I moved the creation of the wait handle to inside the task itself as it doesn't ever need to be global (it can be local in the 'try' block, and a second local inside the task. But again, best solution here.
  • BananaAcid
    BananaAcid over 5 years
    @MarqueIV Thanks - and only catching the specific exception makes sense. The reason I thought it would be good to have the handle synchronously created outside of the task, was to have the app quit before it spins off the async running task - being a tad more resource friendly and maybe keep any app stuff from flashing up or running (or any message box later in the code, debug etc = blocking code flow).
  • Mark A. Donohoe
    Mark A. Donohoe over 5 years
    Makes sense. But even if you create it outside the task, it can still be local to that function as the task should capture it, Shouldn’t it?
  • BananaAcid
    BananaAcid over 5 years
    :D oh sure - I just realised what you meant. It does not have to be in the global scope in that use case. Just left it there, to have accessible, in case there is another method to trigger anything on that eventWaitHandle object or is going to react to it ...
  • miriyo
    miriyo over 5 years
    This has solved my problem too so many thanks. I should add that my App.xaml file has Startup="AppOnStartup" and StartupUri="MainWindow.xaml". I also have the minor issue that a network deployed/published application checks for a new version each time the user starts it from the desktop but I can live with that.
  • Itzalive
    Itzalive about 5 years
    I would suggest two improvements. Firstly that you use TryOpenExisting rather than using an Exception as part of your normal flow and secondly that you register the Task as a long running task - TaskCreationOptions.LongRunning - so you aren't using one of the Task pool spaces for the entire lifetime of your application.
  • Geoff
    Geoff over 4 years
    @LorenzoDematté I realise this is an old question of yours, but for others, read this answer for a good analysis of the important differences.
  • Lorenzo Dematté
    Lorenzo Dematté about 4 years
    @Geoff while the answer is an interesting reading, it is for a different OS. In the NT kernel mutexes and semaphores are both kernel dispatcher objects and they act in a pretty similar way. For sure in such a way that for the OP purpose using a semaphore with a count of 1 instead of a mutex has no effect whatsover.
  • Lorenzo Dematté
    Lorenzo Dematté about 4 years
    Well, EventWaitHandle.OpenExisting, does create a synchronization object underneath so I don't understand why it should be better than a named mutex, but it is interesting to see another solution!
  • bastio84
    bastio84 about 4 years
    IMHO, this is simply a PERFECT answer... Many thanks for this piece of high quality code that works out of the box!
  • Geordie
    Geordie about 2 years
    Can simply this more and remove an arbitrary string: _mutex = new Mutex(true, this.GetType().Namespace.ToString(), out createdNew);
  • Adrian S
    Adrian S about 2 years
    @AltianoGerung, no problem for performance as the thread will not be active when it is waiting on the event.
  • Adrian S
    Adrian S about 2 years
    I like the thread approach - it's better than the old approach of searching for top-level windows with the same name as the app. Just make sure your event name and mutex name really are unique - I use the app's name followed by a GUID created with the Create GUID too.