diff --git a/src/Panama/App.xaml.cs b/src/Panama/App.xaml.cs index 47d50e0f..d8aaac0f 100644 --- a/src/Panama/App.xaml.cs +++ b/src/Panama/App.xaml.cs @@ -4,15 +4,15 @@ * Panama is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License v3.0 * Panama is distributed in the hope that it will be useful, but without warranty of any kind. */ -using Restless.Panama.ViewModel; using Restless.Panama.Core; using Restless.Panama.Database.Core; -using Restless.Panama.Resources; +using Restless.Panama.Database.Tables; using Restless.Panama.Utility; +using Restless.Panama.ViewModel; +using Restless.Toolkit.Controls; using System; +using System.Threading; using System.Windows; -using Restless.Panama.Database.Tables; -using Restless.Toolkit.Controls; namespace Restless.Panama { @@ -21,6 +21,11 @@ namespace Restless.Panama /// public partial class App : Application { + #region Private + private const string ApplicationId = "Panama.d9c20b23-f278-4349-a292-17e5a6abb15a"; + private static readonly Mutex AppMutex = new(true, ApplicationId); + #endregion + #region Protected methods /// /// Called when the application is starting. @@ -32,6 +37,14 @@ public partial class App : Application protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); + + /* Prevent app from being run more than once */ + if (!AppMutex.WaitOne(TimeSpan.Zero, true)) + { + NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SHOW_ACTIVE_WIN, IntPtr.Zero, IntPtr.Zero); + Environment.Exit(0); + } + try { RunApplication(e); @@ -53,6 +66,7 @@ protected override void OnExit(ExitEventArgs e) base.OnExit(e); Config.Instance.SaveFilterObjects(); DatabaseController.Instance.Shutdown(saveTables: true); + AppMutex.ReleaseMutex(); } #endregion diff --git a/src/Panama/Core/Other/NativeMethods.cs b/src/Panama/Core/Other/NativeMethods.cs new file mode 100644 index 00000000..228c001c --- /dev/null +++ b/src/Panama/Core/Other/NativeMethods.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.InteropServices; + +namespace Restless.Panama.Core +{ + /// + /// Provides native methods. + /// + internal static class NativeMethods + { + public const int HWND_BROADCAST = 0xffff; + public static readonly int WM_SHOW_ACTIVE_WIN = RegisterWindowMessage("911BEAEF-5448-4CB8-A0CF-4A839B7602D1"); + + [DllImport("user32")] + public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); + + [DllImport("user32", CharSet = CharSet.Unicode)] + public static extern int RegisterWindowMessage(string message); + } +} \ No newline at end of file diff --git a/src/Panama/Panama.csproj b/src/Panama/Panama.csproj index be26741f..0365f2bf 100644 --- a/src/Panama/Panama.csproj +++ b/src/Panama/Panama.csproj @@ -13,7 +13,7 @@ https://github.com/victor-david/panama https://github.com/victor-david/panama GPL-3.0-only - 4.0.33 + 4.0.34 App.Main.ico diff --git a/src/Panama/View/MainWindow.xaml.cs b/src/Panama/View/MainWindow.xaml.cs index 9badac15..7bc08bbd 100644 --- a/src/Panama/View/MainWindow.xaml.cs +++ b/src/Panama/View/MainWindow.xaml.cs @@ -4,15 +4,79 @@ * Panama is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License v3.0 * Panama is distributed in the hope that it will be useful, but without warranty of any kind. */ +using Restless.Panama.Core; using Restless.Toolkit.Controls; +using System; +using System.Windows; +using System.Windows.Interop; namespace Restless.Panama.View { public partial class MainWindow : AppWindow { + #region Private + private static MainWindow staticMain; + #endregion + + /************************************************************************/ + + #region Constructor + /// + /// Initializes a new instance of the class. + /// public MainWindow() { InitializeComponent(); } + #endregion + + /************************************************************************/ + + #region Protected methods + /// + protected override void OnSourceInitialized(EventArgs e) + { + base.OnSourceInitialized(e); + if (PresentationSource.FromVisual(this) is HwndSource hwndSource) + { + staticMain = this; + hwndSource.AddHook(HandleWindowMessage); + } + } + #endregion + + /************************************************************************/ + + #region Private methods + /// + /// Matches the HwndSourceHook delegate signature so it can be passed to AddHook() as a callback. + /// + /// The window handle + /// The message ID + /// The message's wParam value, historically used in the win32 api for handles and integers + /// The message's lParam value, historically used in the win32 api to pass pointers + /// A value that indicates whether the message was handled + /// + private static IntPtr HandleWindowMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + if (msg == NativeMethods.WM_SHOW_ACTIVE_WIN) + { + staticMain?.ShowMe(); + handled = true; + } + return IntPtr.Zero; + } + + private void ShowMe() + { + if (WindowState == WindowState.Minimized) + { + WindowState = WindowState.Normal; + } + bool top = Topmost; + Topmost = true; + Topmost = top; + } + #endregion } } \ No newline at end of file