获取方向键的正确键盘扫描码

Get correct keyboard scan code for arrow keys

我正在尝试使用 MapVirtualKey Windows 函数从 Windows Forms 应用程序中的虚拟键码获取键盘扫描码。 P/Invoke 东西的声明是:

private const uint MAPVK_VK_TO_VSC = 0;

[DllImport("user32.dll", 
    CallingConvention = CallingConvention.StdCall, 
    CharSet = CharSet.Unicode, 
    EntryPoint = "MapVirtualKey", 
    SetLastError = true, 
    ThrowOnUnmappableChar = false)]
private static extern uint MapVirtualKey(
    uint uCode, 
    uint uMapType);

我已经覆盖了我的应用程序 main window class 的 OnPreviewKeyDown 方法(源自 System.Windows.Forms.Form);在这里,我获取给定 PreviewKeyDownEventArgs 对象的 KeyCode 属性 的值并将其传递给 MapVirtualKey 方法(我假设该值实际上是一个虚拟键码).

protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
    uint uCode = (uint)e.KeyCode;
    uint scanCode = MapVirtualKey(uCode, MAPVK_VK_TO_VSC);

    this.HandleScanCode(scanCode);

    base.OnPreviewKeyDown(e);
}

问题是我收到了错误的箭头键扫描码(我收到的是数字键盘箭头键的扫描码)。例如,如果按下向上箭头键,我希望扫描码是 200 而不是 72。

看来,这个问题只属于增强型键盘。 MapVirtualKey 方法与左右键代码无关。这意味着,键码 38 Up 和 104 NumPad8 产生相同的扫描码,因为 Up 键是增强键。

我决定直接拦截 WM_KEYDOWN 消息,因为该消息的 lParam 参数包含所需的扫描代码和指示密钥是否为增强密钥的位。这解决了我的问题...

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);

    switch (m.Msg)
    {
        case WM_KEYDOWN:
        {
            int lParam = m.LParam.ToInt32();
            int scanCode = (lParam >> 16) & 0x000000ff; // extract bit 16-23
            int ext = (lParam >> 24) & 0x00000001; // extract bit 24
            if (ext == 1) 
                scanCode += 128;

            this.HandleScanCode(scanCode);
            break;
        }
    }
}

如果一个键是增强键,需要的扫描码可以通过加128来计算。