31
\$\begingroup\$

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?

\$\endgroup\$

1 Answer 1

33
\$\begingroup\$

This might be against the spirit of Code Review, but you don't need to write your own single instance manager for WPF! Microsoft has already written code to accomplish this, but it has been poorly advertised.

Microsoft's single instance manager is extremely comprehensive, and I have yet to find any issues with it. (And if you don't want to use it, it can at least be a good reference for your own implementation.)

How to manage single instances in WPF

Step 1: Add the System.Runtime.Remoting reference to your project.

Step 2: Add this single instance class to your project.

Step 3: Implement the ISingleInstanceApp interface in your main application class in App.xaml.cs (this interface is provided by the SingleInstance.cs file).

For example:

public partial class App : Application, ISingleInstanceApp

Step 4: Change your startup object by going to Project --> <projectname> Properties in Visual Studio, clicking the Application tab. Locate Startup object: and select the <projectname>.App option.

Setting WPF startup object

Step 5: Define a Main method in App.xaml.cs, and give it a unique string in the Unique variable.

// TODO: Make this unique!
private const string Unique = "Change this to something that uniquely identifies your program.";

[STAThread]
public static void Main()
{   
    if (SingleInstance<App>.InitializeAsFirstInstance(Unique))
    {
        var application = new App();
        application.InitializeComponent();
        application.Run();

        // Allow single instance code to perform cleanup operations
        SingleInstance<App>.Cleanup();
    }
}

#region ISingleInstanceApp Members
public bool SignalExternalCommandLineArgs(IList<string> args)
{
    // Handle command line arguments of second instance
    return true;
}
#endregion

Step 6: Right-click on App.xaml in the Solution Explorer, select Properties, and change the Build Action to Page.

Single Instance techniques

Do nothing when new instance is opened

If you don't want anything to happen when a single instance is launched, you don't need to modify the sample code above.

Activate original window when new instance is opened

Rather than doing nothing when a user attempts to open a second instance of the program, it's often beneficial to Activate the original window and change its window state to WindowState.Normal, which can provide an improved user experience if the original window was minimized.

To activate and make the window visible, modify the SignalExternalCommandLineArgs method as shown . (Note that MainWindow is a property of the App class, not to be confused with an arbitrarily-named MainWindow.xaml.cs class.)

public bool SignalExternalCommandLineArgs(IList<string> args)
{
    // Bring window to foreground
    if (this.MainWindow.WindowState == WindowState.Minimized)
    {
        this.MainWindow.WindowState = WindowState.Normal;
    }

    this.MainWindow.Activate();

    return true;
}
\$\endgroup\$
6
  • \$\begingroup\$ using this setup, how would you for example show, activate and unminimize the windows belonging to the app already running? \$\endgroup\$
    – Julien
    May 8, 2014 at 19:06
  • \$\begingroup\$ @Julien I just added an additional section to my answer called Single Instance techniques which answers this question. Short version: Call the Activate() method on the MainWindow property of App, and set its WindowState to WindowState.Normal. \$\endgroup\$ Sep 10, 2014 at 23:34
  • \$\begingroup\$ @EvanWondrasek, I have tried your solution for a clickone wpf, which used WindowsFormApplictionBase before.However,both of the approach has same issue of not capture second/later instances startup args. It only works for first instance and not for anything after. I have a separate post, which describe more about the issue. Any help? stackoverflow.com/questions/30463690/… \$\endgroup\$ May 26, 2015 at 22:12
  • 1
    \$\begingroup\$ Updated version available here (with an Apache license): code.msdn.microsoft.com/Windows-7-Taskbar-Single-4120eafd (which the original blog owner referenced in this post: blogs.microsoft.co.il/arik/2011/04/04/…). \$\endgroup\$ Aug 17, 2016 at 23:50
  • 1
    \$\begingroup\$ Isn't it important for App.xaml's build action to be ApplicationDefinition? Can you comment on the implications of changing it? \$\endgroup\$ Mar 29, 2018 at 15:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.