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

ijxebb2r  于 2023-02-26  发布在  .NET
关注(0)|答案(2)|浏览(121)

如果我需要生成一个键盘布局,以供用户自定义,看起来像他/她的键盘,我该怎么做?
比如这样的事情:

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

ecr0jaav

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系列,如果你想进一步探讨这个问题的话。祝你好运!

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,它和你的类似。

koaltpgm

koaltpgm2#

没有包含键盘布局的内置.NET类。键盘布局是操作系统(通常是Windows)的一个功能。在涉及.NET时,按下的键已从硬件事件转换为软件事件。如果您想看到实际操作,找到两个键盘布局,其中一个键在它们之间移动。设置一个虚拟应用程序,该应用程序带有Key_Down事件的事件处理程序,然后注意事件参数是相同的;如果您按了-键,则无论-键位于何处,您都按了-键。

相关问题