Show / Hide Table of Contents

Reactive Global Hooks

If you're using Rx.NET, you can use the SharpHook.Reactive package to integrate SharpHook with Rx.NET. Using this package is preferred when possible since it's more powerful.

The Interface

SharpHook.Reactive provides the SharpHook.Reactive.IReactiveGlobalHook interface along with a default implementation which you can use to use to control the hook and subscribe to its observables. Here's a basic example:

using SharpHook.Reactive;

// ...

var hook = new SimpleReactiveGlobalHook();

hook.HookEnabled.Subscribe(OnHookEnabled);
hook.HookDisabled.Subscribe(OnHookDisabled);

hook.KeyTyped.Subscribe(OnKeyTyped);
hook.KeyPressed.Subscribe(OnKeyPressed);
hook.KeyReleased.Subscribe(OnKeyReleased);

hook.MouseClicked.Subscribe(OnMouseClicked);
hook.MousePressed.Subscribe(OnMousePressed);
hook.MouseReleased.Subscribe(OnMouseReleased);

hook.MouseMoved
    .Throttle(TimeSpan.FromSeconds(0.5))
    .Subscribe(OnMouseMoved);

hook.MouseDragged
    .Throttle(TimeSpan.FromSeconds(0.5))
    .Subscribe(OnMouseDragged);

hook.MouseWheel.Subscribe(OnMouseWheel);

hook.Run();
// or
hook.RunAsync().Subscribe();

The observables for events emit the EventArgs-derived types which are the same as in the events of IGlobalHook. Unlike IGlobalHook you can't get the sender of the event by default. If you need to pass the hook itself as well, then use a closure.

The HookEnabled and HookDisabled observables will emit a single event and then immediately complete afterwards.

The Run and RunAsync methods are basically the same as in IGlobalHook, but RunAsync returns an IObservable<Unit> instead of a Task (the observable is hot). This observable will emit a single value and then complete when the global hook is destroyed. Running the hook when it's already running is also not allowed, and the IsRunning property is also available.

IReactiveGlobalHook extends IDisposable as well and calling Dispose will destroy the global hook. As with IGlobalHook, starting a disposed instance again shouldn't be allowed. Calling Dispose when the hook is not running is safe - it just won't do anything (other than marking the instance as disposed). The IsDisposed property is also available.

The Default Implementations

SharpHook.Reactive provides two implementations of IReactiveGlobalHook:

  • SharpHook.Reactive.SimpleReactiveGlobalHook. Since we're dealing with observables, it's up to you to decide when and where to handle the events through schedulers.

  • SharpHook.Reactive.ReactiveGlobalHookAdapter adapts an IGlobalHook to IReactiveGlobalHook. All subscriptions and changes are propagated to the adapted hook. There is no default adapter from IReactiveGlobalHook to IGlobalHook.

Both classes contain a destructor which will stop the global hook if the object is not reachable anymore. All event observables of both classes will be completed when the hook is destroyed, be it from the Dispose method, or from the destructor.

Next article: Event Simulation.

  • Improve this Doc
In This Article
Back to top
Copyright (c) 2021 Anatoliy Pylypchuk
Generated by DocFx. Icon made by Freepik from www.flaticon.com