marko devcic

  • github:
  • email:
    madevcic {at}

.NET Keyboard Hook

Posted on 25 October 2014

There's a ton of code samples on the web with possible low level keyboard hook implementations, but I haven't found one that takes into account users keyboard layout.

All of these samples showed how you can get a keycode that user pressed, but for one project I did, I needed to scan keyboard input exactly as it appeared on the users screen. So if the users switches the keyboard layout to let's say German, then I want to get the actual German keys he pressed. If he switches to English back, again I'd like the actual English characters that are appearing on the screen.
Here's a full code for a class that does that.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

namespace KeyboardHook
    public delegate IntPtr KeyboardHookCallback(int code, IntPtr wParam, IntPtr lParam);

    public sealed class KeyBoardHook : IDisposable
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_KEYUP = 0x0101; 
        private const int WM_SYSKEYDOWN = 0x0104;
        private const int WM_SYSKEYUP = 0x0105;
        private const int VK_SHIFT = 0x10;
        private const int VK_CONTROL = 0x11;
        private const int VK_MENU = 0x12;
        private const int VK_CAPITAL = 0x14;

        public event EventHandler<KeyboardHookArgs> KeyDown;

        KeyboardHookCallback _hookCallBack;
        IntPtr _hookID = IntPtr.Zero;

        private bool _isDisposed;

        public KeyBoardHook()
            _hookCallBack = new KeyboardHookCallback(HookCallback);
            using (Process process = Process.GetCurrentProcess())
                using (ProcessModule module = process.MainModule)
                    _hookID = WinAPI.SetWindowsHookEx(WH_KEYBOARD_LL, _hookCallBack, WinAPI.GetModuleHandle(module.ModuleName), 0);                   

        private IntPtr HookCallback(int code, IntPtr wParam, IntPtr lParam)
            if ( code < 0)
                return WinAPI.CallNextHookEx(_hookID, code, wParam, lParam);

            WinAPI.KBDLLHOOKSTRUCT keybStruct = (WinAPI.KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(WinAPI.KBDLLHOOKSTRUCT));

            if ((int)wParam == WM_KEYDOWN || (int)wParam == WM_SYSKEYDOWN)
                bool isDownShift = ((WinAPI.GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
                bool isDownCapslock = (WinAPI.GetKeyState(VK_CAPITAL) != 0 ? true : false);
                StringBuilder builder = new StringBuilder(10);
                byte[] lpKeyState = new byte[256];
                if (WinAPI.GetKeyboardState(lpKeyState))
                    if (WinAPI.ToUnicodeEx((uint)keybStruct.vkCode
                                            , (uint)keybStruct.scanCode
                                            , lpKeyState
                                            , builder
                                            , builder.Capacity
                                            , 0
                                            , WinAPI.GetKeyboardLayout(0)) != -1)
                        KeyDown.InvokeSafely<KeyboardHookArgs>(this, new KeyboardHookArgs(keybStruct.vkCode, builder.ToString()));
            return WinAPI.CallNextHookEx(_hookID, code, wParam, lParam);


        private void Dispose(bool disposing)
            if (_isDisposed) return;
            _isDisposed = true;

        public void Dispose()


In the hook call back method, after we obtain the keycode that was pressed, we need to call a few more Win32 functions to get the keyboard layout and keyboard state.
Here are all of the Win32 functions that we need to import.

public class WinAPI
        public struct KBDLLHOOKSTRUCT
            public int vkCode;
            public int scanCode;
            public int flags;
            int time;
            IntPtr dwExtraInfo;
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr SetWindowsHookEx(int idHook, KeyboardHookCallback lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        internal static extern IntPtr GetModuleHandle(string lpModuleName);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetKeyboardState(byte[] lpKeyState);
        internal static extern IntPtr GetKeyboardLayout(uint idThread);
        internal static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[]
           lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff,
           int cchBuff, uint wFlags, IntPtr dwhkl);

And this is what the EventArgs class looks like

public class KeyboardHookArgs : EventArgs
        int _keyCode;
        string _keyName;        
        string _string;

        public int KeyCode { get { return _keyCode; } }
        public string KeyName { get { return _keyName; } }        
        public string String { get { return _string; } }

        public KeyboardHookArgs(int keyCode)
            _keyCode = keyCode;
            _keyName = System.Windows.Input.KeyInterop.KeyFromVirtualKey(keyCode).ToString();

        public KeyboardHookArgs(int keyCode, string str)
            : this(keyCode)
            _string = str;

Happy coding!