将按键码转换为对应的显示字符

5
在C# Windows.Forms项目中,我有一个控件没有提供KeyPressed事件(它是一个COM控件 - ESRI地图)。
它只提供KeyDown和KeyUp事件,包含KeyEventArgs结构。
如何将KeyEventArgs中的信息转换为可显示的Unicode字符,并考虑当前活动键盘布局等因素?
3个回答

11
使用一组user32.dll函数来解决该问题:GetWindowThreadProcessIdGetKeyboardLayoutGetKeyboardStateToUnicodeEx
1.使用控件句柄调用GetWindowThreadProcessId函数,以获取相关的本地线程ID。 2.将该线程ID传递到GetKeyboardLayout以获取当前键盘布局。 3.调用GetKeyboardState以获取当前键盘状态。这有助于下一个方法根据修改器状态决定要生成哪个字符。 4.最后,使用所需的虚拟键码和扫描码(这两个可以相同)、当前键盘状态、作为字符串持有者的字符串生成器(用于保存结果)、没有标志(0)和当前键盘布局指针调用ToUnicodeEx函数。 如果结果不为零,则返回第一个返回的字符。
  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();
        }
    }

8
Itai Bar-Haim的解决方案很不错,但有一个关于死键的问题。
第一次使用扫描码调用CodeToString(int scanCode)时,ToUnicodeEx()的返回代码为-1,并且您的方法返回正确的值。
但是在下一次调用时,ToUnicodeEx()将返回2。结果字符串将包含预期结果之前的dead key,后面跟着内存垃圾数据。
例如,我使用^(dead key)和$分别调用您的方法。第一个调用返回^(好),但第二个调用返回^ $ azertyui。
为了解决这个问题,您必须执行以下两个操作:
1)您必须使用ToUnicodeEx()的返回代码绝对值来设置StringBuilder的长度。因此,避免在字符之后获得垃圾数据。
2)当ToUnicodeEx()返回代码为-1时,您必须将StringBuilder的第一个字符作为方法的结果。然后,您必须从键盘状态中删除dead key:使用Space键的扫描码重新调用ToUnicodeEx()。
您可能遇到的另一个问题是:如果键盘状态已被在调用ToUnicodeEx()之前按下dead key的用户修改,则返回的字符串将在预期值之前包含dead key字符。我找到的解决方法是,在实际调用之前使用Space键的扫描码调用ToUnicodeEx()。
希望这可以帮到您!

0

看看KeysConverter和它的ConvertToString方法。记住,并不是所有的KeyDown都映射到KeyPress。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接