Search code examples
c#keyboardconsole-applicationuser32

Get key from any process


Ive seen many solutions online but none does exactly what I want. What is the best/simplest way to get any keys pressed in a given process (not my console applicaton) while my application is running in background. I dont need the modifiers or anything.


Solution

  • If you don't particularly care which process the keys are being pressed in the easiest method would be to call GetAsyncKeyState. It's rather limited though as it does not hook the keyboard and requires you to call it continuously. The best approach in my opinion is to hook the keyboard.

    Using SetWindowsHookEx you can actually explicitly specify the identifier of the thread with which the hook procedure is to be associated so you can hook keys for a specific process (see dwThreadId).

    Here's a class that you can use (originally found on a Micrsoft blog but I cannot seem to find the authors name at the moment!)

    public delegate IntPtr KeyboardProcess(int nCode, IntPtr wParam, IntPtr lParam);
    
    public sealed class KeyboardHook
    {
        public static event EventHandler<KeyPressedEventArgs> KeyPressed;
        private const int WH_KEYBOARD = 13;
        private const int WM_KEYDOWN = 0x0100;
        private static KeyboardProcess keyboardProc = HookCallback;
        private static IntPtr hookID = IntPtr.Zero;
    
        public static void CreateHook()
        {
            hookID = SetHook(keyboardProc);
        }
    
        public static void DisposeHook()
        {
            UnhookWindowsHookEx(hookID);
        }
    
        private static IntPtr SetHook(KeyboardProcess keyboardProc)
        {
            using (Process currentProcess = Process.GetCurrentProcess())
            using (ProcessModule currentProcessModule = currentProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD, keyboardProc, GetModuleHandle(currentProcessModule.ModuleName), 0);
            }
        }
    
        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
    
                if (KeyPressed != null)
                    KeyPressed(null, new KeyPressedEventArgs((Keys)vkCode));
            }
            return CallNextHookEx(hookID, nCode, wParam, lParam);
        }
    
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProcess lpfn, IntPtr hMod, uint dwThreadId);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);
    
    }
    
    public class KeyPressedEventArgs : EventArgs
    {
        public Keys KeyCode { get; set; }
        public KeyPressedEventArgs(Keys Key)
        {
            KeyCode = Key;
        }
    }
    

    Implementation via Console Application:

    class Program
    {
        static void Main(string[] args)
        {
            KeyboardHook.CreateHook();
            KeyboardHook.KeyPressed += KeyboardHook_KeyPressed;
            Application.Run();
            KeyboardHook.DisposeHook();
        }
    
        static void KeyboardHook_KeyPressed(object sender, KeyPressedEventArgs e)
        {
            Console.WriteLine(e.KeyCode.ToString());
        }
    }