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

问题描述 投票:0回答:4

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

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

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

  • 它总是返回大写字母。对于

    Keys.A
    ,我期望得到角色
    a
    ,而对于
    Keys.A | Keys.ShiftKey
    ,我期望得到
    A
    ;然而,我似乎都得到了
    A

  • 它似乎没有考虑键盘布局。例如,对于

    Keys.OemMinus
    ,我似乎总是得到字符
    -
    ,即使当前的键盘布局是德语,我希望此键返回
    ß

正确的解决方案是什么?

c# winapi
4个回答
34
投票

正确的解决方案是

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
检索非当前活动键盘布局的字符。除了一个额外参数(输入区域设置 ID)之外,签名是相同的,可以使用
LoadKeyboardLayout
函数检索该参数。


1
投票

我认为可以使用这种方法来实现:

[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


0
投票
    [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();
    }

0
投票

我在WinUI 3中也遇到了这个问题。我尝试使用第一个答案中的方法来解决它,但我需要实际的键盘状态信息。这是因为我想将它与接受键盘输入的控件一起使用。为了实现这一点,我添加了另一个函数来直接获取键盘状态。

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

[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetKeyboardState([Out]byte[] receivingBuffer);

public static string GetCharsFromKeys(VirtualKey key)
{
    StringBuilder buffer = new(256);
    byte[] keyboardState = new byte[256];
    KeyboardHelpers.GetKeyboardState(keyboardState);

    _ = KeyboardHelpers.ToUnicode((uint)key, 0, keyboardState, buffer, 256, 0);
    return buffer.ToString();
}

如果您需要有关此功能的更多信息

GetKeyBoardState(PBYTE lpKeyState);
,您可以阅读此文档:https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardstate

© www.soinside.com 2019 - 2024. All rights reserved.