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

Tim*_*mwi 20 c# winapi

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

(char) WinAPI.MapVirtualKey((uint) Keys.A, 2)
Run Code Online (Sandbox Code Playgroud)

但是,这在两个方面不起作用:

  • 它总是返回大写字母.因为Keys.A我希望得到这个角色a,而Keys.A | Keys.ShiftKey我期待得到A; 但是,我似乎得到A了两者.

  • 它似乎没有考虑键盘布局.例如,因为Keys.OemMinus我似乎总是得到角色-,即使当前的键盘布局是德语,我期望这个键返回ß.

对此有什么正确的解决方案?

Tim*_*mwi 24

正确的解决方案是ToUnicodeWinAPI功能:

[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);
Run Code Online (Sandbox Code Playgroud)

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

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();
}
Run Code Online (Sandbox Code Playgroud)

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

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 É
Run Code Online (Sandbox Code Playgroud)

还可以使用ToUnicodeEx检索不是当前活动的键盘布局的字符.签名是相同的,除了一个额外的参数,输入区域设置ID,可以使用该LoadKeyboardLayout函数检索.