Skip to content

Commit 209d089

Browse files
crutkasCopilot
andcommitted
fix(runner): Harden centralized keyboard hook against stuck keys
- Tag dummy 0xFF SendInput with dwExtraInfo to prevent cross-hook interference - Skip LLKHF_INJECTED events from OSK/macro tools (dwExtraInfo==0) - Revalidate key state in PressedKeyTimerProc before firing action - Kill pending timers and reset state in Stop() - Make vkCodePressed atomic to fix data race Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d7d1e54 commit 209d089

1 file changed

Lines changed: 21 additions & 2 deletions

File tree

src/runner/centralized_kb_hook.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "pch.h"
22
#include "centralized_kb_hook.h"
3+
#include <atomic>
34
#include <common/debug_control.h>
45
#include <common/utils/winapi_error.h>
56
#include <common/logger/logger.h>
@@ -42,7 +43,7 @@ namespace CentralizedKeyboardHook
4243

4344
// keep track of last pressed key, to detect repeated keys and if there are more keys pressed.
4445
const DWORD VK_DISABLED = CommonSharedConstants::VK_DISABLED;
45-
DWORD vkCodePressed = VK_DISABLED;
46+
std::atomic<DWORD> vkCodePressed{ VK_DISABLED };
4647

4748
// Save the runner window handle for registering timers.
4849
HWND runnerWindow;
@@ -72,7 +73,12 @@ namespace CentralizedKeyboardHook
7273
{
7374
if (it.idTimer == idTimer)
7475
{
75-
it.action();
76+
// Revalidate that the key is still physically held before firing.
77+
// This prevents ghost activations after the key was already released.
78+
if (GetAsyncKeyState(static_cast<int>(it.virtualKey)) & 0x8000)
79+
{
80+
it.action();
81+
}
7682
}
7783
}
7884

@@ -177,6 +183,7 @@ namespace CentralizedKeyboardHook
177183
dummyEvent[0].type = INPUT_KEYBOARD;
178184
dummyEvent[0].ki.wVk = 0xFF;
179185
dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP;
186+
dummyEvent[0].ki.dwExtraInfo = PowertoyModuleIface::CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
180187
SendInput(1, dummyEvent, sizeof(INPUT));
181188

182189
// Swallow the key press
@@ -263,6 +270,18 @@ namespace CentralizedKeyboardHook
263270

264271
void Stop() noexcept
265272
{
273+
// Kill all pending pressed-key timers before unhooking to prevent
274+
// ghost callbacks firing after the hook is removed.
275+
{
276+
std::unique_lock lock{ pressedKeyMutex };
277+
for (const auto& it : pressedKeyDescriptors)
278+
{
279+
KillTimer(runnerWindow, it.idTimer);
280+
}
281+
}
282+
283+
vkCodePressed = VK_DISABLED;
284+
266285
if (hHook && UnhookWindowsHookEx(hHook))
267286
{
268287
hHook = NULL;

0 commit comments

Comments
 (0)