如果我需要生成一个键盘布局,以供用户自定义,看起来像他/她的键盘,我该怎么做?比如这样的事情:
法语,瑞典语,英语,加拿大语等将有不同的布局,对吧。这是大量的工作还是只是使用某种内置的。NET区域类的问题?
ecr0jaav1#
按下键会生成一个硬件事件,向Windows操作系统报告“扫描代码”。然后,根据扫描代码沿着其他键盘状态因素(Caps Lock状态、Shift/Alt/Ctrl键状态以及任何挂起的死键击),将此扫描代码转换为“虚拟键代码”。转换后的VK值是KeyDown事件等报告的值。从扫描码到VK码的转换取决于当前的输入区域设置-简单地说,输入区域设置定义了扫描码和虚拟按键代码之间的Map。有关键盘输入的完整说明,请参阅the MSDN documentation。通过反转此查找过程,可以确定与每个虚拟键代码对应的扫描代码(当然,由于shift/ctrl/alt状态等原因,同一个扫描代码将Map到多个VK代码)。Win32 API提供MapVirtualKeyEx函数,通过使用MAPVK_VK_TO_VSC_EX选项来执行此Map。您可以使用此函数确定哪个扫描代码生成特定的VK代码。不幸的是,这是你所能通过编程做到的最远--没有办法为每个扫描码确定键盘的物理布局或按键的位置,然而,大多数物理键盘的接线方式都是一样的,所以(例如)在大多数物理键盘设计上,左上角的键将具有相同的扫描码。你可以根据基本的物理键盘布局(101键,102键等),使用这个假设的约定来推断对应于扫描码的物理位置。这不能保证,但这是一个相当安全的猜测。下面的代码摘自我编写的一个更大的键盘处理库(我一直想开源它,但是没有时间)。该方法初始化一个数组(this._virtualKeyScanCodes),由给定输入法区域设置的VK代码索引(存储在this._inputLanguage中,声明为System.Windows.Forms.InputLanguage)。您可以使用数组来确定对应于VK代码的扫描代码,例如检查this._virtualKeyScanCodes[VK_NUMPAD0]-如果扫描代码为零,则在当前输入法区域设置的键盘上该VK不可用;如果它不为零,则它是可以从中推断物理密钥的扫描码。不幸的是,当你进入死键的领域(例如,产生重音字符的多个键组合)时,事情会稍微复杂一些。现在讨论这些太复杂了,但是Michael S. Kaplan早在2006年就写了一个详细的blog posts系列,如果你想进一步探讨这个问题的话。祝你好运!
KeyDown
MapVirtualKeyEx
MAPVK_VK_TO_VSC_EX
this._virtualKeyScanCodes
this._inputLanguage
System.Windows.Forms.InputLanguage
this._virtualKeyScanCodes[VK_NUMPAD0]
private void Initialize() { this._virtualKeyScanCodes = new uint[MaxVirtualKeys]; // Scroll through the Scan Code (SC) values and get the Virtual Key (VK) // values in it. Then, store the SC in each valid VK so it can act as both a // flag that the VK is valid, and it can store the SC value. for (uint scanCode = 0x01; scanCode <= 0xff; scanCode++) { uint virtualKeyCode = NativeMethods.MapVirtualKeyEx( scanCode, NativeMethods.MAPVK_VSC_TO_VK, this._inputLanguage.Handle); if (virtualKeyCode != 0) { this._virtualKeyScanCodes[virtualKeyCode] = scanCode; } } // Add the special keys that do not get added from the code above for (KeysEx ke = KeysEx.VK_NUMPAD0; ke <= KeysEx.VK_NUMPAD9; ke++) { this._virtualKeyScanCodes[(uint)ke] = NativeMethods.MapVirtualKeyEx( (uint)ke, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); } this._virtualKeyScanCodes[(uint)KeysEx.VK_DECIMAL] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_DECIMAL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_DIVIDE] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_DIVIDE, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_CANCEL] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_CANCEL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_LSHIFT] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_LSHIFT, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_RSHIFT] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_RSHIFT, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_LCONTROL] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_LCONTROL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_RCONTROL] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_RCONTROL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_LMENU] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_LMENU, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_RMENU] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_RMENU, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_LWIN] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_LWIN, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_RWIN] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_RWIN, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_PAUSE] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_PAUSE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_UP] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_VOLUME_UP, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_DOWN] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_VOLUME_DOWN, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_MUTE] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_VOLUME_MUTE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_NEXT_TRACK] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_MEDIA_NEXT_TRACK, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_PREV_TRACK] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_MEDIA_PREV_TRACK, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_PLAY_PAUSE] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_MEDIA_PLAY_PAUSE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle); this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_STOP] = NativeMethods.MapVirtualKeyEx( (uint)KeysEx.VK_MEDIA_STOP, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle); this._stateController = new KeyboardStateController(); this._baseVirtualKeyTable = new VirtualKeyTable(this); }
编辑:也可以看this question,它和你的类似。
koaltpgm2#
没有包含键盘布局的内置.NET类。键盘布局是操作系统(通常是Windows)的一个功能。在涉及.NET时,按下的键已从硬件事件转换为软件事件。如果您想看到实际操作,找到两个键盘布局,其中一个键在它们之间移动。设置一个虚拟应用程序,该应用程序带有Key_Down事件的事件处理程序,然后注意事件参数是相同的;如果您按了-键,则无论-键位于何处,您都按了-键。
Key_Down
2条答案
按热度按时间ecr0jaav1#
按下键会生成一个硬件事件,向Windows操作系统报告“扫描代码”。然后,根据扫描代码沿着其他键盘状态因素(Caps Lock状态、Shift/Alt/Ctrl键状态以及任何挂起的死键击),将此扫描代码转换为“虚拟键代码”。转换后的VK值是
KeyDown
事件等报告的值。从扫描码到VK码的转换取决于当前的输入区域设置-简单地说,输入区域设置定义了扫描码和虚拟按键代码之间的Map。有关键盘输入的完整说明,请参阅the MSDN documentation。
通过反转此查找过程,可以确定与每个虚拟键代码对应的扫描代码(当然,由于shift/ctrl/alt状态等原因,同一个扫描代码将Map到多个VK代码)。Win32 API提供
MapVirtualKeyEx
函数,通过使用MAPVK_VK_TO_VSC_EX
选项来执行此Map。您可以使用此函数确定哪个扫描代码生成特定的VK代码。不幸的是,这是你所能通过编程做到的最远--没有办法为每个扫描码确定键盘的物理布局或按键的位置,然而,大多数物理键盘的接线方式都是一样的,所以(例如)在大多数物理键盘设计上,左上角的键将具有相同的扫描码。你可以根据基本的物理键盘布局(101键,102键等),使用这个假设的约定来推断对应于扫描码的物理位置。这不能保证,但这是一个相当安全的猜测。
下面的代码摘自我编写的一个更大的键盘处理库(我一直想开源它,但是没有时间)。该方法初始化一个数组(
this._virtualKeyScanCodes
),由给定输入法区域设置的VK代码索引(存储在this._inputLanguage
中,声明为System.Windows.Forms.InputLanguage
)。您可以使用数组来确定对应于VK代码的扫描代码,例如检查this._virtualKeyScanCodes[VK_NUMPAD0]
-如果扫描代码为零,则在当前输入法区域设置的键盘上该VK不可用;如果它不为零,则它是可以从中推断物理密钥的扫描码。不幸的是,当你进入死键的领域(例如,产生重音字符的多个键组合)时,事情会稍微复杂一些。现在讨论这些太复杂了,但是Michael S. Kaplan早在2006年就写了一个详细的blog posts系列,如果你想进一步探讨这个问题的话。祝你好运!
编辑:也可以看this question,它和你的类似。
koaltpgm2#
没有包含键盘布局的内置.NET类。键盘布局是操作系统(通常是Windows)的一个功能。在涉及.NET时,按下的键已从硬件事件转换为软件事件。如果您想看到实际操作,找到两个键盘布局,其中一个键在它们之间移动。设置一个虚拟应用程序,该应用程序带有
Key_Down
事件的事件处理程序,然后注意事件参数是相同的;如果您按了-键,则无论-键位于何处,您都按了-键。