Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Check if specific key is pressed

Tags:

c#

key

keyboard

I'm trying to check if a key is pressed, but I get these Error Message:

Error 1 The name 'Keyboard' does not exist in the current context

Error 2 The name 'Key' does not exist in the current context

Can you tell me how to fix it?

public void Main() 
{
    while(true) 
    {
        if (Keyboard.IsKeyPressed(Key.A)) 
        {
            //...
        }
        return;
    }
}
like image 735
Niklas Avatar asked Dec 27 '13 16:12

Niklas


1 Answers

It looks like you are trying to create a global hotkey in the system and your application should respond when it is pressed.

You will need two Win32 API functions RegisterHotKey and UnregisterHotKey.

Looking at your using System.Windows.Input, it seems like you are trying to do this with WPF, which is possible.

Let's start with your fairly basic P/Invokes:

using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("user32.dll")]
    public static extern bool RegisterHotKey(IntPtr windowHandle, int hotkeyId, uint modifierKeys, uint virtualKey);

    [DllImport("user32.dll")]
    public static extern bool UnregisterHotKey(IntPtr windowHandle, int hotkeyId);
}

Now, when you register your Window, what happens is that a WM_HOTKEY message is sent to your application's message pump. However, WPF abstracts this message pump away from you, so you'll need to add a HwndSourceHook to tap into it.

How do we do all this? Let's start by initializing our HwndSourceHook delegate. Add this snippet to your MainWindow:

    using System.Windows.Interop;

    static readonly int MyHotKeyId = 0x3000;
    static readonly int WM_HOTKEY = 0x312;

    void InitializeHook()
    {
        var windowHelper = new WindowInteropHelper(this);
        var windowSource = HwndSource.FromHwnd(windowHelper.Handle);

        windowSource.AddHook(MessagePumpHook);
    }

    IntPtr MessagePumpHook(IntPtr handle, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == WM_HOTKEY)
        {
            if ((int)wParam == MyHotKeyId)
            {
                // The hotkey has been pressed, do something!

                handled = true;
            }
        }

        return IntPtr.Zero;
    }

Alright, so now we have everything in place to respond to the WM_HOTKEY message. However, we need to register our hotkey still! Let's add another couple initialization methods:

    void InitializeHotKey()
    {
        var windowHelper = new WindowInteropHelper(this);

        // You can specify modifiers such as SHIFT, ALT, CONTROL, and WIN.
        // Remember to use the bit-wise OR operator (|) to join multiple modifiers together.
        uint modifiers = (uint)ModifierKeys.None;

        // We need to convert the WPF Key enumeration into a virtual key for the Win32 API!
        uint virtualKey = (uint)KeyInterop.VirtualKeyFromKey(Key.A);

        NativeMethods.RegisterHotKey(windowHelper.Handle, MyHotKeyId, modifiers, virtualKey);
    }

    void UninitializeHotKey()
    {
        var windowHelper = new WindowInteropHelper(this);
        NativeMethods.UnregisterHotKey(windowHelper.Handle, MyHotKeyId);
    }

Alright! Where do we put these? Do not put them in the constructor! Why? Because the Handle will be 0 and invalid! Put them here (in MainWindow):

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        InitializeHook();
        InitializeHotKey();
    }

You can register multiple hotkeys, un-register and re-register new ones.. it's up to you. Just remember that each hotkey must have a unique ID registered to it. It only makes sense, as your message pump hook has to know which hotkey caused the WM_HOTKEY message!

It's also good practice to unregister all hotkeys when your application closes.

like image 143
Erik Avatar answered Oct 05 '22 20:10

Erik