From 989c2eab2373565d2724ce29d7e9db626b820b6a Mon Sep 17 00:00:00 2001 From: Noraa Junker Date: Fri, 20 Feb 2026 17:16:27 +0100 Subject: [PATCH] Fix some bugs and add debug menu --- .../Helpers/CentralizedKeyboardHookManager.cs | 30 ++- src/Runner/Helpers/QuickAccessHelper.cs | 3 +- src/Runner/Helpers/SettingsHelper.cs | 9 +- src/Runner/Helpers/TrayIconManager.cs | 241 +++++++++++++++++- .../Models/ProcessModuleAbstractClass.cs | 5 + .../FileExplorerModuleInterface.cs | 2 - .../FindMyMouseModuleInterface.cs | 18 +- .../KeyboardManagerModuleInterface.cs | 37 ++- .../PowerToysRunModuleInterface.cs | 12 +- src/Runner/NativeMethods.cs | 10 + src/Runner/Runner.cs | 19 ++ src/common/interop/KeyboardHook.cpp | 9 + .../Helpers/ModuleHelper.cs | 1 + .../Settings.UI/Strings/en-us/Resources.resw | 3 + 14 files changed, 379 insertions(+), 20 deletions(-) diff --git a/src/Runner/Helpers/CentralizedKeyboardHookManager.cs b/src/Runner/Helpers/CentralizedKeyboardHookManager.cs index 04e267c853..7609c10d61 100644 --- a/src/Runner/Helpers/CentralizedKeyboardHookManager.cs +++ b/src/Runner/Helpers/CentralizedKeyboardHookManager.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Windows.System; @@ -12,7 +14,11 @@ namespace RunnerV2.Helpers { internal static class CentralizedKeyboardHookManager { - private static readonly UIntPtr _ignoreKeyEventFlag = 0x5555; + private static readonly UIntPtr _ignoreKeyEventFlag = 0x5556; + + public static bool DebugConsole { get => _debugConsole; set => _debugConsole = value; } + + private static volatile bool _debugConsole; public static OrderedDictionary> KeyboardHooks { get; } = []; @@ -20,6 +26,11 @@ namespace RunnerV2.Helpers private static void OnKeyDown(int key) { + if (_debugConsole) + { + Console.WriteLine($"Key down: {(VirtualKey)key}, Ctrl: {_ctrlState}, Alt: {_altState}, Shift: {_shiftState}, Win: {_winState}"); + } + if ((VirtualKey)key == VirtualKey.RightMenu && _ctrlState) { _ctrlAltState = true; @@ -63,10 +74,19 @@ namespace RunnerV2.Helpers } SendSingleKeyboardInput((short)key, (uint)NativeKeyboardHelper.KeyEventF.KeyDown); + if (_debugConsole) + { + Console.WriteLine($"Key down send: {(VirtualKey)key}, Ctrl: {_ctrlState}, Alt: {_altState}, Shift: {_shiftState}, Win: {_winState}"); + } } private static void OnKeyUp(int key) { + if (_debugConsole) + { + Console.WriteLine($"Key up: {(VirtualKey)key}, Ctrl: {_ctrlState}, Alt: {_altState}, Shift: {_shiftState}, Win: {_winState}"); + } + switch ((VirtualKey)key) { case VirtualKey.Control: @@ -100,6 +120,10 @@ namespace RunnerV2.Helpers } SendSingleKeyboardInput((short)key, (uint)NativeKeyboardHelper.KeyEventF.KeyUp); + if (_debugConsole) + { + Console.WriteLine($"Key up send: {(VirtualKey)key}, Ctrl: {_ctrlState}, Alt: {_altState}, Shift: {_shiftState}, Win: {_winState}"); + } } private static bool _ctrlState; @@ -206,10 +230,6 @@ namespace RunnerV2.Helpers 0x24 => true, // VK_HOME 0x21 => true, // VK_PRIOR (Page Up) 0x22 => true, // VK_NEXT (Page Down) - 0x25 => true, // VK_LEFT - 0x26 => true, // VK_UP - 0x27 => true, // VK_RIGHT - 0x28 => true, // VK_DOWN 0x90 => true, // VK_NUMLOCK _ => false, }; diff --git a/src/Runner/Helpers/QuickAccessHelper.cs b/src/Runner/Helpers/QuickAccessHelper.cs index 9b856cdec2..8c041a5377 100644 --- a/src/Runner/Helpers/QuickAccessHelper.cs +++ b/src/Runner/Helpers/QuickAccessHelper.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Threading; using ManagedCommon; @@ -57,7 +58,7 @@ namespace RunnerV2.Helpers { try { - return !_process.HasExited; + return _process.StartInfo.FileName == string.Empty ? false : !_process.HasExited; } catch (Exception) { diff --git a/src/Runner/Helpers/SettingsHelper.cs b/src/Runner/Helpers/SettingsHelper.cs index f65215e52d..e537b1d839 100644 --- a/src/Runner/Helpers/SettingsHelper.cs +++ b/src/Runner/Helpers/SettingsHelper.cs @@ -30,6 +30,8 @@ namespace RunnerV2.Helpers /// internal static class SettingsHelper { + public static bool Debugging { get; set; } + private static readonly SettingsUtils _settingsUtils = SettingsUtils.Default; private static Process? _process; private static TwoWayPipeMessageIPCManaged? _ipc; @@ -102,6 +104,11 @@ namespace RunnerV2.Helpers public static void OnSettingsMessageReceived(string message) { + if (Debugging) + { + Console.WriteLine("Received message from settings: " + message); + } + JsonDocument messageDocument = JsonDocument.Parse(message); foreach (var property in messageDocument.RootElement.EnumerateObject()) @@ -111,7 +118,6 @@ namespace RunnerV2.Helpers case "action": foreach (var moduleName in property.Value.EnumerateObject()) { - _settingsUtils.SaveSettings(moduleName.Value.ToString(), moduleName.Name); if (moduleName.Name == "general") { switch (moduleName.Value.GetProperty("action_name").GetString()) @@ -250,6 +256,7 @@ namespace RunnerV2.Helpers if (Runner.LoadedModules.Find(m => m.Name == powertoysSettingsPart.Name) is IPowerToysModuleSettingsChangedSubscriber module && module is IPowerToysModule ptModule && ptModule.Enabled) { Logger.InitializeLogger("\\" + ptModule.Name + "\\ModuleInterface\\Logs"); + SettingsUtils.Default.SaveSettings(powertoysSettingsPart.Value.ToString(), powertoysSettingsPart.Name); module.OnSettingsChanged(); Logger.InitializeLogger("\\RunnerLogs"); } diff --git a/src/Runner/Helpers/TrayIconManager.cs b/src/Runner/Helpers/TrayIconManager.cs index 3944cf993a..b512eacc18 100644 --- a/src/Runner/Helpers/TrayIconManager.cs +++ b/src/Runner/Helpers/TrayIconManager.cs @@ -5,13 +5,16 @@ using System; using System.Diagnostics; using System.Drawing; +using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; +using PowerToys.GPOWrapper; using PowerToys.Interop; +using RunnerV2.Models; using RunnerV2.properties; using static RunnerV2.NativeMethods; @@ -94,6 +97,18 @@ namespace RunnerV2.Helpers ReportBug, Close, QuickAccess, + DebugKeyboardShortcuts, + DebugSettingsIpc, + DebugWriteUncaughtExceptionsToConsole, + DebugSendCustomMessage, + DebugActivateModule, + DebugDeactivateModule, + DebugAllEnableModule, + DebugAllDisableModule, + DebugSendCustomAction, + DebugListAllCustomActions, + DebugListAllShortcuts, + DebugFullModuleReport, } private static bool _doubleClickTimerRunning; @@ -113,6 +128,29 @@ namespace RunnerV2.Helpers public static void RegenerateRightClickMenu() { _trayIconMenu = CreatePopupMenu(); +#if DEBUG + IntPtr ipcMenu = CreateMenu(); + AppendMenuW(ipcMenu, 0u, new UIntPtr((uint)TrayButton.DebugSettingsIpc), "Toggle logging Settings IPC"); + AppendMenuW(ipcMenu, 0u, new UIntPtr((uint)TrayButton.DebugSendCustomMessage), "Send Custom Message To Runner"); + + IntPtr modulesMenu = CreateMenu(); + AppendMenuW(modulesMenu, 0u, new UIntPtr((uint)TrayButton.DebugFullModuleReport), "Full report on all modules"); + AppendMenuW(modulesMenu, 0x00000800u, UIntPtr.Zero, string.Empty); // serator + AppendMenuW(modulesMenu, 0u, new UIntPtr((uint)TrayButton.DebugActivateModule), "Activate Module by name"); + AppendMenuW(modulesMenu, 0u, new UIntPtr((uint)TrayButton.DebugDeactivateModule), "Deactivate Module by name"); + AppendMenuW(modulesMenu, 0u, new UIntPtr((uint)TrayButton.DebugAllEnableModule), "Run all enable functions of all modules"); + AppendMenuW(modulesMenu, 0u, new UIntPtr((uint)TrayButton.DebugAllDisableModule), "Run all disable functions of all modules"); + AppendMenuW(modulesMenu, 0u, new UIntPtr((uint)TrayButton.DebugSendCustomAction), "Send Custom Action"); + AppendMenuW(modulesMenu, 0u, new UIntPtr((uint)TrayButton.DebugListAllCustomActions), "List all Custom Actions"); + AppendMenuW(modulesMenu, 0u, new UIntPtr((uint)TrayButton.DebugListAllShortcuts), "List all Shortcuts"); + + AppendMenuW(_trayIconMenu, 0x2u, UIntPtr.Zero, "Debug build options:"); + AppendMenuW(_trayIconMenu, 0x10u, (UIntPtr)ipcMenu, "Settings <-> Runner IPC"); + AppendMenuW(_trayIconMenu, 0x10u, (UIntPtr)modulesMenu, "Module interfaces"); + AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.DebugKeyboardShortcuts), "Toggle logging Centralized Keyboard Hook"); + AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.DebugWriteUncaughtExceptionsToConsole), "Toggle logging Uncaught Exceptions"); + AppendMenuW(_trayIconMenu, 0x00000800u, UIntPtr.Zero, string.Empty); // separator +#endif if (SettingsUtils.Default.GetSettings().EnableQuickAccess) { AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.QuickAccess), Resources.ContextMenu_QuickAccess + "\t" + Resources.ContextMenu_LeftClick); @@ -123,7 +161,7 @@ namespace RunnerV2.Helpers AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.Settings), Resources.ContextMenu_Settings + "\t" + Resources.ContextMenu_LeftClick); } - AppendMenuW(_trayIconMenu, 0x00000800u, UIntPtr.Zero, string.Empty); // separator + AppendMenuW(_trayIconMenu, 0x00000800u, UIntPtr.Zero, string.Empty); // serator AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.Documentation), Resources.ContextMenu_Documentation); AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.ReportBug), Resources.ContextMenu_ReportBug); AppendMenuW(_trayIconMenu, 0x00000800u, UIntPtr.Zero, string.Empty); // separator @@ -222,6 +260,207 @@ namespace RunnerV2.Helpers case TrayButton.Close: Runner.Close(); break; +#if DEBUG + case TrayButton.DebugKeyboardShortcuts: + AllocConsole(); + CentralizedKeyboardHookManager.DebugConsole = !CentralizedKeyboardHookManager.DebugConsole; + break; + case TrayButton.DebugSettingsIpc: + AllocConsole(); + SettingsHelper.Debugging = !SettingsHelper.Debugging; + break; + case TrayButton.DebugWriteUncaughtExceptionsToConsole: + AllocConsole(); + Runner.DebbugingLogUncaughtExceptions = !Runner.DebbugingLogUncaughtExceptions; + break; + case TrayButton.DebugSendCustomMessage: + AllocConsole(); + Console.Write("Enter the message you want to send to the Runner process: "); + string? message = Console.ReadLine(); + if (string.IsNullOrEmpty(message)) + { + Console.WriteLine("No message entered. Aborting."); + return; + } + + SettingsHelper.OnSettingsMessageReceived(message); + break; + case TrayButton.DebugActivateModule: + AllocConsole(); + Console.Write("Enter the name of the module you want to activate: "); + string? moduleToActivate = Console.ReadLine(); + if (string.IsNullOrEmpty(moduleToActivate)) + { + Console.WriteLine("No module name entered. Aborting."); + return; + } + + SettingsHelper.OnSettingsMessageReceived("{\"module_status\": {\"" + moduleToActivate + "\": true}}"); + break; + case TrayButton.DebugDeactivateModule: + AllocConsole(); + Console.Write("Enter the name of the module you want to deactivate: "); + string? moduleToDeactivate = Console.ReadLine(); + if (string.IsNullOrEmpty(moduleToDeactivate)) + { + Console.WriteLine("No module name entered. Aborting."); + return; + } + + SettingsHelper.OnSettingsMessageReceived("{\"module_status\": {\"" + moduleToDeactivate + "\": false}}"); + break; + + case TrayButton.DebugAllEnableModule: + AllocConsole(); + foreach (var module in Runner.ModulesToLoad) + { + module.Enable(); + } + + Console.WriteLine("All enabled functions run."); + break; + case TrayButton.DebugAllDisableModule: + AllocConsole(); + foreach (var module in Runner.ModulesToLoad) + { + module.Disable(); + } + + Console.WriteLine("All disable functions run."); + break; + case TrayButton.DebugSendCustomAction: + AllocConsole(); + Console.Write("Enter the name of the module you want to send the action to: "); + string? moduleName = Console.ReadLine(); + if (string.IsNullOrEmpty(moduleName)) + { + Console.WriteLine("No module name entered. Aborting."); + return; + } + + Console.Write("Enter the name of the action you want to send: "); + string? actionName = Console.ReadLine(); + if (string.IsNullOrEmpty(actionName)) + { + Console.WriteLine("No action name entered. Aborting."); + return; + } + + Console.Write("Enter the data you want to send with the action (or leave empty for no data): "); + string? actionData = Console.ReadLine(); + if (actionData is null) + { + Console.WriteLine("No action data entered. Aborting."); + return; + } + + SettingsHelper.OnSettingsMessageReceived("{\"action\": {\"" + moduleName + "\": {\"action_name\": \"" + actionName + "\", \"value\": \"" + actionData + "\"}}}"); + break; + case TrayButton.DebugListAllCustomActions: + AllocConsole(); + Console.Write("Name of the module whose custom actions you want to list: "); + string? moduleNameForCustomActions = Console.ReadLine(); + if (string.IsNullOrEmpty(moduleNameForCustomActions)) + { + Console.WriteLine("No module name entered. Aborting."); + return; + } + + var moduleForCustomActions = Runner.ModulesToLoad.FirstOrDefault(m => m.Name.Equals(moduleNameForCustomActions, StringComparison.OrdinalIgnoreCase)); + if (moduleForCustomActions == null) + { + Console.WriteLine("No module with that name found. Aborting."); + return; + } + + if (moduleForCustomActions is not IPowerToysModuleCustomActionsProvider customActionsProvider) + { + Console.WriteLine("Module does not provide custom actions. Aborting."); + return; + } + + foreach (var customAction in customActionsProvider.CustomActions) + { + Console.WriteLine("Action name: " + customAction.Key); + } + + break; + case TrayButton.DebugListAllShortcuts: + AllocConsole(); + Console.Write("Name of the module whose shortcuts you want to list: "); + string? moduleNameForShortcuts = Console.ReadLine(); + if (string.IsNullOrEmpty(moduleNameForShortcuts)) + { + Console.WriteLine("No module name entered. Aborting."); + return; + } + + var moduleForShortcuts = Runner.ModulesToLoad.FirstOrDefault(m => m.Name.Equals(moduleNameForShortcuts, StringComparison.OrdinalIgnoreCase)); + + if (moduleForShortcuts == null) + { + Console.WriteLine("No module with that name found. Aborting."); + return; + } + + if (moduleForShortcuts is not IPowerToysModuleShortcutsProvider shortcutsProvider) + { + Console.WriteLine("Module does not provide shortcuts. Aborting."); + return; + } + + foreach (var shortcut in shortcutsProvider.Shortcuts) + { + Console.WriteLine("Shortcut: " + shortcut.Hotkey.ToString()); + } + + break; + case TrayButton.DebugFullModuleReport: + AllocConsole(); + static string GpoRuleToString(GpoRuleConfigured g) => g switch + { + GpoRuleConfigured.WrongValue => "Wrong value", + GpoRuleConfigured.Unavailable => "Unavailable", + GpoRuleConfigured.NotConfigured => "Not configured", + GpoRuleConfigured.Disabled => "Disabled", + GpoRuleConfigured.Enabled => "Enabled", + _ => "Unknown", + }; + + Console.WriteLine("============================="); + Console.WriteLine("=Full report of all modules:="); + Console.WriteLine("============================="); + foreach (var module in Runner.ModulesToLoad) + { + Console.WriteLine("Module name: " + module.Name); + Console.WriteLine("Enabled: " + module.Enabled); + Console.WriteLine("GPO configured: " + GpoRuleToString(module.GpoRuleConfigured)); + if (module is ProcessModuleAbstractClass pmac) + { + Console.WriteLine("Process name: " + pmac.ProcessName); + Console.WriteLine("Process path: " + pmac.ProcessPath); + Console.WriteLine("Launch options: " + pmac.LaunchOptions); + Console.WriteLine("Launch arguments: " + pmac.ProcessArguments); + Console.WriteLine("Is running: " + pmac.IsProcessRunning()); + } + + if (module is IPowerToysModuleCustomActionsProvider ptmcap) + { + Console.WriteLine("Custom actions: " + string.Join(", ", ptmcap.CustomActions.Keys)); + } + + if (module is IPowerToysModuleShortcutsProvider ptmscp) + { + Console.WriteLine("Shortcuts: " + string.Join(", ", ptmscp.Shortcuts.Select(s => s.Hotkey.ToString()))); + } + + Console.WriteLine("Is subscribed to settings changes: " + (module is IPowerToysModuleSettingsChangedSubscriber)); + + Console.WriteLine("-----------------------------"); + } + + break; +#endif } } } diff --git a/src/Runner/Models/ProcessModuleAbstractClass.cs b/src/Runner/Models/ProcessModuleAbstractClass.cs index 5e998ea190..1c2f1900b7 100644 --- a/src/Runner/Models/ProcessModuleAbstractClass.cs +++ b/src/Runner/Models/ProcessModuleAbstractClass.cs @@ -96,6 +96,11 @@ namespace RunnerV2.Models /// public abstract ProcessLaunchOptions LaunchOptions { get; } + public bool IsProcessRunning() + { + return Process.GetProcessesByName(ProcessName).Length > 0; + } + /// /// Ensures that atleast one process is launched. If the process is already running, does nothing. /// diff --git a/src/Runner/ModuleInterfaces/FileExplorerModuleInterface.cs b/src/Runner/ModuleInterfaces/FileExplorerModuleInterface.cs index b94e9cd143..d918ce8f0e 100644 --- a/src/Runner/ModuleInterfaces/FileExplorerModuleInterface.cs +++ b/src/Runner/ModuleInterfaces/FileExplorerModuleInterface.cs @@ -224,7 +224,6 @@ namespace RunnerV2.ModuleInterfaces string kindPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap"; changes.Add(new RegistryValueChange { - Scope = Microsoft.Win32.RegistryHive.LocalMachine, KeyPath = kindPath, KeyName = fileType, Value = fileKindType, @@ -276,7 +275,6 @@ namespace RunnerV2.ModuleInterfaces changes.Add(new RegistryValueChange { - Scope = Microsoft.Win32.RegistryHive.LocalMachine, KeyPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", KeyName = handlerClsid, Value = displayName, diff --git a/src/Runner/ModuleInterfaces/FindMyMouseModuleInterface.cs b/src/Runner/ModuleInterfaces/FindMyMouseModuleInterface.cs index b1f8c1e5ce..503a544e0c 100644 --- a/src/Runner/ModuleInterfaces/FindMyMouseModuleInterface.cs +++ b/src/Runner/ModuleInterfaces/FindMyMouseModuleInterface.cs @@ -33,14 +33,20 @@ namespace RunnerV2.ModuleInterfaces var thread = new Thread(() => { - uint version = 0x00010008; - int hr = MddBootstrapInitialize(version, 0, IntPtr.Zero); - if (hr < 0) + try { - throw new InvalidOperationException($"Windows app sdk could not be initialized for MouseJump. HR code:{hr}"); - } + uint version = 0x00010008; + int hr = MddBootstrapInitialize(version, 0, IntPtr.Zero); + if (hr < 0) + { + throw new InvalidOperationException($"Windows app sdk could not be initialized for MouseJump. HR code:{hr}"); + } - FindMyMouseMain(); + FindMyMouseMain(); + } + catch + { + } }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); diff --git a/src/Runner/ModuleInterfaces/KeyboardManagerModuleInterface.cs b/src/Runner/ModuleInterfaces/KeyboardManagerModuleInterface.cs index a95e6c0e16..e787d731de 100644 --- a/src/Runner/ModuleInterfaces/KeyboardManagerModuleInterface.cs +++ b/src/Runner/ModuleInterfaces/KeyboardManagerModuleInterface.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Threading; using Microsoft.PowerToys.Settings.UI.Library; using PowerToys.GPOWrapper; @@ -11,8 +12,10 @@ using RunnerV2.Models; namespace RunnerV2.ModuleInterfaces { - internal sealed class KeyboardManagerModuleInterface : ProcessModuleAbstractClass, IPowerToysModule + internal sealed class KeyboardManagerModuleInterface : ProcessModuleAbstractClass, IPowerToysModule, IPowerToysModuleShortcutsProvider, IPowerToysModuleSettingsChangedSubscriber { + private bool isRunning; + public string Name => "Keyboard Manager"; public bool Enabled => SettingsUtils.Default.GetSettings().Enabled.KeyboardManager; @@ -25,14 +28,46 @@ namespace RunnerV2.ModuleInterfaces public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument | ProcessLaunchOptions.RealtimePriority; + public List<(HotkeySettings, Action)> Shortcuts { get; } = []; + + private void InitializeShortcuts() + { + Shortcuts.Clear(); + Shortcuts.Add((SettingsUtils.Default.GetSettings(Name).Properties.ToggleShortcut, () => + { + if (isRunning) + { + Disable(); + } + else + { + Enable(); + } + } + )); + } + public void Disable() { + isRunning = false; using var terminateEvent = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.TerminateKBMSharedEvent()); terminateEvent.Set(); } public void Enable() { + InitializeShortcuts(); + if (!isRunning) + { + isRunning = true; + InitializeShortcuts(); + EnsureLaunched(); + } + } + + public void OnSettingsChanged() + { + InitializeShortcuts(); } } } diff --git a/src/Runner/ModuleInterfaces/PowerToysRunModuleInterface.cs b/src/Runner/ModuleInterfaces/PowerToysRunModuleInterface.cs index e24b1295ef..a88ab2386c 100644 --- a/src/Runner/ModuleInterfaces/PowerToysRunModuleInterface.cs +++ b/src/Runner/ModuleInterfaces/PowerToysRunModuleInterface.cs @@ -12,7 +12,7 @@ using RunnerV2.Models; namespace RunnerV2.ModuleInterfaces { - internal sealed class PowerToysRunModuleInterface : ProcessModuleAbstractClass, IPowerToysModule, IPowerToysModuleShortcutsProvider + internal sealed class PowerToysRunModuleInterface : ProcessModuleAbstractClass, IPowerToysModule, IPowerToysModuleShortcutsProvider, IPowerToysModuleSettingsChangedSubscriber { public string Name => "PowerToys Run"; @@ -38,9 +38,15 @@ namespace RunnerV2.ModuleInterfaces { } + public void OnSettingsChanged() + { + using var settingsChangedEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.RunSendSettingsTelemetryEvent()); + settingsChangedEvent.Set(); + } + public List<(HotkeySettings Hotkey, Action Action)> Shortcuts => [ - ( + /*( SettingsUtils.Default.GetSettings(Name).Properties.OpenPowerLauncher, () => { @@ -48,7 +54,7 @@ namespace RunnerV2.ModuleInterfaces using var invokeRunEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.PowerLauncherCentralizedHookSharedEvent()); invokeRunEvent.Set(); } - ), + ),*/ ]; } } diff --git a/src/Runner/NativeMethods.cs b/src/Runner/NativeMethods.cs index fc36cc30d7..1d51236341 100644 --- a/src/Runner/NativeMethods.cs +++ b/src/Runner/NativeMethods.cs @@ -52,6 +52,9 @@ namespace RunnerV2 [LibraryImport("user32.dll", SetLastError = true)] internal static partial IntPtr CreatePopupMenu(); + [LibraryImport("user32.dll", SetLastError = true)] + internal static partial IntPtr CreateMenu(); + [LibraryImport("user32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] internal static partial IntPtr FindWindowW(string lpClassName, string lpWindowName); @@ -342,5 +345,12 @@ namespace RunnerV2 [MarshalAs(UnmanagedType.LPWStr)] public string lpDisplayName; } + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool AllocConsole(); + + [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] + internal static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam); } } diff --git a/src/Runner/Runner.cs b/src/Runner/Runner.cs index 7d91f0b1fc..7f8e8db29f 100644 --- a/src/Runner/Runner.cs +++ b/src/Runner/Runner.cs @@ -30,6 +30,8 @@ namespace RunnerV2 /// public static nint RunnerHwnd { get; private set; } + public static bool DebbugingLogUncaughtExceptions { get; set; } + public const string TrayWindowClassName = "pt_tray_icon_window_class"; /// @@ -142,6 +144,10 @@ namespace RunnerV2 catch (Exception e) { Logger.LogError("Uncaught error in message loop: ", e); + if (DebbugingLogUncaughtExceptions) + { + Console.WriteLine("Uncaught error in message loop: " + e.Message + "\n" + e.StackTrace); + } } // Supress duplicate handling of HOTKEY messages @@ -157,6 +163,10 @@ namespace RunnerV2 catch (Exception e) { Logger.LogError("Uncaught error in message handling: ", e); + if (DebbugingLogUncaughtExceptions) + { + Console.WriteLine("Uncaught error in message loop: " + e.Message + "\n" + e.StackTrace); + } } } @@ -321,6 +331,8 @@ namespace RunnerV2 } } + private static bool _trayMenuCommandProcessed; + /// /// Handles Windows messages sent to the tray window. /// @@ -332,6 +344,13 @@ namespace RunnerV2 TrayIconManager.ProcessTrayIconMessage(lParam); break; case (uint)WindowMessages.COMMAND: + if (_trayMenuCommandProcessed) + { + _trayMenuCommandProcessed = false; + break; + } + + _trayMenuCommandProcessed = true; TrayIconManager.ProcessTrayMenuCommand((nuint)wParam); break; case (uint)WindowMessages.WINDOWPOSCHANGING: diff --git a/src/common/interop/KeyboardHook.cpp b/src/common/interop/KeyboardHook.cpp index 5b8415c68d..0dc874a790 100644 --- a/src/common/interop/KeyboardHook.cpp +++ b/src/common/interop/KeyboardHook.cpp @@ -66,6 +66,15 @@ namespace winrt::PowerToys::Interop::implementation { if (nCode == HC_ACTION) { + const auto* info = reinterpret_cast(lParam); + + // Don't swallow injected keys (e.g., SendInput / KEYEVENTF_UNICODE from modules like PowerAccent). + // Let them propagate normally. + if ((info->flags & LLKHF_INJECTED) != 0) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + std::vector instances_copy; { /* Use a copy of instances, to iterate through the copy without needing to maintain the lock */ diff --git a/src/settings-ui/Settings.UI.Library/Helpers/ModuleHelper.cs b/src/settings-ui/Settings.UI.Library/Helpers/ModuleHelper.cs index 561cbb15c0..01c61b64f7 100644 --- a/src/settings-ui/Settings.UI.Library/Helpers/ModuleHelper.cs +++ b/src/settings-ui/Settings.UI.Library/Helpers/ModuleHelper.cs @@ -188,6 +188,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Helpers MouseWithoutBordersSettings.ModuleName => ModuleType.MouseWithoutBorders, NewPlusSettings.ModuleName => ModuleType.NewPlus, PeekSettings.ModuleName => ModuleType.Peek, + PowerDisplaySettings.ModuleName => ModuleType.PowerDisplay, PowerRenameSettings.ModuleName => ModuleType.PowerRename, PowerLauncherSettings.ModuleName => ModuleType.PowerLauncher, PowerAccentSettings.ModuleName => ModuleType.PowerAccent, diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index f7e035e339..7a403eb64a 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -5809,4 +5809,7 @@ Text uses the current drawing color. Sample + + Default + \ No newline at end of file