将键码转换为相关的显示字符

时间:2008-12-16 08:58:26

标签: .net windows winforms input keyboard

在C#Windows.Forms项目中,我有一个不提供KeyPressed事件的控件(它是一个COM控件 - ESRI映射)。

它仅提供包含KeyEventArgs结构的KeyUp和KeyDown事件。

如何将KeyEventArgs中的信息转换为可显示的Unicode字符,并考虑当前的活动键盘布局等?

3 个答案:

答案 0 :(得分:11)

诀窍是使用一组user32.dll函数:GetWindowThreadProcessIdGetKeyboardLayoutGetKeyboardStateToUnicodeEx

  1. GetWindowThreadProcessId函数与控制句柄一起使用,以获得相关的本机线程ID。
  2. 将该线程ID传递给GetKeyboardLayout以获取当前的键盘布局。
  3. 致电GetKeyboardState以获取当前的键盘状态。这有助于下一个方法根据修饰符状态决定生成哪个字符。
  4. 最后,使用所需的虚拟密钥代码和扫描代码(这两个可以是相同的)调用ToUnicodeEx函数,当前的键盘状态,字符串生成器作为字符串持有者(保存结果),没有标志(0)和当前的键盘布局指针。
  5. 如果结果不为零,则只返回第一个返回的字符。

      public class KeyboardHelper
        {
            [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
            private static extern int ToUnicodeEx(
                uint wVirtKey,
                uint wScanCode,
                Keys[] lpKeyState,
                StringBuilder pwszBuff,
                int cchBuff,
                uint wFlags,
                IntPtr dwhkl);
    
            [DllImport("user32.dll", ExactSpelling = true)]
            internal static extern IntPtr GetKeyboardLayout(uint threadId);
    
            [DllImport("user32.dll", ExactSpelling = true)]
            internal static extern bool GetKeyboardState(Keys[] keyStates);
    
            [DllImport("user32.dll", ExactSpelling = true)]
            internal static extern uint GetWindowThreadProcessId(IntPtr hwindow, out uint processId);
    
            public static string CodeToString(int scanCode)
            {
                uint procId;
                uint thread = GetWindowThreadProcessId(Process.GetCurrentProcess().MainWindowHandle, out procId);
                IntPtr hkl = GetKeyboardLayout(thread);
    
                if (hkl == IntPtr.Zero)
                {
                    Console.WriteLine("Sorry, that keyboard does not seem to be valid.");
                    return string.Empty;
                }
    
                Keys[] keyStates = new Keys[256];
                if (!GetKeyboardState(keyStates))
                    return string.Empty;
    
                StringBuilder sb = new StringBuilder(10);
                int rc = ToUnicodeEx((uint)scanCode, (uint)scanCode, keyStates, sb, sb.Capacity, 0, hkl);
                return sb.ToString();
            }
        }
    

答案 1 :(得分:8)

Itai Bar-Haim的解决方案很不错,但死钥匙有问题。

第一次使用死键的扫描码调用CodeToString(int scanCode)时,ToUnicodeEx()的返回码为-1,您的方法返回正确的值。

但是在下一次调用时,ToUnicodeEx()将返回2.结果字符串将在预期结果之前包含死键,然后是内存垃圾数据。

例如,我用^(死键)然后用$调用你的方法。第一个调用返回^(不错),但第二个调用返回^ $ azertyui。

你必须做两件事来解决这个问题:

1)您必须使用ToUnicodeEx()返回码绝对值来设置StringBuilder的长度。所以你不要在字符后面获取垃圾数据。

2)当ToUnicodeEx()返回代码为-1时,必须将StringBuilder的第一个char作为方法的结果。然后你必须从键盘状态中删除死键:使用Space键的扫描码调用ToUnicodeEx()。

您可能遇到的另一个问题:如果在您调用ToUnicodeEx()之前遇到死键的用户修改了键盘状态,则返回的字符串将包含预期值之前的死键字符。我找到的解决方法是在实际调用之前使用Space键的扫描码调用ToUnicodeEx()。

希望这有帮助!

答案 2 :(得分:0)

查看KeysConverter及其ConvertToString方法。请记住,并非所有KeyDown都映射到KeyPress。

相关问题