Search code examples
c#winformskeyboard-hookregisterhotkey

WinForms Global Hotkey Typing Interference


I am trying to get this keyboard hook to echo registered keys to a Label when pressed. My goal is to do this while the Window is out of focus.

This program is only looking for the keys*: w, a, s, d


The issue I'm running into is that I can't type the letters w, a, s, d in any other programs (example: notepad) while this program is running.


Here is my Form class:

Form1.cs:

public partial class Form1 : Form
{
    private KeyHandler _keyHandler;
    private List<Keys> keys = new List<Keys>() { Keys.W, Keys.A, Keys.S, Keys.D };

    public Form1()
    {
        InitializeComponent();

        foreach (var key in keys) (_keyHandler = new KeyHandler(key, this)).Register();
    }

    private void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        label1.Text = e.KeyCode.ToString();
    }
    
    // Key Logger =======================
    private void HandleHotkey(int keyPress)
    {
        if (keyPress == 5701632)
            label1.Text = "W pressed";
        else if (keyPress == 4259840)
            label1.Text = "A pressed";
        else if (keyPress == 5439488)
            label1.Text = "S pressed";
        else if (keyPress == 4456448)
            label1.Text = "D pressed";
    }
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == Constants.WM_HOTKEY_MSG_ID)
            HandleHotkey(m.LParam.ToInt32());
        base.WndProc(ref m);
    }
}

and the class it's using to register keys:

KeyHandler.cs:

public static class Constants
{
    //windows message id for hotkey
    public const int WM_HOTKEY_MSG_ID = 0x0312;
}

public class KeyHandler
{
    [DllImport("user32.dll")]
    private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

    [DllImport("user32.dll")]
    private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    private int key;
    private IntPtr hWnd;
    private int id;

    public KeyHandler(Keys key, Form form)
    {
        this.key = (int)key;
        this.hWnd = form.Handle;
        id = this.GetHashCode();
    }

    public override int GetHashCode()
    {
        return key ^ hWnd.ToInt32();
    }

    public bool Register()
    {
        return RegisterHotKey(hWnd, id, 0, key);
    }

    public bool Unregiser()
    {
        return UnregisterHotKey(hWnd, id);
    }
}

I'll take all suggestions/answers. Thank you!


Update 1

I was able to use what Jimi suggested, GetAsyncKeyState(). While this does not interfere with typing in other windows, it does not update my application window until it is back in focus.

Form1.cs

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void HandleHotkey(Keys key)
        {
            label1.Text = key.ToString() + " done";
        }

        protected override void WndProc(ref Message m)
        {
            KeyState keyState = new KeyState();
            if (keyState.GetKeyState(Keys.W) == 1) HandleHotkey(Keys.W);

            base.WndProc(ref m);
        }
    }

KeyState.cs

    public class KeyState
    {
        [DllImport("user32.dll")]
        public static extern short GetAsyncKeyState(Keys key);

        public int GetKeyState(Keys key)
        {
            return GetAsyncKeyState(key);
        }
    }

Solution

  • Solution

    With the help of Jimi and this stack overflow form, I was able to solve the problem I had.

    By using the method GetAsyncKeyState() and a timer, I was able to constantly check if the keys w, a, s, d were being pressed down.


    Form1.cs

        public partial class Form1 : Form
        {
            [DllImport("user32.dll")]
            public static extern short GetAsyncKeyState(Keys key);
    
            public Form1()
            {
                InitializeComponent();
                StartTimer(timer1_Tick);
            }
    
            private void timer1_Tick(object sender, EventArgs e)
            {
                if (GetAsyncKeyState(Keys.W) <= -32767) label1.Text = Keys.W + " special";
                else if (GetAsyncKeyState(Keys.A) <= -32767) label1.Text = Keys.A + " special";
                else if (GetAsyncKeyState(Keys.S) <= -32767) label1.Text = Keys.S + " special";
                else if (GetAsyncKeyState(Keys.D) <= -32767) label1.Text = Keys.D + " special";
    
                else label1.Text = "Nothing Pressed";
            }
    
            private void StartTimer(EventHandler eventHandler)
            {
                timer1.Tick += new EventHandler(eventHandler);
                timer1.Interval = 35;
                timer1.Start();
            }
        }
    

    Add this to Form1.Designer.cs

    public System.Windows.Forms.Timer timer1;