0

I'm trying to bring up the main window when trying to reopen the app, it works when it's in a background or minimized state but it doesn't work when the window is in a hidden state!

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;

namespace SingleInstanceTest
{
    public partial class Program
    {
        private static Mutex? _mutex;

        [STAThread]
        public static void Main()
        {
            u_mutex = new Mutex(true, GetApplicationMutexName(), out var isNewInstance);
            if (isNewInstance)
            {
                var app = new App();
                app.InitializeComponent();
                app.Run();

                app.Exit += (sender, e) =>
                {
                    _mutex.ReleaseMutex();
                    _mutex.Dispose();
                };
            }
            else
            {
                // var handle = NativeMethods.FindWindow(null, "MainWindow");
                // if (handle != IntPtr.Zero)
                // {
                //     if (NativeMethods.IsIconic(handle))
                //     {
                //         // NativeMethods.SendMessage(handle, NativeMethods.WM_SYSCOMMAND, (IntPtr)NativeMethods.SC_RESTORE, (IntPtr)0);
                //         NativeMethods.ShowWindowAsync(handle, WindowShowStyle.Restore);
                //     }
                //     NativeMethods.SetForegroundWindow(handle);
                // }
                var firstInstanceProcess = GetFirstInstanceProcess();
                if (firstInstanceProcess != null)
                {
                    if (NativeMethods.IsIconic(firstInstanceProcess.MainWindowHandle))
                    {
                        // NativeMethods.SendMessage(firstInstanceProcess.MainWindowHandle, NativeMethods.WM_SYSCOMMAND, (IntPtr)NativeMethods.SC_RESTORE, (IntPtr)0);
                        NativeMethods.ShowWindowAsync(firstInstanceProcess.MainWindowHandle, WindowShowStyle.Restore);
                    }
                    NativeMethods.SetForegroundWindow(firstInstanceProcess.MainWindowHandle);
                }
            }
        }

        private static string GetApplicationMutexName()
        {
            return $"Global\\{Assembly.GetExecutingAssembly().GetType().GUID}";
        }

        private static Process? GetFirstInstanceProcess()
        {
            var currentProcess = Process.GetCurrentProcess();
            var processes = Process.GetProcessesByName(currentProcess.ProcessName);
            foreach (var process in processes)
            {
                if (process.Id != currentProcess.Id)
                {
                    return process;
                }
            }
            return null;
        }
    }

    internal static class NativeMethods
    {
        public const int WM_SYSCOMMAND = 0x0112;
        public const int SC_RESTORE = 0xF120;

        [DllImport("user32.dll", SetLastError = true)]
        internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsIconic(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ShowWindowAsync(IntPtr hWnd, WindowShowStyle nCmdShow);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindowInternal(IntPtr hWnd);
    }

    internal enum WindowShowStyle : uint
    {
        Hide = 0,
        ShowNormal = 1,
        ShowMinimized = 2,
        ShowMaximized = 3,
        Maximize = 3,
        ShowNormalNoActivate = 4,
        Show = 5,
        Minimize = 6,
        ShowMinNoActivate = 7,
        ShowNoActivate = 8,
        Restore = 9,
        ShowDefault = 10,
        ForceMinimized = 11
    }
}

Does anyone have a solution to my problem or a complete library for Single Instance like what is available in .NET Framework, as on my test on this application https://github.com/IgrisModz/WPFSingleInstance/tree/master ?

PS: I had tried with SingleInstanceCore as I'm using .NET 6 but couldn't find how to get the desired behavior.

  • You need to control the lifetime of the mutex explicitly. It should be better to have it as a field and dispose it on the application exit since disposing it will release the memory. – Eldar Aug 23 '23 at 10:26
  • As a note, `app.Run()` will not return as long as the application is running. This means that your Exit handler may not be attached in time, and that your original code was ok with regard to the Mutex. – Clemens Aug 23 '23 at 11:18
  • The code has a race bug, it calls SetForegroundWindow() on a window that might still be minimized. Change ShowWindowAsync to ShowWindow. – Hans Passant Aug 23 '23 at 11:37
  • Why is your window in a minimized state in the first place? I would consider sending a message to your open process instead of using the win32 API, using a waitHandle or some other IPC method. That might make it easier to implement the behavior you want. – JonasH Aug 23 '23 at 11:51

1 Answers1

0

I was able to find what I was looking for via this library: https://github.com/novotnyllc/SingleInstanceHelper/tree/master

I also had this sample: https://github.com/gerardo-lijs/SingleInstance-WPF-Sample it worked but didn't really fit my needs