是否可以创建与所用键盘相同的键盘布局?

如果我需要为看起来像他/她的键盘的用户生成用于自定义的键盘布局,我该怎么办?

例如这样的事情:

在此处输入图像描述

法语,瑞典语,英语,加拿大等将有不同的布局,对吧。 这是很多工作还是只是使用某种内置.NET区域类的问题?

按键生成硬件事件,向Windows操作系统报告“扫描代码”。 然后,基于扫描代码以及其他键盘状态因素( 大写锁定状态, Shift / Alt / Ctrl 状态以及任何未决的死键击键)将该扫描代码转换为“虚拟键代码”。 转换后的VK值是KeyDown事件等报告的值。

从扫描代码到VK代码的转换取决于当前的输入区域设置 – 简单地说,输入区域设置定义了扫描代码和虚拟键代码之间的映射。 有关键盘输入的完整说明,请参阅MSDN文档 。

通过反转该查找过程,可以确定与每个虚拟键码对应的扫描码(当然,由于shift / ctrl / alt状态等,相同的扫描码将映射到多个VK码)。 Win32 API通过使用MAPVK_VK_TO_VSC_EX选项提供MapVirtualKeyEx函数来执行此映射。 您可以使用它来确定哪个扫描代码生成特定的VK代码。

不幸的是,这是以编程方式进行的 – 无法确定键盘的物理布局或每个扫描代码的键位置。 但是,大多数物理键盘的布线方式相同,因此(例如)左上角的键在大多数物理键盘设计上都具有相同的扫描码。 您可以使用此假设约定来推断与扫描代码对应的物理位置,具体取决于基本的物理键盘布局(101键,102键等)。 这不是保证,但这是一个非常安全的猜测。

下面的代码是我写的一个更大的键盘处理库的摘录(我一直打算开源但没有时间)。 该方法初始化一个数组( this._virtualKeyScanCodes ),该数组由VK代码索引给定的输入语言环境(存储在this._inputLanguage ,声明为System.Windows.Forms.InputLanguage 。您可以使用该数组来确定扫描代码通过检查例如this._virtualKeyScanCodes[VK_NUMPAD0]对应于VK代码 – 如果扫描代码为零,则当前输入语言环境中键盘上的VK不可用;如果它不为零,则为扫描码从中可以推断出物理键。

不幸的是,当你进入死键的领域(例如,产生重音字符的多个键组合)时,事情会比这更复杂。 这一切都太复杂了,不能立即进入,但如果你想进一步探索,Michael S. Kaplan写了一系列详细的博客文章 。 祝好运!

 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); } 

编辑:另请参阅与您类似的问题 。

没有包含键盘布局的内置.NET类。 键盘布局是操作系统的function,通常是Windows。 当涉及到.NET时,按下的键已从硬件事件转换为软件事件。 如果要查看此操作,请找到2个键盘布局,其中键在它们之间移动。 在Key_Down事件上设置一个带有事件处理程序的虚拟应用程序,然后注意事件args是相同的; 如果你按下-键,你按下-键,无论在哪里-键位于。