使用 Win32 的 WM_KEYDOWN 修复了与键盘布局无关的虚拟键

问题描述 投票:0回答:1

2021-07-29:

我正在寻找一种方法,用 Win32 的 KeyboardEvent.code

 重现 JavaScript 的 
WM_KEYDOWN
 的行为:

这会忽略用户的键盘布局,因此,如果用户按下 QWERTY 键盘布局中“Y”位置的键(靠近主行上方行的中间),这将始终返回“KeyY”,即使用户拥有 QWERTZ 键盘(这意味着用户期望输入“Z”,所有其他属性都指示“Z”)或 Dvorak 键盘布局(用户期望输入“F”)。

到目前为止,通过使用

MapVirtualKeyEx()
和默认键盘布局映射扫描码,我已经非常接近我的目标了:

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
static unsafe nint WindowProc(HWND hWnd, WindowMessage msg, WPARAM wparam, LPARAM lparam)
{
   if (msg is WindowMessage.WM_KEYDOWN)
   {
      var scanCode = (uint) (((nint) lparam) >> 16) & 0xFF;
      var keyboardLayout = User32.LoadKeyboardLayout("00000409", 0); // "00000409" is supposed to be the default layout.
      var inputDebug = ((VK) (nuint) wparam).ToString();
      var mappingDebug = ((Key) User32.MapVirtualKeyEx(scanCode, MapTypes.MAPVK_VSC_TO_VK_EX, keyboardLayout)).ToString();
      //                    `-> where "Key" is my own "VK" enum, for the sake of naming convensions.

      Console.WriteLine("Input:  " + inputDebug);
      Console.WriteLine("Mapped: " + mappingDebug);
   }
   else if (msg is WindowMessage.WM_CHAR)
   {
      Console.WriteLine("Char:   " + (char) (nuint) wparam);
   }
}

这给出了以下类型的日志:

Input:  VK_UP
Mapped: ArrowUp

Input:  VK_CONTROL
Mapped: ControlLeft

Input:  VK_OEM_7
Mapped: Backquote
Char:   ²

Input:  A
Mapped: KeyQ
Char:   a

但是,

LoadKeyboardLayout()
(我用来将
HKL
设置为默认布局)有一个恼人的倾向,会更新整个系统的语言输入设置,这绝对不是我想要的...

I need 10 reputation to post images. You'll have ASCII art instead: _________________________________ | ENG English (United States) | | FR French keyboard | | | | ENG English (United States) | | US US keyboard | | | | AZ Languate preferences | _________________________________________________ ^ [/]: ((. <)) |░ENG/FR░| 2021-07-29 [, ]

而且,我被堵在那里了...

有没有办法获得代表不可知键盘布局的

HKL

,而无需实际加载它并更改系统设置?

或者我在这里寻找的方向正确吗?

2021-08-10:

到目前为止,我一直尝试使用
    System.Globalization.CultureInfo.InvariantCulture.KeyboardLayoutId
  • 代替
    LoadKeyboardLayout("00000409", 0)
    ,但没有成功。
    我还尝试从低位和高位字构建指针,但没有成功(
  • MapVirtualKeyEx()
  • 返回
    0
    我尝试进入 Chromium 源代码,但这看起来远远超出了我的理解阈值。尽管如此,我(认为我)发现了 
  • 负责在 C++ 端创建 DOM 键盘事件对象的代码
  • ,但是对此 KeyboardEventInit 指针的唯一其他引用是一个
    forward 类声明
    ,并且没有任何迹象实施KeyboardEventInit::Create()...
    
        
c# .net-5 win32gui
1个回答
0
投票
Hans Passant

评论,使用 KeyboardEvent.code

 函数
,应该可以在 Win32 环境中复制 JavaScript 的 GetKeyboardLayout
 行为而不改变系统的键盘设置。
User's Physical Key Press -> WM_KEYDOWN Message -> Extract Scan Code ↓ ↓ Get HKL of Current Thread Map Scan Code to Virtual Key Using HKL ↓ ↓ Return Consistent Virtual Key Code Based on Physical Key Position

该函数将帮助您获取当前线程的输入区域设置标识符(
HKL

),它代表活动的键盘布局,而无需加载新的布局。

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
static unsafe nint WindowProc(HWND hWnd, WindowMessage msg, WPARAM wparam, LPARAM lparam)
{
    if (msg == WindowMessage.WM_KEYDOWN)
    {
        // Extract the scan code from LPARAM
        var scanCode = (uint)(((nint)lparam >> 16) & 0xFF);
        // Get the HKL for the current thread (0 means current thread)
        var keyboardLayout = User32.GetKeyboardLayout(0);
        // Map the scan code to a virtual key using the current HKL
        var virtualKey = User32.MapVirtualKeyEx(scanCode, MapTypes.MAPVK_VSC_TO_VK_EX, keyboardLayout);
        var inputDebug = ((VK)(nuint)wparam).ToString();
        var mappingDebug = ((Key)virtualKey).ToString();  // 'Key' being a custom enum for clarity

        Console.WriteLine("Input:  " + inputDebug);
        Console.WriteLine("Mapped: " + mappingDebug);
    }
    else if (msg == WindowMessage.WM_CHAR)
    {
        Console.WriteLine("Char:   " + (char)(nuint)wparam);
    }
    return User32.DefWindowProc(hWnd, msg, wparam, lparam);
}

扫描码是从
lparam

中提取出来的,其中包含了

WM_KEYDOWN
消息处理时的关键信息。
使用 
GetKeyboardLayout(0)
,您无需修改系统设置即可获取当前线程的 HKL,从而确保您的应用程序不会影响其范围之外的用户环境。
MapVirtualKeyEx
使用获得的HKL将扫描码转换为适当的虚拟按键。该映射本质上与布局无关,这意味着它不依赖于根据用户活动键盘布局的按键的物理位置。
使用 

GetKeyboardLayout

不需要全局更改键盘布局,并确保物理键与其虚拟按键的一致映射,紧密模仿

KeyboardEvent.code
的行为。
如果应用程序运行时键盘布局可以更改(例如,通过用户操作或以编程方式),请考虑处理 

WM_INPUTLANGCHANGE 消息

。这将通知您的应用程序输入语言或键盘布局的任何更改,以便您根据需要进行调整。

© www.soinside.com 2019 - 2024. All rights reserved.