Full Changelog: https://github.com/justcoding121/event-hook/compare/v2.0.0...v3.0.0
event-hook
justcoding121MIT活跃维护跨平台 .NET 库,用来订阅 Windows、macOS、Linux 上的全局用户操作,包括键盘、鼠标、剪贴板、窗口、打印任务和热键,方便做行为监听类应用。
A .NET library to subscribe to global user actions on Windows, macOS, and Linux: keyboard, mouse, clipboard, application windows, print jobs, and hotkeys.
README 里没有找到可直接复制的安装命令,请到 README 或官方文档查看安装方式。
README 没有说明硬件要求。
项目说明
这个项目的说明还在生成中,先参考下面「最近发版」和 README。
- v3.0.0EventHook 3.0.0最新正式版2026-09-08 · 2026-09-08 14:01 UTC
- v3.0.0-beta.2EventHook 3.0.0-beta.2pre-release2026-09-08 · 2026-09-08 13:33 UTC
查看更新说明
- v3.0.0-betaEventHook 3.0.0-betapre-release2026-09-08 · 2026-09-08 11:13 UTC
查看更新说明
- v2.0.0EventHook 2.0.02026-09-07 · 2026-09-07 18:23 UTC
查看更新说明
Breaking
- Targets
net10.0-windowsonly (dropped .NET Framework 4.5). Existing 1.4.x consumers should stay on 1.4.113.
Highlights
HotkeyWatcher- Mouse-move filter (
IncludeMouseMove, #28) - Clipboard images/files
- Dialog / MsgBox tracking and
AppWindowFilter - PDF / virtual printers
- x64/ARM64 P/Invoke pointer-size fixes
- Reliable
Stop/Dispose - GitHub Actions CI, DocFX, SonarCloud
Full Changelog: https://github.com/justcoding121/windows-user-action-hook/compare/1.4.113...v2.0.0
- Targets
- v2.0.0-betaEventHook 2.0.0-betapre-release2026-08-31 · 2026-08-31 12:56 UTC
查看更新说明
What's Changed
- Expose MouseData and fix an issue with AsyncConcurrentQueue by @Jabe in https://github.com/justcoding121/windows-user-action-hook/pull/20
New Contributors
- @Jabe made their first contribution in https://github.com/justcoding121/windows-user-action-hook/pull/20
Full Changelog: https://github.com/justcoding121/windows-user-action-hook/compare/1.4.39...v2.0.0-beta
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
EventHook
A .NET library to subscribe to global user actions across Windows, macOS, and Linux: keyboard, mouse, clipboard, application windows, print jobs, and hotkeys.
- API Documentation (DocFX site on
develop)
Install
dotnet add package EventHook
| Platform | Target framework |
|---|---|
| Windows | net10.0-windows |
| macOS / Linux | net10.0 |
Windows applications must target net10.0-windows. The portable net10.0 package is for macOS and Linux.
Sample
using System;
using (var eventHookFactory = new EventHookFactory())
{
var keyboardWatcher = eventHookFactory.GetKeyboardWatcher();
var kb = keyboardWatcher.Start();
if (!kb.Success)
{
Console.WriteLine(kb); // PermissionDenied / PrivilegeRequired / etc.
}
else
{
keyboardWatcher.OnKeyInput += (s, e) =>
Console.WriteLine($"Key {e.KeyData.EventType} of {e.KeyData.Keyname}");
}
var mouseWatcher = eventHookFactory.GetMouseWatcher();
mouseWatcher.IncludeMouseMove = false; // default is false
mouseWatcher.Start().ThrowIfFailed();
mouseWatcher.OnMouseInput += (s, e) =>
Console.WriteLine($"Mouse {e.Message} at {e.Point.x},{e.Point.y}");
var clipboardWatcher = eventHookFactory.GetClipboardWatcher();
clipboardWatcher.Start().ThrowIfFailed();
clipboardWatcher.OnClipboardModified += (s, e) =>
Console.WriteLine($"Clipboard {e.DataFormat}: {e.Data}");
var applicationWatcher = eventHookFactory.GetApplicationWatcher();
applicationWatcher.Start().ThrowIfFailed();
applicationWatcher.OnApplicationWindowChange += (s, e) =>
Console.WriteLine($"{e.ApplicationData.AppName} was {e.Event}");
var printWatcher = eventHookFactory.GetPrintWatcher();
printWatcher.Start().ThrowIfFailed();
printWatcher.OnPrintEvent += (s, e) =>
Console.WriteLine($"Printer {e.EventData.PrinterName} pages={e.EventData.Pages}");
var hotkeyWatcher = eventHookFactory.GetHotkeyWatcher();
hotkeyWatcher.Start().ThrowIfFailed();
hotkeyWatcher.Register("demo", new Hotkey(KeyModifiers.Control | KeyModifiers.Alt, EventKey.H))
.ThrowIfFailed();
hotkeyWatcher.OnHotkeyPressed += (s, e) =>
Console.WriteLine($"Hotkey {e.Id} ({e.Hotkey})");
Console.ReadLine();
}
OS hook callbacks only copy a lightweight snapshot and return immediately. Decoding and user event handlers run on a dedicated offload path so input is never blocked.
Permissions and failures
Start() / Register() return HookStartResult. Check Success, or call ThrowIfFailed(). IsRunning is true only after a successful install.
| Reason | Typical cause |
|---|---|
PermissionDenied | macOS Input Monitoring / Accessibility (TCC) |
PrivilegeRequired | Linux /dev/input not readable (add user to input group) |
DisplayUnavailable | No DISPLAY / session when required (Linux/macOS). Not used for a missing Win32 pump. |
NotSupportedOnPlatform | Feature needs X11 on Linux Wayland-only, or wrong TFM on Windows |
AlreadyInUse | Hotkey already registered by another process |
NativeFailure | Native API failed after permissions were OK |
If EventHookFactory cannot start its own Windows message pump (or macOS cannot start its CFRunLoop), construction throws TimeoutException. That is not a HookStartResult — there is no HookFailureReason for “pump missing.”
See examples/MAC.md and examples/LINUX.md for host setup.
Message pump / event loop
Hooks need an OS event loop. Callers usually do not create one: the factory (Windows) or platform hosts (macOS / Linux) start it.
| Platform | Who pumps | What you must do |
|---|---|---|
| Windows | EventHookFactory | Nothing in console/service code. Construct the factory on an STA UI thread or let it create a background STA WinForms loop (EventHook.MessagePump). |
| macOS | Library CFRunLoop (EventHook.Mac.CFRunLoop) | Nothing. The HWND argument is ignored. |
| Linux X11 | Library XNextEvent thread | Need DISPLAY (or Xvfb). HWND is ignored. |
| Linux evdev | /dev/input reads (no X loop) | User must be in the input group. Clipboard / windows / hotkeys still need X11. |
Windows details
- Keyboard and mouse (
WH_KEYBOARD_LL/WH_MOUSE_LL) are installed on the factory pump thread. That thread must keep pumping or Windows stops delivering. - Clipboard (
WM_CLIPBOARDUPDATE), hotkeys (WM_HOTKEY), and application/shell hooks are HWND messages on the same pump. WindowHookExdoes not use the factory pump. CallStart()from a thread that already pumps messages (your UI thread).- Hosted COM / Office add-ins: construct the factory on the STA UI thread, or pass that window’s HWND. A provided HWND must keep pumping. EventHook registers hotkeys on that handle but does not subclass it — if you pass a host HWND, your
WndProcmust dispatchWM_HOTKEYor the watcher will reportIsRunningand still never fire. - Prefer the default
new EventHookFactory()(library-owned pump) unless you are hosting and will forward messages.
Application window filter (Windows)
EventHook.Helpers.AppWindowFilter.IncludeWindowsWithoutSysMenu = true;
EventHook.Helpers.AppWindowFilter.IncludeDialogs = true;
EventHook.Helpers.AppWindowFilter.CustomFilter = hwnd => true;
Hosted apps (COM / Office add-ins on Windows)
Prefer constructing the factory on an STA UI thread, or pass an existing message-pump HWND:
using var factory = new EventHookFactory(hostMainWindowHandle);
Development
- .NET 10 SDK
- Windows:
dotnet build src/Event.Hook.sln -c Release - Portable (macOS/Linux CI): build the library, tests, and console example projects (not the full Windows-only solution)
dotnet test tests/Event.Hook.Testsdotnet test tests/Event.Hook.IntegrationTestsdotnet test tests/Event.Hook.E2ETests --filter Category=E2E- Docs:
dotnet tool restorethendocfx .github/docfx.json
Release branches
| Branch | NuGet | Notes |
|---|---|---|
develop | (no publish) | CI on Windows + macOS + Linux; SonarCloud + DocFX on develop push only |
beta | {VersionPrefix}-beta.2 | Same CI, then publish prerelease (3.0.0-beta already shipped) |
stable / tag v* | {VersionPrefix} | Same CI, then stable release + GitHub Pages docs |
Publishing uses NuGet Trusted Publishing (NUGET_USER on the nuget-publish environment).
Version 3.0 notes
Breaking: multi-platform (net10.0-windows + net10.0), Start()/Register() return HookStartResult, portable Hotkey replaces WinForms Keys, IncludeMouseMove defaults to false, non-blocking OS hook offload.
Repository renamed from windows-user-action-hook to event-hook. Solution/projects use Event.Hook.*; NuGet package id remains EventHook.