C#如何将虚拟键码转换为字符?

Posted

技术标签:

【中文标题】C#如何将虚拟键码转换为字符?【英文标题】:C# How to translate virtual keycode to char? 【发布时间】:2010-09-24 01:02:51 【问题描述】:

我正在尝试将虚拟键码映射到字符。

我的代码使用 ProcessCmdKey 来监听 WM_KEYDOWN,这使我可以访问所按下的键。例如,当我按下单引号时,我得到一个 222 的键,我想将它映射到 keychar 39,它表示......你猜对了......单引号。

我的开发环境是: - .net 框架 2.0 - UserControl 放置在很多地方

你知道问题的答案吗?

【问题讨论】:

MapVirtualKey() ? 【参考方案1】:

是的,我确实使用了MapVirtualKey 方法。但我期待有关如何使用它的更多详细信息:使用什么 DllImport 指令,enum 特定于映射到字符等。

我不喜欢你在谷歌上搜索 5 秒钟然后提出解决方案的这些答案:真正的挑战是将所有部分放在一起,而不必浪费时间处理大量无样本的 MSDN 页面或其他编码论坛以获得您的答案。无意冒犯,但你的回答(甚至是好的)毫无价值,因为我在论坛上发布我的问题之前就有了这个答案!

那么你去吧,我将发布我正在寻找的东西 - 一个开箱即用的 C# 解决方案:

1- 将此指令放在你的类中:

[DllImport("user32.dll")]
static extern int MapVirtualKey(uint uCode, uint uMapType);

2- 像这样检索你的字符:

  protected override bool ProcessCmdKey(ref Message msg, Keys keyData)      
  
     const int WM_KEYDOWN = 0x100;

     if (msg.Msg == WM_KEYDOWN)
                 
        // 2 is used to translate into an unshifted character value 
        int nonVirtualKey = MapVirtualKey((uint)keyData, 2);

        char mappedChar = Convert.ToChar(nonVirtualKey);
     

     return base.ProcessCmdKey(ref msg, keyData);
  

感谢您的关心...并享受!

【讨论】:

感谢您的跟进。如果您已经看过 MapVirtualKey() 或许您应该将其包含在您的问题中(即“我看过 MapVirtualKey() 但我不知道如何从 C# 调用它)。为了您未来的需要,您可能会发现 pinvoke.net 很有用。 对于 MapVirtualKey,这里是来自 pinvoke.net 的条目:pinvoke.net/default.aspx/user32/MapVirtualKey.html 小修正:MapVirtualKey 实际上返回一个 uint,而不是一个 int。但是感谢您提供的那段代码,这正是我自己的项目所需要的。 非常有帮助的问答。我正在寻找这个。谢谢。 这应该是投票最多的答案。当前投票最多的不是字母数字字符。【参考方案2】:

这不就是 System.Windows.Form.KeysConverter 类的用途吗?

KeysConverter kc = new KeysConverter();
string keyChar = kc.ConvertToString(keyData);

【讨论】:

刚刚注意到我从来没有回复过...KeysConverter exists in .NET 2.0. 嗯,但这只是部分起作用。例如,它不起作用:(new KeysConverter()).ConvertToString(Keys.OemQuestion) (Spoiler: 它将返回 OemQuestion 而不是 ?)。此外,不清楚如何处理 Shift 键。有可行的解决方案吗? @Hi-Angel 确保将正确的 KeysConverter 与正确的 Keys 枚举匹配。 System.Windows.Forms.KeyConverter 用于 WinForms,System.Windows.Input.KeyConverter 用于 WPF。 至于修饰键,Keys 枚举实际上由标志组成,所以 D 实际上是 Keys.Shift | Keys.D 不,这与 Keys.ToString() 相同【参考方案3】:

KeysConverter 获取键名而不是键“文本” 例如:“Num2”而不是“2” MapVirtualKey 适用于英语,但适用于使用 MapVirtualKeyEx 的非英语字符文档状态,但这需要语言环境标识符 该标识符由 LoadKeyBoardLayout 加载,它需要一个文化 id 常量 但是在找到正确的 id 值后,我尝试了它并没有用,所以最后我把整个东西都扔了

【讨论】:

MapVirtualKey 也适用于非英语布局。它使用实际的键盘布局。【参考方案4】:

在阅读并测试了所提供的一些答案后,我想我会建议一个替代方案。

正如MM 所述,System.Windows.KeysConverter 不提供键的字符表示,而是提供枚举的名称,例如“输入”而不是“\n”。

Horas 在回答他自己的问题时建议的 MapVirtualKey 方法是一个很好的起点,但仍然不支持大写锁定或使用 shift 键输入的字符,例如'!'、'$' 和 '>'。

我正在使用的 MapVirtualKey 方法的替代方法是 Keys 类的扩展方法:

public static char ToChar(this Keys key)

    char c = '\0';
    if((key >= Keys.A) && (key <= Keys.Z))
    
        c = (char)((int)'a' + (int)(key - Keys.A));
    

    else if((key >= Keys.D0) && (key <= Keys.D9))
    
        c = (char)((int)'0' + (int)(key - Keys.D0));
    

    return c;

上面显示的方法将提供对字母数字字符的支持。可以使用 switch 语句或查找表来实现对附加字符的支持。

【讨论】:

这是使用System.Windows.Forms 还是System.Windows.InputKeySystem.Windows.Input中的一个枚举,但是这里没有用到,也不清楚Keys是从哪里来的。 'Keys' 是 'System.Windows.Forms' 的一部分【参考方案5】:

我正在寻找类似的东西,但我需要映射到当前键盘布局的字符。由于以上答案都没有满足我的要求,这就是我想出的。


        public string KeyCodeToUnicode(Keys key)
        
            byte[] keyboardState = new byte[255];
            bool keyboardStateStatus = GetKeyboardState(keyboardState);

            if (!keyboardStateStatus)
            
                return "";
            

            uint virtualKeyCode = (uint)key;
            uint scanCode = MapVirtualKey(virtualKeyCode, 0);
            IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);

            StringBuilder result = new StringBuilder();
            ToUnicodeEx(virtualKeyCode, scanCode, keyboardState, result, (int)5, (uint)0, inputLocaleIdentifier);

            return result.ToString();
        

        [DllImport("user32.dll")]
        static extern bool GetKeyboardState(byte[] lpKeyState);

        [DllImport("user32.dll")]
        static extern uint MapVirtualKey(uint uCode, uint uMapType);

        [DllImport("user32.dll")]
        static extern IntPtr GetKeyboardLayout(uint idThread);

        [DllImport("user32.dll")]
        static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);

【讨论】:

优秀。它与非英文字符完美配合。我从 KeyDown 事件中使用它并使用 e.Keycode 或 e.KeyData 调用它,两者都有效。谢谢 我们需要的英雄,但不是我们应得的英雄! 这很棒。按预期工作。另一方面,如此明显的任务需要大量代码。干得好。 我注意到在调用函数时按住修饰键时出现意外结果并开始想知道...为什么要获取当前的键盘状态?在没有与当前键盘状态相关的部分的情况下按预期工作 - 我不明白为什么当前状态无论如何都会相关......我错过了什么吗? 看起来这是从 PInvoke.net (pinvoke.net/default.aspx/user32.tounicodeex) 中提取的(未注明出处)【参考方案6】:

我刚刚编写了对Ivan Petrov answer 的改进,以在 WPF 中显示按下的组合键的字符串表示,请参阅下面的代码:

public static string GetKeyString(Key key, ModifierKeys modifiers)

    string result = "";
    if (key != Key.None)
    
        // Setup modifiers
        if (modifiers.HasFlag(ModifierKeys.Control))
            result += "Ctrl + ";
        if (modifiers.HasFlag(ModifierKeys.Alt))
            result += "Alt + ";
        if (modifiers.HasFlag(ModifierKeys.Shift))
            result += "Shift + ";
        // Get string representation
        string keyStr = key.ToString();
        int keyInt = (int)key;
        // Numeric keys are returned without the 'D'
        if (key >= Key.D0 && key <= Key.D9)
            keyStr = char.ToString((char)(key - Key.D0 + '0'));
        // Char keys are returned directly
        else if (key >= Key.A && key <= Key.Z)
            keyStr = char.ToString((char)(key - Key.A + 'A'));
        // If the key is a keypad operation (Add, Multiply, ...) or an 'Oem' key, P/Invoke
        else if ((keyInt >= 84 && keyInt <= 89) || keyInt >= 140)
            keyStr = KeyCodeToUnicode(key);
        result += keyStr;
    
    return result;


private static string KeyCodeToUnicode(Key key)

    byte[] keyboardState = new byte[255];
    bool keyboardStateStatus = GetKeyboardState(keyboardState);

    if (!keyboardStateStatus)
    
        return "";
    

    uint virtualKeyCode = (uint)KeyInterop.VirtualKeyFromKey(key);
    uint scanCode = MapVirtualKey(virtualKeyCode, 0);
    IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);

    StringBuilder result = new StringBuilder();
    ToUnicodeEx(virtualKeyCode, scanCode, new byte[255], result, (int)5, (uint)0, inputLocaleIdentifier);

    return result.ToString();


[DllImport("user32.dll")]
static extern bool GetKeyboardState(byte[] lpKeyState);

[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);

[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);

[DllImport("user32.dll")]
static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);

【讨论】:

嗯,这可行,但如果按下非字母数字键,则返回结果为空字符串。同样,如果用户按下spaceTAB,结果将是invisible,让用户认为没有返回任何结果。是否有一个非字母数字键代码及其对应名称的列表,以便我们可以返回按下的键的名称? 可能在GetKeyString方法中加入更多条件。【参考方案7】:

如果你使用的是WPF(我没有用WinForms尝试过),有一个非常简单的解决方案,直接返回按下的字符,即Window的TextInput Event。

public partial class MainWindow : Window

    public MainWindow()
    
        InitializeComponent();
        TextInput += MainWindow_TextInput;
    

    private void MainWindow_TextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    
        txtInput.Text += e.Text;
    

【讨论】:

以上是关于C#如何将虚拟键码转换为字符?的主要内容,如果未能解决你的问题,请参考以下文章

将虚拟键码转换为 unicode 字符串

无法从虚拟键码转换为 unicode

获得虚拟键键码

keydown 事件的键码

SDL 物理键码和 SDL 虚拟键码有啥区别?

键盘扫描码 ASCII码的区别?