Upload FastSeekWpf/App.xaml.cs
Browse files- FastSeekWpf/App.xaml.cs +27 -10
FastSeekWpf/App.xaml.cs
CHANGED
|
@@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
|
|
| 4 |
using System.Threading;
|
| 5 |
using System.Windows;
|
| 6 |
using System.Windows.Interop;
|
|
|
|
| 7 |
using FastSeekWpf.NativeInterop;
|
| 8 |
|
| 9 |
namespace FastSeekWpf;
|
|
@@ -13,6 +14,7 @@ public partial class App : Application
|
|
| 13 |
private const string MutexName = "FastSeekWpf_SingleInstance_Mutex";
|
| 14 |
private const uint WM_TOGGLE_WINDOW = Win32Api.WM_USER + 3;
|
| 15 |
private Mutex? _mutex;
|
|
|
|
| 16 |
private volatile bool _shutdown;
|
| 17 |
private MainWindow? _mainWindow;
|
| 18 |
private Thread? _hotkeyThread;
|
|
@@ -20,25 +22,29 @@ public partial class App : Application
|
|
| 20 |
|
| 21 |
private void OnStartup(object sender, StartupEventArgs e)
|
| 22 |
{
|
|
|
|
|
|
|
| 23 |
// Single-instance guard
|
| 24 |
_mutex = new Mutex(true, MutexName, out bool createdNew);
|
| 25 |
if (!createdNew)
|
| 26 |
{
|
|
|
|
| 27 |
IntPtr hwnd = Win32Api.FindWindowW(null, "FastSeek");
|
| 28 |
if (hwnd != IntPtr.Zero)
|
| 29 |
Win32Api.PostMessageW(hwnd, WM_TOGGLE_WINDOW, IntPtr.Zero, IntPtr.Zero);
|
|
|
|
|
|
|
| 30 |
Shutdown();
|
| 31 |
return;
|
| 32 |
}
|
|
|
|
|
|
|
| 33 |
|
| 34 |
_mainWindow = new MainWindow();
|
| 35 |
_mainWindow.Show();
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
// Create manual reset event for waking GetMessageW thread
|
| 39 |
-
_hotkeyWakeEvent = CreateEventW(IntPtr.Zero, false, false, null);
|
| 40 |
|
| 41 |
-
// Start global hotkey thread (
|
| 42 |
_hotkeyThread = new Thread(HotkeyLoop)
|
| 43 |
{
|
| 44 |
IsBackground = true,
|
|
@@ -55,23 +61,32 @@ public partial class App : Application
|
|
| 55 |
if (_hotkeyWakeEvent != IntPtr.Zero)
|
| 56 |
SetEvent(_hotkeyWakeEvent);
|
| 57 |
_hotkeyThread?.Join(1000);
|
| 58 |
-
|
|
|
|
| 59 |
_mutex?.Dispose();
|
| 60 |
if (_hotkeyWakeEvent != IntPtr.Zero)
|
| 61 |
CloseHandle(_hotkeyWakeEvent);
|
|
|
|
| 62 |
}
|
| 63 |
|
| 64 |
private void HotkeyLoop(object? param)
|
| 65 |
{
|
| 66 |
IntPtr targetHwnd = (IntPtr)(param ?? IntPtr.Zero);
|
| 67 |
-
if (targetHwnd == IntPtr.Zero)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
-
// Register
|
| 70 |
-
|
|
|
|
| 71 |
{
|
| 72 |
-
|
|
|
|
| 73 |
return;
|
| 74 |
}
|
|
|
|
| 75 |
|
| 76 |
try
|
| 77 |
{
|
|
@@ -83,6 +98,7 @@ public partial class App : Application
|
|
| 83 |
|
| 84 |
if (msg.message == Win32Api.WM_HOTKEY)
|
| 85 |
{
|
|
|
|
| 86 |
Win32Api.PostMessageW(targetHwnd, WM_TOGGLE_WINDOW, IntPtr.Zero, IntPtr.Zero);
|
| 87 |
}
|
| 88 |
else
|
|
@@ -95,6 +111,7 @@ public partial class App : Application
|
|
| 95 |
finally
|
| 96 |
{
|
| 97 |
Win32Api.UnregisterHotKey(IntPtr.Zero, 1);
|
|
|
|
| 98 |
}
|
| 99 |
}
|
| 100 |
|
|
|
|
| 4 |
using System.Threading;
|
| 5 |
using System.Windows;
|
| 6 |
using System.Windows.Interop;
|
| 7 |
+
using FastSeekWpf.Core;
|
| 8 |
using FastSeekWpf.NativeInterop;
|
| 9 |
|
| 10 |
namespace FastSeekWpf;
|
|
|
|
| 14 |
private const string MutexName = "FastSeekWpf_SingleInstance_Mutex";
|
| 15 |
private const uint WM_TOGGLE_WINDOW = Win32Api.WM_USER + 3;
|
| 16 |
private Mutex? _mutex;
|
| 17 |
+
private bool _ownsMutex;
|
| 18 |
private volatile bool _shutdown;
|
| 19 |
private MainWindow? _mainWindow;
|
| 20 |
private Thread? _hotkeyThread;
|
|
|
|
| 22 |
|
| 23 |
private void OnStartup(object sender, StartupEventArgs e)
|
| 24 |
{
|
| 25 |
+
Logger.Log("=== App startup ===");
|
| 26 |
+
|
| 27 |
// Single-instance guard
|
| 28 |
_mutex = new Mutex(true, MutexName, out bool createdNew);
|
| 29 |
if (!createdNew)
|
| 30 |
{
|
| 31 |
+
Logger.Log("Another instance is running. Sending toggle message.");
|
| 32 |
IntPtr hwnd = Win32Api.FindWindowW(null, "FastSeek");
|
| 33 |
if (hwnd != IntPtr.Zero)
|
| 34 |
Win32Api.PostMessageW(hwnd, WM_TOGGLE_WINDOW, IntPtr.Zero, IntPtr.Zero);
|
| 35 |
+
else
|
| 36 |
+
Logger.Log("Could not find existing window to toggle.");
|
| 37 |
Shutdown();
|
| 38 |
return;
|
| 39 |
}
|
| 40 |
+
_ownsMutex = true;
|
| 41 |
+
Logger.Log("Mutex acquired. Starting main window.");
|
| 42 |
|
| 43 |
_mainWindow = new MainWindow();
|
| 44 |
_mainWindow.Show();
|
| 45 |
+
// DO NOT hide on startup — let the user see the window immediately
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
+
// Start global hotkey thread (Ctrl+Shift+Space = MOD_CONTROL|MOD_SHIFT, VK_SPACE)
|
| 48 |
_hotkeyThread = new Thread(HotkeyLoop)
|
| 49 |
{
|
| 50 |
IsBackground = true,
|
|
|
|
| 61 |
if (_hotkeyWakeEvent != IntPtr.Zero)
|
| 62 |
SetEvent(_hotkeyWakeEvent);
|
| 63 |
_hotkeyThread?.Join(1000);
|
| 64 |
+
if (_ownsMutex)
|
| 65 |
+
_mutex?.ReleaseMutex();
|
| 66 |
_mutex?.Dispose();
|
| 67 |
if (_hotkeyWakeEvent != IntPtr.Zero)
|
| 68 |
CloseHandle(_hotkeyWakeEvent);
|
| 69 |
+
Logger.Log("=== App exit ===");
|
| 70 |
}
|
| 71 |
|
| 72 |
private void HotkeyLoop(object? param)
|
| 73 |
{
|
| 74 |
IntPtr targetHwnd = (IntPtr)(param ?? IntPtr.Zero);
|
| 75 |
+
if (targetHwnd == IntPtr.Zero)
|
| 76 |
+
{
|
| 77 |
+
Logger.Log("HotkeyLoop: targetHwnd is zero, exiting.");
|
| 78 |
+
return;
|
| 79 |
+
}
|
| 80 |
|
| 81 |
+
// Register Ctrl+Shift+Space (MOD_CONTROL=0x0002, MOD_SHIFT=0x0004)
|
| 82 |
+
uint modifiers = 0x0002 | 0x0004;
|
| 83 |
+
if (!Win32Api.RegisterHotKey(IntPtr.Zero, 1, modifiers, 0x20))
|
| 84 |
{
|
| 85 |
+
int err = Marshal.GetLastWin32Error();
|
| 86 |
+
Logger.Log($"RegisterHotKey failed: error {err}. Hotkey will not work.");
|
| 87 |
return;
|
| 88 |
}
|
| 89 |
+
Logger.Log("Hotkey registered: Ctrl+Shift+Space");
|
| 90 |
|
| 91 |
try
|
| 92 |
{
|
|
|
|
| 98 |
|
| 99 |
if (msg.message == Win32Api.WM_HOTKEY)
|
| 100 |
{
|
| 101 |
+
Logger.Log("Hotkey pressed, toggling window.");
|
| 102 |
Win32Api.PostMessageW(targetHwnd, WM_TOGGLE_WINDOW, IntPtr.Zero, IntPtr.Zero);
|
| 103 |
}
|
| 104 |
else
|
|
|
|
| 111 |
finally
|
| 112 |
{
|
| 113 |
Win32Api.UnregisterHotKey(IntPtr.Zero, 1);
|
| 114 |
+
Logger.Log("Hotkey unregistered.");
|
| 115 |
}
|
| 116 |
}
|
| 117 |
|