Introduction

This article discusses a class that I wrote that wraps a global low level keyboard hook. The sample hooks the A and B keys, just for demonstration.

Background

I was trying to find a way for an application that I am writing to restore itself when a combination of keys was pressed. This was born from searching around for the answer.

Using the Code

First download the source, and add globalKeyboardHook.cs to your project. Then add...

using Utilities;

... to the top of the file you are going to use it in. Next add an instance of globalKeyboardHook to your class:

globalKeyboardHook gkh = new globalKeyboardHook() ;

When a globalKeyboardHook is constructed, it automatically installs the hook, so all there is left to do is add some keys for it to watch, and define some event handlers for the KeyDown and KeyUp events. I usually do this on the main form's load event handler, like this:

Here I have chosen to watch for the A and B keys, and defined handlers for the KeyUp and KeyDown events that both log to a listbox called lstLog. So whenever the user presses the A or B keys, no matter what has focus, the application will be notified. Setting e.Handled to true makes it so no other notifications for this event go out, in the sample, this effectively stops the user from typing an A or B. This can be useful in ensuring that key combinations are not also typed out when used.

You can add hooks for as many keys as you would like, just add them like above. Don't get frustrated if you add a hook for a key and it doesn't work, many of them, like Keys.Shift show up as other more specific keys, like Keys.LShiftKey or Keys.RShiftKey. Keys.Alt shows up as Keys.LMenu or Keys.RMenu, Keys.Control shows up as Keys.LControl or Keys.RControl, just to name a few.

If you would like to hook or unhook the keyboard hook at any point, just call your globalKeyboardHook's hook and unhook methods, like so:

//unhook
gkh.unhook()
//set the hook again
gkh.hook()

Points of Interest

The bulk of the work in this code is done in the globalKeyboardHook class, although it is a fairly simple piece of code itself. The hardest part of doing this was finding the correct parameters for SetWindowsHookEx.

The first parameter WH_KEYBOARD_LL is just saying that we want to hook the low level keyboard events, hookProc is the callback for the event, hInstance is a handle to User32.dll, where this event is first processed (I think). The last parameter is if you want to hook a specific thread, then you would just pass a thread id instead of using the hInstance.

Hi, I tried the sample and it worked well so without further thought I implemented it in my app. However when a key is pressed... i get the error "object reference not set to an instanc eof an object" now before you go jumping to conclusions let me tell you WHERE this error occurs... on the Application.Run(new Form1()); line in the main program file...

System.NullReferenceException: Object reference not set to an instance of an object.
at ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop
at ThreadContext.RunMessageLoopInner
at ThreadContext.RunMessageLoop
at System.Windows.Forms.Application.Run

This will occur whenever ANY key is pressed, and all I need to do is create an instance of your class and it will happen.

Now I have gotten this error before in this app, it uses directx's audio object, and upon trying to stop an already stopped audio object this weird error would happen.. therefore I'm thinking the existance of these two things are to blame.. not that it makes much sense though...

I used the "GetAsyncKeyState" dll call instead for my app. (difference between it and this is that it sniffs any key without having to hook to it, but for that reason doesn't provide a neat event, you poll it instead).

Hello, I'm using your great hook class, and it's been responding great, until I needed to use Settings in Visual Studio!
Basically, I allow the user to save his settings; the settings of the modified key functions.
Just adding this line in the code generates an annoying error:

if (Properties.Settings.Default.DisableAll == false) { }

The error I'm getting is:

A callback was made on a garbage collected delegate of type 'key preview!Utilities.globalKeyboardHook+keyboardHookProc::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.

This is really stopping me from further progress, and I am completely unable to do anything about it, even MSDN experts are not capable to providing solutions!

I was trying to capture Alt+Q. I was able to get "Alt" through Control.ModifierKeys. However, when casting lParam.vkCode to (Keys), I constantly got "MButton | Space | F17", and vkCode was always 164. (I was expecting to get "Q")

I tried with other key combinations, stil getting the same thing. Does anyone knoow what could have caused this and how to resolve it?

I added your class to my wpf project but I get an exception with the following message:

A callback was made on a garbage collected delegate of type 'BingIt!Utilities.globalKeyboardHook+keyboardHookProc::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.

So i cant make it unhook the keyboard, and when i press a button only the program registers it Any way to fix this?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Utilities;
using System.IO;

Use the "GetAsyncKeyState" dll call instead which sounds more appropriate in your case since you are hooking like every key... which isn't what hooks are really setup for, use keystate instead shouldn't have too much trouble finding an example.

Im getting the following error while trying to use this class in my app

A callback was made on a garbage collected delegate of type 'TestFlashCommunication!Utilities.globalKeyboardHook+keyboardHookProc::Invoke'. This may cause application crashes, corruption and data loss.

Hi,
I used your example to capture keystrokes. It captures them globally without any problem, but only upto a certain number of key strokes(which is random). For example, sometimes when I have pressed 280 keys it stops capturing. Sometimes 236, 182 rarely, but even 8 !! I know it might not be related to these numbers at all. (I'm running Windows Vista) but what makes it stop capturing key strokes?

Hm, instead of modifying the hookProc it'd be better to handle both the KeyUp/KeyDown events, and replace the key presses by using SendInput (http://pinvoke.net/default.aspx/user32/SendInput.html[^]) instead of SendKeys, and then setting e.handled to true. You have to handle both the KeyUp and KeyDown events for the key press to be successfully replaced.

There is not a way that I am familiar with, Interop between Managed code and Clarion tends to be pretty hard. You may be able to create a Clarion Dll and use pinvoke to call a method in the dll to create the window, passing in the handle to the C# window should allow you to tell the Clarion window that it is a child and it can reference it's parent via the handle. Most of the time when I have to do interop between the two I'm consuming Managed code from Clarion by creating a COM visible lib in C# and then using it thru OLE in Clarion, so I don't do much in this direction.

I found out a little more by experimenting with the demo and adding more threads. It turns out that the objects only work when instantiated in the main thread of the application. If you instantiate these objects in any other thread, the SetWindowsHookEx returns zero meaning the hook failed and, of course, the hook doesn't work.

it really only works w/ desktop applications, b/c it's hooking the computer that the app is running on, and it looks like you are using some asp stuff. It should compile if you tell it the full name of the border style that it's complaining about.

Does anyone have any examples of using the IEnumerable type for the HookedKeys.addrange method? I'm trying to find a nice way to either hook all keyboard presses, or create an array which does a large number of them without having to "add(keys.a), add(keys.b),... etc etc..."