You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SendInput无法向部分窗口发送基础Unicode字符的问题求助

解决SendInput无法向部分窗口发送常规英文字母的问题

这个问题我之前也碰到过!原因在于不同窗口对键盘输入的处理逻辑有差异:

  • 你当前的代码用KEYEVENTF_UNICODE发送所有字符,这种方式会触发窗口的WM_CHAR或WM_UNICHAR消息。但有些窗口(尤其是一些旧程序或依赖DirectInput的窗口)更依赖WM_KEYDOWN/WM_KEYUP这类虚拟键消息,对于ASCII范围内的字符,它们可能不会处理Unicode字符消息,导致常规英文字母无法被识别。
  • 而像íáó这类特殊字符没有对应的虚拟键,系统只能通过WM_UNICHAR传递,这类窗口通常会处理这些消息,所以能正常接收。

解决方案:分情况处理字符输入

我们需要针对不同类型的字符选择对应的发送方式:

  1. 对于有对应虚拟键的字符(比如英文字母、数字、常见标点),发送虚拟键的按下/抬起事件,适配依赖虚拟键消息的窗口。
  2. 对于无对应虚拟键的特殊字符,继续使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:42:11