简体   繁体   English

GetKeyState() 仅在德语键盘布局上误报

[英]GetKeyState() false positive only on German keyboard layouts

When using GetKeyState to check whether a key is pressed, I'm getting a strange false positive only on German keyboard layouts.当使用GetKeyState检查是否按下某个键时,我只会在德语键盘布局上得到一个奇怪的误报。 The check is within a low-level keyboard hook.检查在低级键盘挂钩内。

The specific key that seems to be causing issues is RMenu (the right Alt key).似乎导致问题的特定键是RMenu (右 Alt 键)。

In the hook proc below, pressing RMenu+P on a US keyboard will populate the pressedKeys dictionary with Menu , RMenu , P .在下面的钩子过程中,在美式键盘上按RMenu+P将使用MenuRMenuP填充pressedKeys字典。 Whereas pressing the same keybinding on a German layout will populate the dict with Menu , RMenu , ControlKey , LControlKey , P (where ControlKey , LControlKey are falsely reported as down).而在德语布局上按下相同的键绑定将使用MenuRMenuControlKeyLControlKeyP填充字典(其中ControlKeyLControlKey被错误地报告为关闭)。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

internal static class Program
{
  private const uint WM_KEYDOWN = 0x100;
  private const uint WM_SYSKEYDOWN = 0x104;

  [STAThread]
  private static void Main()
  {
    _ = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, KeybindingHookProc, Process.GetCurrentProcess().MainModule.BaseAddress, 0);

    // `SetWindowsHookEx` requires a message loop within the thread that is executing the code.
    Application.Run();
  }

  private static IntPtr KeybindingHookProc(int nCode, IntPtr wParam, IntPtr lParam)
  {
    var shouldPassThrough = nCode != 0 || !((uint)wParam == WM_KEYDOWN || (uint)wParam == WM_SYSKEYDOWN);

    // If nCode is less than zero, the hook procedure must pass on the hook notification.
    if (shouldPassThrough)
      return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);

    var inputEvent =
      (LowLevelKeyboardInputEvent)Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardInputEvent));

    var pressedKeys = new Dictionary<Keys, bool>
    {
      [inputEvent.Key] = true
    };

    foreach (var key in Enum.GetValues<Keys>())
    {
      if (IsKeyDown(key))
        pressedKeys[key] = true;
    }

    // Check for arbitrary keybinding (Right alt + P):
    // !!!!!
    if (!pressedKeys.ContainsKey(Keys.P) && !pressedKeys.ContainsKey(Keys.LMenu))
      return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);

    Debug.WriteLine("Keybinding triggered!");
    return new IntPtr(1);
  }

  private static bool IsKeyDown(Keys key)
  {
    return (GetKeyState(key) & 0x8000) == 0x8000;
  }

  public enum HookType
  {
    WH_KEYBOARD_LL = 13,
  }

  public delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);

  [DllImport("user32.dll")]
  public static extern IntPtr SetWindowsHookEx(HookType hookType, [MarshalAs(UnmanagedType.FunctionPtr)] HookProc lpfn, IntPtr hMod, int dwThreadId);

  [DllImport("user32.dll")]
  public static extern IntPtr CallNextHookEx([Optional] IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

  [DllImport("user32.dll")]
  public static extern short GetKeyState(Keys nVirtKey);

  [StructLayout(LayoutKind.Sequential)]
  public struct LowLevelKeyboardInputEvent
  {
    public int VirtualCode;
    public Keys Key => (Keys)VirtualCode;
    public int HardwareScanCode;
    public int Flags;
    public int TimeStamp;
    public IntPtr AdditionalInformation;
  }
}

Is there any way to avoid this strange behavior?有什么办法可以避免这种奇怪的行为? Is this a bug?这是一个错误吗?

As @HansPassant mentioned, the AltGr key is present on certain keyboard layouts, like German and US International.正如@HansPassant 所提到的,AltGr 键出现在某些键盘布局上,例如德语和美国国际键盘。 It generates both Control and Alt when pressed.按下时它会同时生成 Control 和 Alt。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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