Determining the state of modifier keys when hooking keyboard input

As part of a utility I was writing recently I wanted to hook keyboard input. In particular I was interested in global hooks (ie input to any program, not just mine), and I needed to handle complex key inputs, for example Ctrl-Alt-G, i.e. keys plus modifiers. The usual way to do this is to use SetWindowsHookEx.

Looking around I quickly found a class over on CodeProject here. The globalKeyboardHook class deals with the hooking, allows you to specify which keys you are interested in and disregards the rest. The events are raised using the delegate type KeyEventHandler. KeyEventHandler uses KeyEventArgs and has easily referenced properties for each of the modifier keys. So far so good.

However I found that the instance of KeyEventArgs I was receiving in my event handler never had the modifier properties set to true. Looking at the code for the class I couldn’t see anything there that would set the properties in the call back function for SetWindowsHookEx.

The pertinent section of the original globalKeyboardHook class looks like this:

    public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) {
        if (code >= 0) {

            Keys key = (Keys)lParam.vkCode;

            if (HookedKeys.Contains(key)) {

                KeyEventArgs kea = new KeyEventArgs(key);

                if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null)) {
                    KeyDown(this, kea) ;
                } else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null)) {
                    KeyUp(this, kea);
                }
                if (kea.Handled)
                    return 1;
            }
        }
        return CallNextHookEx(hhook, code, wParam, ref lParam);
    }

While this code does return a KeyEventArgs, there is no effort made to populate the modifier indicating properties on the class.

Googling around on the problem (for quite a while!), I found another reference to another WinApi command, GetKeyState in this CodeProject article. There is documentation for GetKeyState on the MSDN Dev Center. GetKeyState is declared as follows:

    /// <summary>
    /// Gets the state of modifier keys for a given keycode.
    /// </summary>
    /// <param name="keyCode">The keyCode</param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
    public static extern short GetKeyState(int keyCode);

    //Modifier key vkCode constants
    private const int VK_SHIFT = 0x10;
    private const int VK_CONTROL = 0x11;
    private const int VK_MENU = 0x12;
    private const int VK_CAPITAL = 0x14;

We use a method to call GetKeyState for each vk constant above. It then adds each active key to the key code, so that, when KeyEventArgs is populated from keyCode, the modifier properties are set as we would expect.

    /// <summary>
    /// Checks whether Alt, Shift, Control or CapsLock
    /// is pressed at the same time as the hooked key.
    /// Modifies the keyCode to include the pressed keys.
    /// </summary>
    private Keys AddModifiers(Keys key)
    {
        //CapsLock
        if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) key = key | Keys.CapsLock;

        //Shift
        if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) key = key | Keys.Shift;

        //Ctrl
        if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) key = key | Keys.Control;

        //Alt
        if ((GetKeyState(VK_MENU) & 0x8000) != 0) key = key | Keys.Alt;

        return key;
    }

Finally this method is included in the original hook processing code:

    public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) {
    if (code >= 0) {

        Keys key = (Keys)lParam.vkCode;

    if (HookedKeys.Contains(key)) {

                //Get modifiers
                key = AddModifiers(key);

            KeyEventArgs kea = new KeyEventArgs(key);
    ...

The returned KeyEventArgs passed in the KeyDown and KeyUp events will now in include the correct values of the modifier properties.

The full (and latest) implementation of globalKeyboardHook can be found in the source for the I was working on in GitHub here.

2 thoughts on “Determining the state of modifier keys when hooking keyboard input”

  1. Following Up On :

    http://www.codeproject.com/Articles/19004/A-Simple-C-Global-Low-Level-Keyboard-Hook

    Please explain how this would behave for a 32 bit Windows system and a 64 bit system.
    Will a build compiled in a 32 bit system work on a 64 bit machine and vice versa ?
    Any other points to consider.
    Usually Anti Viruses are not happy to see Set Windows Hook
    This code expecially using the paramater “0″ attaches itself to all the threads , which probably is not a good thing. Please provide your opinion.
    The only change that I have done is to add all the alphabets.
    I was trying to add other language characters as well.
    Please explain as to how that may be achieved.

    Thanks

    1. 1: I’ve not found any issues switching it between 64-bit and 32. The mini project I wrote it into was developed on a 64-bit Windows 7 install and there were no obvious problems.
      2: AV issues, yes – for obvious reasons. It should be possible to add exceptions for your AV where you know that a particular program is ok.
      3: Its specifically looking for the use of the required key anywhere across all threads/processes (ie Globally). It acts courteously by forwarding on the key once it has been processed.

Leave a Reply

Your email address will not be published.

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>