在 C# Windows.Forms 项目中,我有一个不提供 KeyPressed 事件的控件(它是一个 COM 控件 - ESRI 映射)。
它仅提供包含KeyEventArgs结构的 KeyUp 和 KeyDown 事件。
如何将 KeyEventArgs 中的信息转换为可显示的 Unicode 字符,同时考虑当前活动的键盘布局等?
诀窍是使用一组 user32.dll 函数:GetWindowThreadProcessId、GetKeyboardLayout、GetKeyboardState和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();
}
}
Itai Bar-Haim 的解决方案很好,但有死键的问题。
第一次使用死键的扫描码调用 CodeToString(int scanCode) 时,ToUnicodeEx() 的返回码为 -1,并且您的方法返回正确的值。
但是在下一次调用时,ToUnicodeEx() 将返回 2。结果字符串将包含预期结果之前的死键,然后是内存垃圾数据。
例如,我用 ^(死键)然后用 $ 调用你的方法。第一次调用返回 ^(不错),但第二次调用返回 ^$azertyui。
你必须做两件事来解决这个问题:
1) 您必须使用 ToUnicodeEx() 返回码绝对值来设置 StringBuilder 的长度。因此,您可以避免在字符之后获取垃圾数据。
2) 当 ToUnicodeEx() 返回码为 -1 时,您必须将 StringBuilder 的第一个字符作为方法的结果。然后您必须从键盘状态中删除死键:使用空格键的扫描码调用 ToUnicodeEx()。
您可能遇到的另一个问题:如果在调用 ToUnicodeEx() 之前按下死键的用户修改了键盘状态,则返回的字符串将包含预期值之前的死键字符。我发现的一种解决方法是在实际调用之前使用空格键的扫描码调用 ToUnicodeEx()。
希望这有帮助!
查看KeysConverter及其 ConvertToString 方法。请记住,并非所有 KeyDown 都映射到 KeyPress。