Skip to content

[Runner] Harden centralized keyboard hook against stuck/ghost keys#48570

Open
crutkas wants to merge 1 commit into
microsoft:mainfrom
crutkas:user/crutkas/fix-centralized-kb-hook-stuck-keys
Open

[Runner] Harden centralized keyboard hook against stuck/ghost keys#48570
crutkas wants to merge 1 commit into
microsoft:mainfrom
crutkas:user/crutkas/fix-centralized-kb-hook-stuck-keys

Conversation

@crutkas

@crutkas crutkas commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

Hardens the runner's centralized keyboard hook against stuck and "ghost" key activations — cases where a hotkey action fires after the key was already released, or a pending timer fires after the hook was torn down.

What this changes

  • vkCodePressed is now std::atomic<DWORD>. It is read/written from the low-level hook callback and from the timer/teardown paths; the plain DWORD was a data race.
  • Revalidate the key is still physically held before firing a held-key timer. The pressed-key timer callback now checks GetAsyncKeyState(virtualKey) & 0x8000 before invoking the action, preventing ghost activations after the user has let go.
  • Tag the injected dummy 0xFF key-up with CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG so the hook does not reprocess its own synthetic event.
  • Stop() kills all pending pressed-key timers and resets vkCodePressed before unhooking, so a timer can't fire a callback into a half-removed hook.

Testing

  • Builds Release x64 (runner / PowerToys.exe) clean against current main.

This is one of a small set of related "stuck key" hardening fixes; each stands alone.

- 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>
@github-actions

Copy link
Copy Markdown

@check-spelling-bot Report

🔴 Please review

See the 📂 files view, the 📜action log, 👼 SARIF report, or 📝 job summary for details.

❌ Errors and Warnings Count
⚠️ binary-file 1
⚠️ duplicate-pattern 2
❌ forbidden-pattern 1
⚠️ large-file 1

See ❌ Event descriptions for more information.

Some files were automatically ignored 🙈

These sample patterns would exclude them:

^src/modules/ZoomIt/ZoomIt/rnnoise/rnnoise_data_little\.c$
^src/modules/ZoomIt/ZoomIt/selfie_segmentation\.onnx$

You should consider adding them to:

.github/actions/spell-check/excludes.txt

File matching is via Perl regular expressions.

To check these files, more of their words need to be in the dictionary than not. You can use patterns.txt to exclude portions, add items to the dictionary (e.g. by adding them to allow.txt), or fix typos.

To update file exclusions, you could run the following commands

... in a clone of the git@github.com:crutkas/autoUpgradeAttempt.git repository
on the user/crutkas/fix-centralized-kb-hook-stuck-keys branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/27451218271/attempts/1' &&
git commit -m 'Update check-spelling metadata'

Forbidden patterns 🙅 (1)

In order to address this, you could change the content to not match the forbidden patterns (comments before forbidden patterns may help explain why they're forbidden), add patterns for acceptable instances, or adjust the forbidden patterns themselves.

These forbidden patterns matched content:

Should be a
\san (?=(?:[b-dfgjklpqtvwz]|h(?!onou?r|our|s[lv]|tml|ttp|ref)|n(?!ginx|grok|pm)|r(?!c)|s(?!s[ho]|vg))[a-z]|x(?!\b|[-\d]|ml))
If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant