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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/370754/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 11:44:35  来源:igfitidea点击:

convert a keycode to the relevant display character

.netwindowswinformsinputkeyboard

提问by Asher

In a C# Windows.Forms project I have a control that does not supply the KeyPressed event (It's a COM control – ESRI map).

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

It only supplies the KeyUp and KeyDown events, containing the KeyEventArgsstructure.

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

How can I convert the information in KeyEventArgs to a displayable Unicode character, taking the current active keyboard layout into account, etc.?

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

回答by Itai Bar-Haim

The trick is to use a set of user32.dll functions: GetWindowThreadProcessId, GetKeyboardLayout, GetKeyboardStateand ToUnicodeEx.

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

  1. Use the GetWindowThreadProcessIdfunction with your control handle to achieve the relevant native thread id.
  2. Pass that thread id to GetKeyboardLayoutto get the current keyboard layout.
  3. Call GetKeyboardStateto get the current keyboard state. This helps the next method to decide which character to generate according to modifiers state.
  4. Finally, call the ToUnicodeExfunction with the wanted virtual key code and scan code (those two can be the same), the current keyboard state, a string builder as a string holder (to hold the result), no flags (0), and the current keyboard layout pointer.
  1. 使用GetWindowThreadProcessId函数和您的控制句柄来获取相关的本机线程 ID。
  2. 将该线程 id 传递给GetKeyboardLayout以获取当前键盘布局。
  3. 调用GetKeyboardState获取当前键盘状态。这有助于下一个方法根据修饰符状态决定生成哪个角色。
  4. 最后,调用ToUnicodeEx与通缉的虚拟键代码和扫描码功能(这两个可以是相同的),当前的键盘状态,一个字符串生成器作为一个字符串持有人(持有的结果),不挂旗(0),当前键盘布局指针。

If the result is not zero, just return the first returned character.

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

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

回答by Stephane

Itai Bar-Haim's solution is nice but has a problem with dead keys.

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

The first time you call CodeToString(int scanCode) with the scan code of a dead key, the return code of ToUnicodeEx() is -1 and your method returns the right value.

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

But on the next call, ToUnicodeEx() will return 2. The result string will contain the dead key before the expected result, followed by memory junk data.

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

For instance, I call your method with ^ (dead key) and then with $. The first call returns ^ (nice), but the second call returns ^$azertyui.

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

You must do two things to fix that :

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

1) You must use the ToUnicodeEx() return code absolute value to set the length of your StringBuilder. So you avoid getting junk data after the characters.

1) 您必须使用 ToUnicodeEx() 返回码绝对值来设置 StringBuilder 的长度。因此,您可以避免在字符之后获取垃圾数据。

2) When ToUnicodeEx() return code is -1, you must take the first char of your StringBuilder as a result for your method. And then you must remove the dead key from the keyboard state : recall ToUnicodeEx() with the scancode of the Space key.

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

Another problem you could have : if the keyboard state has been modified by a user who hit a dead key before you call ToUnicodeEx(), the returned string will contains the dead key character before the expected value. A workaround I found is to call ToUnicodeEx() with the scancode of the Space key before the actual call.

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

Hope this help!

希望这有帮助!

回答by Martin Plante

Look at KeysConverterand its ConvertToString method. Remember that not all KeyDown map to a KeyPress.

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