Keyboard Hooking With C#

This post is obsolete. Check out the new version.

When I first started coding QuickShift, I stumbled upon a blog post which described a Hotkey class the author had written based on the RegisterHotkey and UnregisterHotkey Windows API functions. I found it because someone had posted a link on this StackOverflow question which, as of this writing, is still the number two Google hit for “C# hotkey”. I really liked the way it was implemented and ended up using it with minimal modification as the parent class for my own hotkey implementation in QuickShift. But recently, as I considered how to rework QuickShift to make it more useful in Windows 7, I decided I need to move beyond hotkeys. So even though there are roughly one BILLION .NET-based keyboard hook implementations out there—in one form or another—I decided I wanted to write my own. It’s pretty obvious that I was heavily influenced by the Hotkey class referenced above. I can only hope that someone finds my KeyboardHook class as useful as I found that Hotkey class. But my debt to the Hotkey guy isn’t the only one I need to pay. Stephen Toub’s blog post conveniently laid out how to use all the necessary building blocks for a keyboard hook. And, even though I discovered this code project post by Emma Burrows after I was nearly finished with my own hook, I did borrow a couple of her ideas. Her class is actually very similar to what I produced. After I found it I almost regretted spending the time writing my own, but there are certain design choices I made that illustrate a slightly different agenda, making it worthwhile after all.

For one thing, I decided not to allow multiple hooks to have the same key combinations assigned to them. It didn’t seem logical. Instead, I specifically designed the KeyboardHook to handle multicast events. So if for whatever reason you wanted to assign more than one method to a hook’s Pressed event, that’s totally doable. Plus, all hook events execute asynchronously. That wasn’t something I intended to do from the outset, but I found that if the hook callback method didn’t return right away, Windows seemed to reassert its responsibility for the keystrokes. I discovered that during testing when I put a message box in the callback. It was supposed to display the box and then block further processing of the key strokes. I got the message box, but still got the keystroke output as well. (I was typing in notepad.) When I replaced the message box call with a Console.WriteLine()it worked correctly, so my assumption is that Windows found the delay induced by a modal dialog box unacceptable. Calling the events asynchronously resolved that issue. [Edit: My assumption was correct. There is a default hook timeout somewhere in the neighborhood of 300ms.]

My KeyboardHook uses a modified version of the Windows.Forms.Keys enumeration called EnhancedKeys. Since the original enumeration doesn’t include a modifier for the Windows Logo key, I added it. Incidentally, I just noticed that WPF has its own keys enumeration that does include the Windows Logo modifier key. I guess I can add that to my growing list of reasons to learn WPF. One last thing worth stating: if you hate out parameters, too bad! =P

Anyway, here’s the kitchen sink exposé of my hook class. You can download the code here. Feel free to use it, abuse it, distribute it, alter it, remove my out parameter, whatever. Post your thoughts in the comments below.

16 Comments Keyboard Hooking With C#

  1. Raven

    Hello, I’m kinda new to developing gui applications.
    (Though not from programming languages)

    And I’m trying to make a small hot keys application.

    I want to use your class (and of course, give due credits) but I’m having a problem on how to implement this:

    if the user pressed LShift + Q
    then myLabel.content = “Content Here” (in WPF).

    How could I do that?

    Thanks and more success to your applications! :)

    Reply
    1. Nick Spreitzer

      Hi,

      Are you just asking how to handle a key press event using this hook class?

      KeyboardHook hook = new KeyboardHook();
      hook.TrySetKeys(EnhancedKeys.Shift | EnhancedKeys.Q);
      hook.Pressed += (s,e) => myLabel.content = “Content Here”;
      hook.Engage();

      Let me know if I misunderstood your question.

      -Nick

      Reply
  2. Nathan

    Hi,

    I’ve tried including your code in a VSTO add-in application. I’m getting this error:

    CallbackOnCollectedDelegate was detected
    Message: A callback was made on a garbage collected delegate of type ‘!.NativeMethods+LowLevelKeyboardProc::Invoke’. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.

    Can you help me with this? I tried your code on a simple Windows form and it works.

    Reply
    1. Nick Spreitzer

      Hey Nathan, I wouldn’t be able to give you an exact answer without looking at how you integrated my code into your project, but the error message you’re getting seems pretty clear: The callback delegate is getting garbage collected when it shouldn’t be. You need to change your code so that it’s kept alive.

      Reply
  3. Nathan

    Hi HeadMinion,

    You are right. I debugged my application and discovered it was being garbage collected. I was able to fix it already.

    I have a different question this time, is disengaging & re-engaging the hooks on a different thread going to cause a different? I noticed that disengaging them on a thread works but after putting the thread to sleep and re-engaging them, the hooks don’t work. Checking the value of IsEngage returns true but the code itself is not being called.

    Thanks a lot! This article is really helpful.

    Reply
    1. Nick Spreitzer

      Well, it depends. :) It “shouldn’t” matter if another thread accesses the hook, but I did not design that hook class with thread safety in mind. As always, multithreading requires an extra degree of caution; perhaps something funky is happening with your design which is rendering the hook unusable. Again, it’s hard for me to say without looking at your code.

      Reply
  4. Nathan

    Hi HeadMinion,

    This is Nathan again. When I tested your code on Windows XP, on this code:

    this.hookID = SetWindowsHookEx(
    Constants.WH_KEYBOARD_LL,
    lowLevelKeyboardProcDelegate,
    IntPtr.Zero,
    0);

    HookID is always zero, so I get the throw new Win32Exception(Marshal.GetLastWin32Error()) exception. Do you have any idea why this happens? It works fine on Vista and 7. Thanks!

    Reply
  5. Nick Spreitzer

    Hey Nathan,

    This post needs to be updated. I’ve overhauled this hook class because of several issues I discovered while getting it ready for QuickShift 2.0. I’ll try to get an update (with new code) posted within a couple days…

    – Nick

    Reply
  6. Izzy

    Hello HeadMinion,

    I’m starting a new project and your hook code is the best I’ve found. Can you please let us know how soon can we can have the overhauled code?

    Reply
    1. Nick Spreitzer

      Hi Izzy,

      I’ll put it up this weekend. Check back on Monday. The class has undergone some significant changes which made it more practical to use and includes a pretty big bug fix. :-)

      – Nick

      Reply
  7. Archana

    Hi,

    I am trying to do something similar and need to hook up the Cntrl Z event. This a MS word add in which is being coded in C#. Aftre hooking up, I have added a message box (for the time being). When I press Cntrl Z, it gets hooked and the msg box is displayed. But after I close the message box, “Z” gets written onto my word!
    I need your advice on how to handle the last part ie. Not allowing “Z” to be written on to my word as I have already handled it.

    Reply
  8. Nathan

    Hi HeadMinion,

    This post really rocks. If you update it this weekend, please include the changes and/or improvements/enhancements. :) Thanks!

    Reply

Leave a Reply