diff --git a/Guard.Core/InstallationContext.cs b/Guard.Core/InstallationContext.cs index 0898f74..47cc953 100644 --- a/Guard.Core/InstallationContext.cs +++ b/Guard.Core/InstallationContext.cs @@ -99,7 +99,7 @@ public static string GetOsVersionString() public static string GetAppEditionString() { - return InstallationContext.GetInstallationType() switch + return GetInstallationType() switch { InstallationType.CLASSIC_PORTABLE => "portable", InstallationType.CLASSIC_INSTALLER => "installer", @@ -107,5 +107,19 @@ public static string GetAppEditionString() _ => "unknown" }; } + + public static string GetMutexName() + { + if (installationType == InstallationType.CLASSIC_PORTABLE) + { + return "2FAGuardPortable"; + } + else if (installationType == InstallationType.MICROSOFT_STORE) + { + return "2FAGuardStore"; + } + + return "2FAGuard"; + } } } diff --git a/Guard.WPF/App.xaml.cs b/Guard.WPF/App.xaml.cs index af8dfc5..7515ebf 100644 --- a/Guard.WPF/App.xaml.cs +++ b/Guard.WPF/App.xaml.cs @@ -22,19 +22,11 @@ public partial class App : Application protected override async void OnStartup(StartupEventArgs e) { base.OnStartup(e); - string mutexName = "2FAGuard"; var installationInfo = InstallationInfo.GetInstallationContext(); InstallationContext.Init(installationInfo.installationType, installationInfo.version); - if (installationInfo.installationType == InstallationType.CLASSIC_PORTABLE) - { - mutexName += "Portable"; - } - else if (installationInfo.installationType == InstallationType.MICROSOFT_STORE) - { - mutexName += "Store"; - } + string mutexName = InstallationContext.GetMutexName(); singleInstanceMutex = new Mutex(true, mutexName, out bool notAlreadyRunning); @@ -65,23 +57,31 @@ Process process in Process.GetProcessesByName( && process.MainModule.FileName.Equals( currentProcess.MainModule.FileName ) - && !process.MainWindowHandle.Equals(IntPtr.Zero) ) { IntPtr existingWindowHandle = process.MainWindowHandle; - if (NativeWindow.IsIconic(existingWindowHandle)) + if (existingWindowHandle != IntPtr.Zero) { - NativeWindow.ShowWindow( - existingWindowHandle, - NativeWindow.ShowWindowCommand.Restore - ); + if (NativeWindow.IsIconic(existingWindowHandle)) + { + NativeWindow.ShowWindow( + existingWindowHandle, + NativeWindow.ShowWindowCommand.Restore + ); + } + + NativeWindow.SetForegroundWindow(existingWindowHandle); + focusedOtherProcess = true; + break; } - NativeWindow.SetForegroundWindow(existingWindowHandle); - - focusedOtherProcess = true; - break; + // If the process has no main window, try to bring it to the front using IPC + if (IPC.SendToFront()) + { + focusedOtherProcess = true; + break; + } } } } diff --git a/Guard.WPF/Core/IPC.cs b/Guard.WPF/Core/IPC.cs new file mode 100644 index 0000000..75ba9ca --- /dev/null +++ b/Guard.WPF/Core/IPC.cs @@ -0,0 +1,63 @@ +using System.IO.Pipes; +using System.Windows; +using Guard.Core; +using Guard.Core.Storage; + +namespace Guard.WPF.Core +{ + internal class IPC + { + internal static void InitPipeServer() + { + MainWindow mainWindow = (MainWindow)Application.Current.MainWindow; + + var pipeServerThread = new Thread(() => + { + using var pipeServer = new NamedPipeServerStream( + $"{InstallationContext.GetMutexName()}-Start-Pipe" + ); + + while (true) + { + pipeServer.WaitForConnection(); + // When a connection is received, bring the app to the front + Application.Current.Dispatcher.Invoke(() => + { + MainWindow mainWindow = (MainWindow)Application.Current.MainWindow; + mainWindow.Show(); + mainWindow.WindowState = WindowState.Normal; + mainWindow.Activate(); + + // Re-add tray icon if enabled to ensure it's visible (e.g. after a explorer.exe restart) + if (SettingsManager.Settings.MinimizeToTray) + { + mainWindow.RemoveTrayIcon(); + mainWindow.AddTrayIcon(); + } + }); + pipeServer.Disconnect(); + } + }) + { + IsBackground = true + }; + pipeServerThread.Start(); + } + + internal static bool SendToFront() + { + try + { + using var pipeClient = new NamedPipeClientStream( + $"{InstallationContext.GetMutexName()}-Start-Pipe" + ); + pipeClient.Connect(1000); + return true; + } + catch (Exception) + { + return false; + } + } + } +} diff --git a/Guard.WPF/MainWindow.xaml.cs b/Guard.WPF/MainWindow.xaml.cs index 579add2..a2ff2cf 100644 --- a/Guard.WPF/MainWindow.xaml.cs +++ b/Guard.WPF/MainWindow.xaml.cs @@ -51,6 +51,8 @@ public MainWindow(bool autostart) ); SessionSwitchEvent.Register(this); + + IPC.InitPipeServer(); } private void OnWindowLoaded()