SendInput无法向部分窗口发送基础Unicode字符的问题求助
解决SendInput无法向部分窗口发送常规英文字母的问题
这个问题我之前也碰到过!原因在于不同窗口对键盘输入的处理逻辑有差异:
- 你当前的代码用
KEYEVENTF_UNICODE发送所有字符,这种方式会触发窗口的WM_CHAR或WM_UNICHAR消息。但有些窗口(尤其是一些旧程序或依赖DirectInput的窗口)更依赖WM_KEYDOWN/WM_KEYUP这类虚拟键消息,对于ASCII范围内的字符,它们可能不会处理Unicode字符消息,导致常规英文字母无法被识别。 - 而像íáó这类特殊字符没有对应的虚拟键,系统只能通过
WM_UNICHAR传递,这类窗口通常会处理这些消息,所以能正常接收。
解决方案:分情况处理字符输入
我们需要针对不同类型的字符选择对应的发送方式:
- 对于有对应虚拟键的字符(比如英文字母、数字、常见标点),发送虚拟键的按下/抬起事件,适配依赖虚拟键消息的窗口。
- 对于无对应虚拟键的特殊字符,继续使用Unicode方式发送。
修改后的完整代码
首先补充必要的WinAPI导入和结构体定义,然后重写发送逻辑:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; public static class InputSender { // WinAPI函数导入 [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); [DllImport("kernel32.dll")] private static extern uint GetLastError(); [DllImport("user32.dll")] private static extern IntPtr SetMessageExtraInfo(IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern short VkKeyScanExW(char ch, IntPtr dwhkl); [DllImport("user32.dll")] private static extern IntPtr GetKeyboardLayout(uint idThread); // 常量定义 private const int INPUT_KEYBOARD = 1; private const int KEYEVENTF_UNICODE = 0x0004; private const int KEYEVENTF_KEYUP = 0x0002; private const byte VK_SHIFT = 0x10; // 结构体定义 [StructLayout(LayoutKind.Sequential)] private struct INPUT { public int type; public KEYBDINPUT ki; } [StructLayout(LayoutKind.Sequential)] private struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } /// <summary> /// Sends Unicode (UTF16) string to foreground window, compatible with all window types. /// </summary> /// <param name="inputString">String to be sent to foreground window.</param> internal static void Send(string inputString) { if (string.IsNullOrEmpty(inputString)) return; var inputs = new List<INPUT>(); // 获取当前线程的键盘布局,确保虚拟键映射正确 var keyboardLayout = GetKeyboardLayout(0); foreach (char c in inputString) { short keyScanResult = VkKeyScanExW(c, keyboardLayout); byte virtualKey = (byte)(keyScanResult & 0xFF); byte shiftState = (byte)((keyScanResult >> 8) & 0xFF); // 检查字符是否有对应的虚拟键 if (virtualKey != 0xFF) { // 如果需要Shift键(比如大写字母、!@#等),先按下Shift if ((shiftState & 1) != 0) { inputs.Add(CreateKeyInput(VK_SHIFT, isKeyUp: false)); } // 发送按键按下事件 inputs.Add(CreateKeyInput(virtualKey, isKeyUp: false)); // 发送按键抬起事件 inputs.Add(CreateKeyInput(virtualKey, isKeyUp: true)); // 如果之前按下了Shift,现在释放它 if ((shiftState & 1) != 0) { inputs.Add(CreateKeyInput(VK_SHIFT, isKeyUp: true)); } } else { // 无对应虚拟键,使用Unicode方式发送 ushort unicodeChar = c; inputs.Add(CreateUnicodeInput(unicodeChar, isKeyUp: false)); inputs.Add(CreateUnicodeInput(unicodeChar, isKeyUp: true)); } } uint sentCount = SendInput((uint)inputs.Count, inputs.ToArray(), Marshal.SizeOf(typeof(INPUT))); if (sentCount == 0) { Debug.WriteLine($"SendInput error: {GetLastError()}"); } } // 辅助方法:创建虚拟键输入事件 private static INPUT CreateKeyInput(byte virtualKey, bool isKeyUp) { return new INPUT { type = INPUT_KEYBOARD, ki = new KEYBDINPUT { wVk = virtualKey, wScan = 0, dwFlags = isKeyUp ? KEYEVENTF_KEYUP : 0, time = 0, dwExtraInfo = SetMessageExtraInfo(IntPtr.Zero) } }; } // 辅助方法:创建Unicode输入事件 private static INPUT CreateUnicodeInput(ushort unicodeChar, bool isKeyUp) { return new INPUT { type = INPUT_KEYBOARD, ki = new KEYBDINPUT { wVk = 0, wScan = unicodeChar, dwFlags = KEYEVENTF_UNICODE | (isKeyUp ? KEYEVENTF_KEYUP : 0), time = 0, dwExtraInfo = SetMessageExtraInfo(IntPtr.Zero) } }; } }
代码说明
VkKeyScanExW:这个函数会根据当前键盘布局,返回字符对应的虚拟键码和Shift状态,帮我们判断是否需要用虚拟键方式发送。- 分逻辑处理:对于有虚拟键的字符,模拟真实的按键操作(包括Shift键的配合),让依赖虚拟键消息的窗口能正确识别;对于特殊字符,保留原有的Unicode发送方式。
- 代码拆分:把创建输入事件的逻辑拆成辅助方法,让代码更易读和维护。
这样修改后,你的代码就能在所有窗口中正常发送包括常规英文字母、标点和特殊字符在内的所有Unicode字符串了。
内容的提问来源于stack exchange,提问作者Jedi Commymullah
相关产品推荐
相关产品推荐

