Windows silently stops delivering WH_KEYBOARD_LL hooks when Chromium has focus

wudmaing001 pts0 comments

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...

hook window chromium windows focus events

Related Articles