如何根据当前键盘布局将虚拟键代码转换为字符

本文关键字:虚拟 代码 转换 字符 布局 何根 键盘 | 更新日期: 2023-09-27 18:04:18

我已经浏览了之前关于这个问题的几个问题,到目前为止我找到的最好的答案是这样的:

(char) WinAPI.MapVirtualKey((uint) Keys.A, 2)

但是,这在以下两种情况下不起作用:

  • 它总是返回大写字母。对于Keys.A,我期望得到字符a,而对于Keys.A | Keys.ShiftKey,我期望得到字符A;然而,我似乎都得到了A

  • 它似乎没有考虑到键盘布局。例如,对于Keys.OemMinus,我似乎总是得到字符-,即使当前的键盘布局是德语,我希望这个键返回ß

正确的解决方案是什么?

如何根据当前键盘布局将虚拟键代码转换为字符

正确的解决方法是ToUnicode WinAPI函数:

[DllImport("user32.dll")]
public static extern int ToUnicode(uint virtualKeyCode, uint scanCode,
    byte[] keyboardState,
    [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
    StringBuilder receivingBuffer,
    int bufferSize, uint flags);

将其包装成一个合理、方便的方法的一种方法是:

static string GetCharsFromKeys(Keys keys, bool shift, bool altGr)
{
    var buf = new StringBuilder(256);
    var keyboardState = new byte[256];
    if (shift)
        keyboardState[(int) Keys.ShiftKey] = 0xff;
    if (altGr)
    {
        keyboardState[(int) Keys.ControlKey] = 0xff;
        keyboardState[(int) Keys.Menu] = 0xff;
    }
    WinAPI.ToUnicode((uint) keys, 0, keyboardState, buf, 256, 0);
    return buf.ToString();
}

现在我们可以检索字符并实际得到预期的结果:

Console.WriteLine(GetCharsFromKeys(Keys.E, false, false));    // prints e
Console.WriteLine(GetCharsFromKeys(Keys.E, true, false));     // prints E
// Assuming British keyboard layout:
Console.WriteLine(GetCharsFromKeys(Keys.E, false, true));     // prints é
Console.WriteLine(GetCharsFromKeys(Keys.E, true, true));      // prints É

也可以使用ToUnicodeEx来检索键盘布局的字符,而不是当前活动的键盘布局。签名是相同的,除了一个额外的参数,即可以使用LoadKeyboardLayout函数检索的输入地区ID。

我认为这可以通过以下方法实现:

[DllImportAttribute("User32.dll")]
public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState,
        byte[] lpChar, int uFlags);

示例用法可以在这里找到:http://www.pcreview.co.uk/forums/toascii-function-t1706394.html

    [DllImport("user32.dll")]
    static extern int MapVirtualKey(int uCode, uint uMapType);
    private void textBox1_OnKeyDown(object sender, KeyEventArgs e)
    {
        char c = (char)MapVirtualKey((int)e.KeyData, (uint)2);
        if (char.IsNumber(c)) DoSomething();
        else if (!char.IsNumber(c)) DoNothing();
    }