I spent a few hours yesterday piecing together the needed code to create a single instance (non-re-entrant) WPF desktop application. There are lots of examples out there of how to do this. All the ones I could find fall short however, in that, if they detect that a prior instance is open, the just display a message box telling the user that another instance is already running. I wanted something more user friendly, namely to bring the running instance to the foreground. In my experience, this is the expected behavior for non-reentrant applications.
I discovered that the reason all the examples take the easy way out is that it can’t be done in .Net directly. To bring another window to the foreground, you have to make native Win32 API calls. Ugh!
I finally found this worth-while article on how to do what I wanted in WinForms, and combining that with the information found in this StackOverflow post, I updated the solution for Windows Presentation Foundation and encapsulated it all in a the following handy class.
using System; using System.Runtime.InteropServices; using System.Threading; using System.Windows; using System.Windows.Interop; namespace SIPC.SLS.ClaimsProcessing.ProcessorWpfApp.Application { internal class SingleInstanceEnforcementUtility : IDisposable { private Mutex _mutex; private Window _mainApplicationWindow; private bool _isDisposed; #region Win32 API Interop private const int HWND_BROADCAST = 0xffff; private const string UNIQUE_MUTEX_NAME = "{490D60C5-A51C-4c10-ADDE-47B4EECF9EE0}"; private static readonly int WM_MOVE_TO_TOP = RegisterWindowMessage("WM_MOVE_TO_TOP" + UNIQUE_MUTEX_NAME); [DllImport("user32")] private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); [DllImport("user32")] private static extern int RegisterWindowMessage(string message); #endregion public void EnforceSingleApplicationInstance() { bool isOwned; _mutex = new Mutex(true, UNIQUE_MUTEX_NAME, out isOwned); if (isOwned) return; //Broadcast to all open windows the custom move to top command //(Will be ignored by any window that does not recongize it); PostMessage((IntPtr)HWND_BROADCAST, WM_MOVE_TO_TOP, IntPtr.Zero, IntPtr.Zero); Environment.Exit(0); } public void RegisterMainApplicationWindow(Window window) { //Hook into window message processing loop to watch for our custom message _mainApplicationWindow = window; var source = PresentationSource.FromVisual(window) as HwndSource; if (source != null) source.AddHook(HandleWindowsMessage); } private IntPtr HandleWindowsMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_MOVE_TO_TOP) MoveWindowToTopOfDesktop(); return IntPtr.Zero; } private void MoveWindowToTopOfDesktop() { if (_mainApplicationWindow.WindowState == WindowState.Minimized) { _mainApplicationWindow.WindowState = WindowState.Normal; } // get our current "TopMost" value var top = _mainApplicationWindow.Topmost; // make our form jump to the top of everything _mainApplicationWindow.Topmost = true; // set it back to whatever it was _mainApplicationWindow.Topmost = top; } public void Dispose() { if(_isDisposed) return; _isDisposed = true; if(_mutex != null) _mutex.ReleaseMutex(); } } } |
With this class in place, you can a create a non-reentrant WPF application like so:
[System.STAThreadAttribute()] static void Main() { using(var sieu = new SingleInstanceEnforcementUtility()) { sieu.EnforceSingleApplicationInstance(); var app = new MyApplication(); app.InitializeComponent(); sieu.RegisterMainApplicationWindow(app.MainWindow); app.Run(); } } |
You will probably also want to change the UNIQUE_MUTEX_NAME, just in case you application ever gets installed on the same machine as mine! 🙂
Happy coding!
–Ken