【问题标题】:convert a keycode to the relevant display character将键码转换为相关的显示字符
【发布时间】:2010-09-27 02:40:53
【问题描述】:

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

它只提供 KeyUp 和 KeyDown 事件,包含 KeyEventArgs 结构。

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

【问题讨论】:

    标签: .net windows winforms input keyboard


    【解决方案1】:

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

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

    如果结果不为零,则返回第一个返回的字符。

      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();
            }
        }
    

    【讨论】:

      【解决方案2】:

      Itai Bar-Haim 的解决方案不错,但存在死键问题。

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

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

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

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

      1) 您必须使用 ToUnicodeEx() 返回码绝对值来设置 StringBuilder 的长度。所以你避免在字符之后得到垃圾数据。

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

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

      希望对您有所帮助!

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-23
          • 1970-01-01
          相关资源
          最近更新 更多