GitHub - wudaming00/wh-keyboard-ll-chromium: Windows stops delivering WH_KEYBOARD_LL events when a Chromium window has focus - a logger + writeup of a real production failure · GitHub
/" data-turbo-transient="true" />
Skip to content
Search/
Sign in<br>Sign upAppearance settings
You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.
Dismiss alert
{{ message }}
wudaming00
wh-keyboard-ll-chromium
Public
Notifications<br>You must be signed in to change notification settings
Fork
Star
master
BranchesTags
Go to file
CodeOpen more actions menu
Latest commit
History<br>1 Commit<br>1 Commit
Folders and files<br>NameNameLast commit message<br>Last commit date<br>src
src
.gitignore
.gitignore
Cargo.toml
Cargo.toml
LICENSE
LICENSE
README.md
README.md
View all files
Repository files navigation
Windows stopped delivering our WH_KEYBOARD_LL events when a Chromium window had focus
This repo documents a production failure we hit shipping a push-to-talk dictation app<br>(VocalCode) on Windows, and provides a minimal logger to test it<br>on your own machine. We are publishing it because the failure cost us a week, the usual<br>search results don't describe it, and we suspect every global-hotkey app that meets an<br>Electron/CEF/WebView2 window eventually meets some version of it.
The symptom
Our app installs a low-level keyboard hook (SetWindowsHookExW(WH_KEYBOARD_LL, …)) to<br>implement push-to-talk. It worked everywhere — terminals, editors, native apps — except:<br>when a Chromium-family window (our own WebView2 settings UI, and separately CEF/Electron<br>windows) was in the foreground, the hook callback simply stopped being invoked. No error,<br>no unhook, no GetLastError. The events were delivered to the focused application, but our<br>hook never saw them.
Release the focus to a native window: events flow again. Focus the Chromium window: silence.
The observer effect (this is the maddening part)
While debugging, we installed a second diagnostic hook to log what the first one was<br>missing — and the system started delivering events to both. Remove the diagnostic hook,<br>and the original went deaf again under the same conditions. A watched hook never misbehaves.
Because of this, a minimal repro does not reliably reproduce — the repro program itself<br>perturbs the hook chain it is trying to observe. That is exactly why this repo exists as a<br>logger you run against your own real setup rather than a guaranteed-failing test case:<br>run it alongside the actual application whose hook goes quiet, not instead of it.
What we believe is going on (hypotheses, not verdicts)
The documented contract allows this: a low-level hook that exceeds the<br>LowLevelHooksTimeout budget can be silently removed or skipped by the system, and<br>Windows 10/11 will silently stop calling hooks it considers slow. Chromium pumps input<br>very differently from native apps (raw input, a busy message loop, high-frequency<br>pointer events), which plausibly changes timing enough to trip the threshold — for the<br>whole chain or for your position in it.
Hook-chain position matters, and installing/removing any hook re-orders the chain.<br>That would explain the observer effect: the diagnostic hook changed the chain, and the<br>timing, and the outcome.
We could not find an API that reports "your hook was skipped/removed for slowness."<br>If you know one, please open an issue — that's half the reason this repo exists.
What actually fixed it for us
We stopped relying on the global hook while our own WebView2 window had focus, and<br>handled the key inside the page (a keydown listener in the webview answers the<br>push-to-talk chord itself). The global hook still serves every native window; the<br>webview serves itself. This shipped, and the failure disappeared for all users.
If your hotkey must work over other people's Chromium windows (Chrome, VS Code,<br>Slack…), keep your hook callback microscopic (post to your own thread, return<br>immediately), never log/allocate/lock inside it, and re-install the hook when you detect<br>prolonged silence while keys are clearly being typed (compare against GetAsyncKeyState<br>polling — ugly, effective).
The logger
src/main.rs installs a WH_KEYBOARD_LL hook and prints one line per event:<br>timestamp, key, and the executable + title of the foreground window. Run it, type into<br>different apps, and watch whether lines stop when a Chromium window is focused.
cargo run --release --bin khook-logger
Interpreting results:
Lines flow in every app, including Chrome: your machine/timing doesn't trip it (ours<br>usually didn't either, until the app also had audio callbacks and a webview running).
Lines stop in exactly one app family: congratulations, you've reproduced our week.
Your other hotkey app goes deaf only while this logger is NOT running: that's the<br>observer effect above, and we'd love a comment in the issues...