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
设置为默认布局)有一个恼人的倾向,会更新整个系统的语言输入设置,这绝对不是我想要的...
_________________________________
| 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 源代码,但这看起来远远超出了我的理解阈值。尽管如此,我(认为我)发现了 KeyboardEventInit
指针的唯一其他引用是一个 forward 类声明,并且没有任何迹象实施
KeyboardEventInit::Create()
...
的 评论,使用 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)
MapVirtualKeyEx
GetKeyboardLayout
不需要全局更改键盘布局,并确保物理键与其虚拟按键的一致映射,紧密模仿
KeyboardEvent.code
的行为。如果应用程序运行时键盘布局可以更改(例如,通过用户操作或以编程方式),请考虑处理 WM_INPUTLANGCHANGE
消息
。这将通知您的应用程序输入语言或键盘布局的任何更改,以便您根据需要进行调整。