简体   繁体   中英

C# - Check if specific key is pressed

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;
    }
}

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.

You can use a keyboard hook. Check this out, from this answer to a similar question :

Global keyboard hooks are not the right solution if you only want a few global hotkeys. A high level keyboard hook means that your dll will be injected into other applications, and shouldn't be done at all in managed code. A low level keyboard hook is a bit better, since it processes the keyboard events in your own application. Still it requires that every keyboard event is handled by your thread.

The windows API function RegisterHotKey is much better suited for that.

But using a smple F-Key as global hotkey is problematic since it might collide with a local hotkey of the application that has focus. So you should make global hotkeys configurable, so the user can avoid collisions with his commonly used applications.

If you are trying to do this in a windows forms application, maybe you can add these codes into the Form's KeyDown event(or which key event you need to use):

switch (e.KeyData)
{
    //Detects that you pressed the Up Arrow Key, which key do you need, just 
    //write it here.
    case Keys.Up: 
    //enter code here`
    break;
}

You need to add PresentationCore.dll and WindowsBase to references and add to the header of the method [STAThread]

The type of application is not clear. If you have a console application, not a Windows form one, you can try this:

while (true)
  if (Console.KeyAvailable) 
    if (Console.ReadKey(true).Key == ConsoleKey.A)
    { 
      // Do something
    }

and read this if you want to have a global hotkey in a windows forms app: http://www.liensberger.it/web/blog/?p=207

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM