AgentFire.Input.Hooks 2.0.0

dotnet add package AgentFire.Input.Hooks --version 2.0.0                
NuGet\Install-Package AgentFire.Input.Hooks -Version 2.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AgentFire.Input.Hooks" Version="2.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add AgentFire.Input.Hooks --version 2.0.0                
#r "nuget: AgentFire.Input.Hooks, 2.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install AgentFire.Input.Hooks as a Cake Addin
#addin nuget:?package=AgentFire.Input.Hooks&version=2.0.0

// Install AgentFire.Input.Hooks as a Cake Tool
#tool nuget:?package=AgentFire.Input.Hooks&version=2.0.0                

Usage

  1. Instantiate your keyboard hook.
IDisposable keyboardHook = Hardware.HookKeyboard(ev => {
    // Your actions against the event of RawKeyboardEvent (ev).
});
  1. Instantiate your mouse hook.
IDisposable mouseHook = Hardware.HookMouse(ev => {
    // Your actions against the event of RawMouseEvent (ev).
});

It is that easy. Now for the "what's the catch" part:

1. You have to call the factory (Hardware.HookKeyboard() / Hardware.HookMouse()) from a thread which has a running message loop

Assuming you are using WPF directly (from this example code:

private readonly IDisposable _keyboardHook;

private void OnKey(RawKeyboardEvent ev)
{
    // Here you go.
}

public MainWindow()
{
    InitializeComponent();
     
    // As code is run on the app's main thread, the event's callbacks will also be called on that thread (via the Message Loop mechanism).
    // InvokeAsync posts the anonymous delegate asynchronously, which is my best attempt at releasing the event's callback as fast as possible.
    _keyboardHook = Hardware.HookKeyboard(ev => Dispatcher.InvokeAsync(() => OnKey(ev)));
}

protected override void OnClosed(EventArgs e)
{
    // Don't forget to clean up.
    _keyboardHook.Dispose();

    base.OnClosed(e);
}

Assuming you are using just the WPF's engine, here is a nice way to start a new thread and the required Message Loop inside it:

private void OnMouse(RawMouseEvent ev)
{
    // Here you go.
    // This will run on the Message Loop's internal thread, so return as fast as possible.
}

(IDisposable mouseHookLoop, IDisposable mouseHook) = MessageLoop.Create(() => Hardware.HookMouse(OnMouse));

// Dispose of the hook when not needed anymore.
mouseHook.Dispose();

// Stop the message loop.
mouseHookLoop.Dispose();

3. Other notes

  1. Don't forget that Global Hooks considered a bad practice since they hurt the performance system-wide.
  2. Try to minimize your handler code. If you spend more than 1 ms on handling the message, consider using cross-thread message queue or posting a delegate elsewhere.
  3. Feedback is appreciated.
  4. Enjoy.
Product Compatible and additional computed target framework versions.
.NET net6.0-windows7.0 is compatible.  net7.0-windows was computed.  net7.0-windows7.0 is compatible.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0-windows7.0

    • No dependencies.
  • net7.0-windows7.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.0.0 226 7/29/2023
1.0.0 769 3/3/2019