Return to Snippet

Revision: 61662
at January 4, 2013 02:38 by skivsoft


Initial Code
namespace Utils
{
    /// <summary>
    /// This checks for another instance of an app.
    /// Use inside Main() by calling SingleInstanceCheck.Check();
    /// Include this class as a sibling to Main's class.
    /// If another instance of the app is detected, 
    /// - MainWindow of the first instance appears.
    /// - 'this' instance calls Exit.
    /// </summary>
    /// <example>
    ///    static void Main()
    ///    {
    ///       SingleInstanceCheck.Check();
    ///       Application.EnableVisualStyles();
    ///       Application.SetCompatibleTextRenderingDefault(false);
    ///       Application.Run(new Form1());
    ///    }
    /// </example>
    public static class SingleInstanceCheck
    {

        static bool FindInvisibleWindow(IntPtr hWnd, IntPtr lParam)
        {
            var wndRef = new HandleRef(lParam, hWnd);
            // skip visible windows
            if (SafeNativeMethods.IsWindowVisible(wndRef))
                return true;
            // skip windows without caption
            if (SafeNativeMethods.GetWindowTextLength(wndRef) == 0)
                return true;
            // retrieve process id for a window
            int ProcID;
            SafeNativeMethods.GetWindowThreadProcessId(wndRef, out ProcID);
            // we found or not
            if (ProcID == lParam.ToInt32())
            {
                UnsafeNativeMethods.ShowWindowAsync(wndRef, NativeMethods.SW_NORMAL);
                UnsafeNativeMethods.SetForegroundWindow(wndRef);
                return false;
            }
            return true;
        }

        static public void Check()
        {
            bool isOwnedHere = false;
            appStartMutex = new Mutex(
                true,
                Application.ProductName,
                out isOwnedHere
            );
            if (isOwnedHere) return; 

            Process me = Process.GetCurrentProcess();
            foreach (Process process in Process.GetProcessesByName(me.ProcessName)) // Debug note: Set Enable the Visual Studio Hosting Process = false.
            {
                if (process.Id != me.Id) // If the ProcessName matches but the Id doesn't, it's another instance of mine.
                {
                    if (process.MainWindowHandle != IntPtr.Zero)
                    {
                        HandleRef wndRef = new HandleRef(process, process.MainWindowHandle);
                        UnsafeNativeMethods.ShowWindowAsync(wndRef, NativeMethods.SW_NORMAL);
                        UnsafeNativeMethods.SetForegroundWindow(wndRef);
                    }
                    else
                    {
                        var callback = new SafeNativeMethods.EnumThreadWindowsCallback(FindInvisibleWindow);
                        SafeNativeMethods.EnumWindows(callback, new IntPtr(process.Id));
                        GC.KeepAlive(callback);
                    }
                    break;
                }
            }
            Environment.Exit(0); // This will kill my instance.
        }

        // volatile is intended to prevent GC. Otherwise, GC.KeepAlive(appStartMutex) might be needed.
        // This appears to be a concern in In release builds but not debug builds.
        static volatile Mutex appStartMutex;

    }

    internal static class NativeMethods
    {
        public static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);

        public const int
            SW_HIDE = 0,
            SW_NORMAL = 1,
            SW_SHOWMINIMIZED = 2,
            SW_SHOWMAXIMIZED = 3,
            SW_MAXIMIZE = 3,
            SW_SHOWNOACTIVATE = 4,
            SW_SHOW = 5,
            SW_MINIMIZE = 6,
            SW_SHOWMINNOACTIVE = 7,
            SW_SHOWNA = 8,
            SW_RESTORE = 9,
            SW_MAX = 10,
            SWP_NOSIZE = 0x0001,
            SWP_NOMOVE = 0x0002,
            SWP_NOZORDER = 0x0004,
            SWP_NOACTIVATE = 0x0010,
            SWP_SHOWWINDOW = 0x0040,
            SWP_HIDEWINDOW = 0x0080,
            SWP_DRAWFRAME = 0x0020,
            SWP_NOOWNERZORDER = 0x0200;

    }

    internal class ExternDll
    {
        private ExternDll()
        { }

        internal const string User32 = "user32.dll";
    }

    [SuppressUnmanagedCodeSecurity]
    internal static class UnsafeNativeMethods
    {
        [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
        public static extern bool SetForegroundWindow(HandleRef hWnd);

        /// <SecurityNote> 
        ///     Critical: This elevates to unmanaged code permission 
        /// </SecurityNote>
        [SecurityCritical, SuppressUnmanagedCodeSecurity]
        [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
        public static extern bool ShowWindowAsync(HandleRef hWnd, int nCmdShow);
    }

    [SuppressUnmanagedCodeSecurity]
    internal static class SafeNativeMethods
    {
        [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
        public static extern bool IsWindowVisible(HandleRef hWnd);

        [DllImport(ExternDll.User32, CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData);
        internal delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam);

        [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
        public static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId);

        [DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
        public static extern int GetWindowTextLength(HandleRef hWnd);

    }
}

Initial URL

                                

Initial Description
This snippet is based on [jimfred's snippet](http://snipplr.com/view/19272/ "ingle-instance-check using mutex. Implements a static function to be called in Program::Main()."). Second instance of application shows MainWindow of the previous one instance of application, even if it has become invisible.
This is may be usefull when the very first instance sits in the tray and hides their own MainWindow.

Initial Title
single-instance-check using mutex.  With showing previous instance MainWindow.

Initial Tags

                                

Initial Language
C#