在C#Windows.Forms项目中,我有一个不提供KeyPressed事件的控件(它是一个COM控件 - ESRI映射).
它仅提供包含KeyEventArgs结构的KeyUp和KeyDown事件.
如何将KeyEventArgs中的信息转换为可显示的Unicode字符,考虑当前的活动键盘布局等?
诀窍是使用一组user32.dll函数:GetWindowThreadProcessId,GetKeyboardLayout,GetKeyboardState和ToUnicodeEx.
将GetWindowThreadProcessId函数与您的控件句柄一起使用以实现相关的本机线程ID.
将该线程ID传递给GetKeyboardLayout以获取当前的键盘布局.
调用GetKeyboardState以获取当前键盘状态.这有助于下一个方法根据修饰符状态决定生成哪个字符.
最后,调用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(); } }
Itai Bar-Haim的解决方案很不错,但死键有问题.
当您第一次调用CodeToString(INT扫描码)用死键的扫描码,ToUnicodeEx()的返回代码为-1,你的方法返回正确的值.
但是在下一次调用时,ToUnicodeEx()将返回2.结果字符串将在预期结果之前包含死键,然后是内存垃圾数据.
例如,我用^(死键)然后用$调用你的方法.第一个调用返回^(不错),但第二个调用返回^ $ azertyui.
你必须做两件事来解决这个问题:
1)您必须使用ToUnicodeEx()返回代码绝对值来设置StringBuilder的长度.因此,您可以避免在字符后获取垃圾数据.
2)当ToUnicodeEx()返回代码为-1时,必须将StringBuilder的第一个char作为方法的结果.然后你必须从键盘状态中删除死键:使用Space键的扫描码调用ToUnicodeEx().
还有一个问题,你可以有:如果键盘状态已经由谁打一个死键调用ToUnicodeEx(之前用户修改),返回的字符串将包含该预期值前死键字符.我找到的解决方法是在实际调用之前使用Space键的scancode调用ToUnicodeEx().
希望这有帮助!