SharpHook
SharpHook provides a cross-platform global keyboard and mouse hook, event simulation, and text entry simulation for .NET
Install / Use
/learn @TolikPylypchuk/SharpHookREADME
SharpHook
SharpHook provides a cross-platform global keyboard and mouse hook, event simulation, and text entry simulation for .NET. It is a wrapper of libuiohook and provides direct access to its features as well as higher-level types to work with it.
Installation
dotnet add package SharpHook
dotnet add package SharpHook.Reactive
dotnet add package SharpHook.R3
Upgrading
A migration guide is available for upgrading between major versions.
Docs
You can find more information (including the API reference) in the docs at https://sharphook.tolik.io.
Supported Platforms
SharpHook targets .NET 8+, .NET Framework 4.7.2+, and .NET Standard 2.0. The following table describes the availability of SharpHook on various platforms:
<table> <tr> <th></th> <th>Windows</th> <th>macOS</th> <th>Linux</th> </tr> <tr> <th>x86</th> <td>Yes</td> <td>N/A</td> <td>No</td> </tr> <tr> <th>x64</th> <td>Yes</td> <td>Yes</td> <td>Yes</td> </tr> <tr> <th>Arm32</th> <td>No</td> <td>N/A</td> <td>Yes</td> </tr> <tr> <th>Arm64</th> <td>Yes</td> <td>Yes</td> <td>Yes</td> </tr> </table>Platform support notes:
-
Windows 10/11 is supported. Support for Windows on Arm32 was removed in version 5.0 since it was removed in .NET 5.
-
macOS 10.15+ is supported. Mac Catalyst 13.1+ is also supported. macOS requires that the access to the Accessibility API be enabled for the application if it wants to create a global hook or simulate events.
-
Linux distributions supported by .NET are supported by SharpHook. Linux on x86 is not supported by .NET itself. Only X11 is supported – Wayland support may be coming, but it's not yet here.
[!NOTE] Support for versions of the OSes that are out of official support will be provided on a best-effort basis only and some issues that only affect older versions might not be fixed.
More info on OS support can be found in an article on OS-specific constraints.
Usage
Native Functions of libuiohook
SharpHook exposes the functions of libuiohook in the SharpHook.Native.UioHook class. The SharpHook.Data namespace
contains types which represent the data used by libuiohook.
In general, you don't need to use the native methods directly. Instead, use the higher-level interfaces and classes provided by SharpHook. However, you should still read this section to know how the high-level features work under the hood.
If you want to use the low-level functionality, you don't need to use the UioHook class directly. Instead you can use
interfaces in the SharpHook.Providers namespace. The methods in those interfaces are the same as in the UioHook
class. SharpHook.Providers.UioHookProvider implements all of these interfaces and simply calls the corresponding
methods in UioHook. This should be done to decouple your code from UioHook and make testing easier.
UioHook contains the following methods for working with the global hook:
SetDispatchProc– sets the function which will be called when an event is raised by libuiohook.Run– creates a keyboard and mouse global hook and runs it on the current thread, blocking it untilStopis called.RunKeyboard– creates a keyboard-only global hook and runs it on the current thread, blocking it untilStopis called.RunMouse– creates a mouse-only global hook and runs it on the current thread, blocking it untilStopis called.Stop– stops the global hook.
[!IMPORTANT] You have to remember that only one global hook can exist at a time since calling
SetDispatchProcwill override the previously set one. Also, running a global hook when another global hook is already running will corrupt the internal global state of libuiohook.
Additionally, UioHook contains the PostEvent method for simulating input events.
UioHook also contains the PostText method which simulates text entry. The text to simulate doesn't depend on the
current keyboard layout. The full range of UTF-16 (including surrogate pairs, e.g. emojis) is supported.
libuiohook also provides functions to get various system properties. The corresponding methods are also present in
UioHook.
Global Hooks
SharpHook provides the IGlobalHook interface along with three default implementations which you can use to control the
hook and subscribe to its events. Here's a basic usage example:
using SharpHook;
using SharpHook.Providers;
// KeyTyped events may cause system-wide side effects, so they should be disabled if unused.
UioHookProvider.Instance.KeyTypedEnabled = false; // or true
var hook = new EventLoopGlobalHook();
hook.HookEnabled += OnHookEnabled; // EventHandler<HookEventArgs>
hook.HookDisabled += OnHookDisabled; // EventHandler<HookEventArgs>
hook.KeyTyped += OnKeyTyped; // EventHandler<KeyboardHookEventArgs>
hook.KeyPressed += OnKeyPressed; // EventHandler<KeyboardHookEventArgs>
hook.KeyReleased += OnKeyReleased; // EventHandler<KeyboardHookEventArgs>
hook.MouseClicked += OnMouseClicked; // EventHandler<MouseHookEventArgs>
hook.MousePressed += OnMousePressed; // EventHandler<MouseHookEventArgs>
hook.MouseReleased += OnMouseReleased; // EventHandler<MouseHookEventArgs>
hook.MouseMoved += OnMouseMoved; // EventHandler<MouseHookEventArgs>
hook.MouseDragged += OnMouseDragged; // EventHandler<MouseHookEventArgs>
hook.MouseWheel += OnMouseWheel; // EventHandler<MouseWheelHookEventArgs>
hook.Run();
// or
await hook.RunAsync();
First, you create the hook, then subscribe to its events, and then run it. The Run method runs the hook on the current
thread, blocking it. The RunAsync() method runs the hook on a separate thread and returns a Task which is finished
when the hook is stopped. You can subscribe to events after the hook is started.
IGlobalHook contains the Stop method to stop the global hook. After stopping, the global hook can be started again
by calling the Run or RunAsync method. Calling Stop when the hook is not running won't do anything.
IGlobalHook extends IDisposable. When you call the Dispose method on a hook, it's disposed and stopped if it was
running. Once a hook has been disposed, it cannot be started again – you'll have to create a new instance. Calling
Dispose when the hook is not running won't do anything other than marking the instance as disposed.
Hook events are of type HookEventArgs or a derived type which contains more info. It's possible to suppress event
propagation by setting the SuppressEvent property to true inside the event handler. This must be done synchronously
and is only supported on Windows and macOS. You can check the event time and whether the event is real or simulated with
the EventTime and IsEventSimulated properties respectively.
[!IMPORTANT] Always use one instance of
IGlobalHookat a time in the entire application since they all must use the same static method to set the hook callback for libuiohook, so there may only be one callback at a time. Running a global hook when another global hook is already running will corrupt the internal global state of libuiohook.
You can create a keyboard-only or a mouse-only hook by passing a GlobalHookType to the hook's constructor. This makes
a real difference only on Windows where there are two different global hooks – a keyboard hook and a mouse hook. On
macOS and Linux, there is one hook for all events, and this simply enables filtering keyboard or mouse events out on
these OSes.
SharpHook provides three implementations of IGlobalHook:
-
SharpHook.SimpleGlobalHookruns all of its event handlers on the same thread on which the hook itself runs. This means that the handlers should generally be fast since they will block the hook from handling the events that follow if they run for too long. -
SharpHook.EventLoopGlobalHookruns all of its event handlers on a separate dedicated thread. On backpressure it will queue the remaining events which means that the hook will be able to process all events. This implementation should be preferred toSimpleGlobalHookexcept for very simple use-cases. But it has a downside – suppressing event propagation will be ignored since event handlers are run on another thread. -
SharpHook.TaskPoolGlobalHookruns all of its event handlers on other threads inside the default thread pool for tasks. The parallelism level of the handlers can be configured. On backpressure it will queue the remaining events which means that the hook will be able to process all events. This implementation should be preferred toSimpleGlobalHookexcept for very simple use-cases. But it has a downside – suppressing event propagation will be ignored since event handlers are run on other threads. In general,EventLoopGlobalHookshould be preferred instead, as this class provides benefits only if events should be processed in parallel, which is rarely the case.
The library also provides the SharpHook.GlobalHookBase class which you can extend to create your own implementation
of the global hook. It calls the appropriate event handlers, and you only need to implement a strategy for di
Related Skills
node-connect
343.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
92.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.3kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
