Fix(windows): dispatch hotkeys with a thread-safe function - #54
Merged
Conversation
Replace AsyncWorker callbacks queued from the polling thread with a per-environment N-API thread-safe function. Add safe teardown and Electron 29/43 physical hotkey regression tests.
There was a problem hiding this comment.
Pull request overview
Reworks Windows hotkey dispatch to safely bridge the polling thread and JavaScript environments.
Changes:
- Adds per-environment thread-safe callback dispatch and lifecycle cleanup.
- Supports safe unregister, restart, worker teardown, and queued-event disposal.
- Adds real Windows input tests across Electron 29 and 43.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
source/hook-win.cpp |
Implements thread-safe Windows dispatch and cleanup. |
source/module.cpp |
Connects per-environment state to exports. |
source/hook.h |
Declares Windows initialization support. |
test/send_input_win.cpp |
Generates real F24 input. |
test/preload_hotkey_win.js |
Hosts renderer-side hook commands. |
test/test_hotkey_win.js |
Tests callbacks, unregister, restart, and cleanup. |
test/test_hotkey_teardown_win.js |
Tests active renderer teardown. |
test/test_hotkey_worker_win.js |
Tests worker-environment teardown. |
test/run_electron_test.js |
Runs and validates Electron tests. |
.github/workflows/build.yml |
Adds Electron 29/43 Windows testing. |
CMakeLists.txt |
Builds the input helper and updates CMake requirements. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
summeroff
approved these changes
Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and context
After upgrading Desktop from Electron 29 to Electron 43, pressing a registered global hotkey on Windows can crash the renderer. The Game Overlay hotkey provides a 100% reproducible path, although the problem affects Windows hotkeys generally and occurs before the JavaScript callback or Game Overlay logic executes.
The Windows polling thread currently invokes
Napi::AsyncWorker::Queue()directly. This calls Node-API using anapi_envfrom an addon-created thread, which Node-API does not permit. Electron 43 exposes this undefined behavior as a crash innode::Environment::GetCurrent.What changed
AsyncWorkerinstances with a per-environment N-API thread-safe function.worker_threads.Testing
Added Windows tests that use
SendInputto generate real F24 key presses rather than renderer-synthetic keyboard events. They cover:worker_threadsenvironment teardown.The test matrix runs the same N-API 4 addon binary under:
Compatibility
There are no JavaScript API changes. The implementation continues to target N-API 4, so the same binary remains compatible with the previous Electron 29 runtime as well as Electron 43.
After merging, a new native package release must be published and consumed by Desktop.