windows-user-action-hook icon indicating copy to clipboard operation
windows-user-action-hook copied to clipboard

WM_HOTKEY hook

Open retailcoder opened this issue 8 years ago • 3 comments

Beautiful work! I think this library would be more complete if it also handled hotkeys; the API could intake a Keys flag enum value for the key combination, and internally work out the applicable +^% modifier string... or perhaps intake the hotkey string as an overload.

It also needs a way to identify hotkeys, without forcing a type on the client code - object would work, but a generic type argument would be even better. Something like this:

var key = Keys.Ctrl + Keys.Shift + Keys.R;
// generic type parameter is inferred for Register<T>
HotkeyWatcher.Register(MyHotkeyEnum.SomeCoolCommand, key);
HotkeyWatcher.Start();

When a hotkey message is captured, an event is raised and the corresponding "key" that the hotkey was registered with is sent back to the client code, who can then decide what to do.

Note: I'll submit a PR soon-ish :-)

retailcoder avatar Apr 05 '16 00:04 retailcoder

I have implemented a hotkey in past, not on low level though. Basically it was like a Collection of keys and then removing items from collection when it matches the current key press. As soon as the collection becomes empty I would raise hotkey press event. When its not empty after the number of keys expected to be pressed together consecutively I would repopulate the collection.

Just an idea and I would be happy to merge whatever the way you would like to implement it. For long I wanted to add some comments and cleanup the code, never happened.

justcoding121 avatar Apr 05 '16 01:04 justcoding121

Okay... this is harder than I expected :smiley:

@justcoding121 so I implemented what should be a HotkeyHook, but I'm getting lost into implementation details and levels of indirection - feel free to take a look here...

In Rubberduck I'm hooking the hotkeys using the VBE MainWindow's hWnd - I'm not sure how to fit that into your API, so for now I'm having that IntPtr hWnd passed as a constructor parameter to the HotkeyHook.

The API I'm shooting for is made obvious with the HotkeyHook.Register(Keys, ICommand) method, but I'm not sure it's at the right level of abstraction - still, the idea is that the client code would call Register and supply the key combination along with an ICommand implementation (that's the standard System.Windows.Input.ICommand interface), and when Start is called, the registered combos get actually registered as hotkeys, and when the WM_HOTKEY message is picked up, it runs the command if the command's CanExecute method returns true.

Or perhaps it could be made more generic, and the hotkey could be registered with any Action delegate to be invoked... I've been using an ICommand in my project, so it fits my purposes, but this is a library, and thinking again asking the client code to have an ICommand to run might not be ideal. ... actually perhaps it's best to just raise an event saying "hotkey Ctrl+R was pressed" and let the client code deal with the rest - it's just that having the ICommand there, avoids having to figure out the Keys value to pass back to the client in the HotkeyEventArgs.

Anyway it's a "first pass", and a lot of it is a lousy copy/paste job moving code from Rubberduck and then tweaking it... let me know what you think (even if that's "ew thanks but no thanks"!)


Forgot to mention - I'd like the thing to support "chords" / 2-step hotkeys at one point, e.g. Ctrl+R,R for Refactor/Rename. The watcher should then fire up an event for the client to report something like "Ctrl+R was pressed. Awaiting second key..." - and then run the command (/raise that event) when the 2nd key combo is pressed. I just haven't figured out a good way to register and handle those yet.

retailcoder avatar Apr 05 '16 06:04 retailcoder

I agree that we should use an action delegate, but if you can isolate the changes for hotkey and PR to a feature branch I can work on integrating the shared hwnd, asynchronous message handling & delegates.

Since we don't really know if this library will be used in a console app or an app with a message loop I use the helper class to lazily create a hWnd and a message loop. Its been reused for KeyboardHook, MouseHook, ClipboardHook & ApplicationWatcher since all of them needs to be run in a thread with a message loop.

justcoding121 avatar Apr 05 '16 13:04 justcoding121