SendInput 是 Windows 原生最可靠的模拟输入 API,走底层输入队列,兼容管理员进程、UWP 和高 DPI 场景;需严格初始化 INPUT 结构、用绝对坐标换算、分步触发鼠标事件,并注意权限、焦点、DPI 感知与输入法等环境因素。

用 SendInput 模拟鼠标点击最可靠
Windows 原生 API 中,SendInput 是唯一推荐的模拟输入方式——它走的是底层输入队列,能被绝大多数 GUI 程序(包括管理员权限进程、UWP 应用)正常接收。比 mouse_event(已废弃)和 SetCursorPos+keybd_event 组合更稳定,也绕过了 UIPI(用户界面特权隔离)拦截。
关键点:
-
SendInput要求输入结构体严格对齐,INPUT数组必须按需初始化,未用字段清零(尤其dwExtraInfo和联合体 padding) - 鼠标点击需两步:先
MOUSEEVENTF_LEFTDOWN,再MOUSEEVENTF_LEFTUP;单次调用只发一个事件 - 坐标是**屏幕绝对坐标**(0, 0 为左上角),单位为“逻辑像素”,非 DPI 缩放后像素;若需相对当前光标位置,得先用
GetCursorPos读取再计算 - 如果目标窗口在另一个桌面(如锁屏桌面)或高 DPI 缩放不一致,坐标可能偏移——此时应确保程序和目标进程 DPI 感知模式一致(建议设为
PerMonitorV2)
// 左键单击屏幕坐标 (100, 200)
INPUT input[2] = {};
input[0].type = INPUT_MOUSE;
input[0].mi.dx = 100 * (65536 / GetSystemMetrics(SM_CXSCREEN));
input[0].mi.dy = 200 * (65536 / GetSystemMetrics(SM_CYSCREEN));
input[0].mi.mouseData = 0;
input[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN;
input[1].type = INPUT_MOUSE;
input[1].mi.dx = input[0].mi.dx;
input[1].mi.dy = input[0].mi.dy;
input[1].mi.mouseData = 0;
input[1].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTUP;
SendInput(2, input, sizeof(INPUT));
键盘按键必须用虚拟键码,不能直接传 ASCII
SendInput 键盘事件依赖 Windows 虚拟键码(VK_*),比如 VK_A、VK_RETURN、VK_SHIFT。传 ASCII 字符(如 `'a'`)会失败——系统根本不认识这个值。
常见陷阱:
立即学习“C++免费学习笔记(深入)”;
通过 SSH 隧道从 Android 设备在互联网上对 Windows PC 实现完整远程控制的设置与使用指南,适用于远程桌面访问、安全 RDP 等。
- 大小写由
Shift键状态控制,不是靠字符大小写:想输大写 A,要先按VK_SHIFT,再按VK_A,再松开VK_SHIFT - 某些键(如 Win 键、Alt+Tab)受系统策略限制,普通权限进程可能被拦截;需要
SetThreadExecutionState防止休眠,但无法绕过锁定屏幕时的输入屏蔽 - 中文输入法环境下,直接发
VK_A可能触发拼音上屏而非字母输入;如需强制英文,得先用LoadKeyboardLayout切到英文布局,或发VK_PROCESSKEY关闭输入法 - 组合键(Ctrl+C)必须严格控制按键顺序:先
VK_CONTROLdown →VK_Cdown →VK_Cup →VK_CONTROLup;中间不能漏掉 up,否则 Ctrl 会卡住
权限和兼容性问题比想象中更棘手
即使代码完全正确,仍可能无声失败——原因往往不在 API 调用本身,而在运行环境:
- 以标准用户权限启动的程序,无法向以管理员权限运行的目标窗口(如任务管理器、某些游戏反作弊进程)发送输入;必须让自己的进程也以管理员身份启动(加 manifest 或右键“以管理员身份运行”)
- 远程桌面(RDP)会话中,
SendInput默认只影响本地桌面;若要在 RDP 会话内操作,需调用SetThreadDesktop切换到对应桌面对象 - Windows 10/11 启用“增强型键盘安全”(Credential Guard 或 HVCI)时,部分低层输入模拟会被拦截;此时无明确错误码,
SendInput返回值仍是成功数,但目标窗口收不到 - Unity、Qt Quick 或 Electron 应用若用了自绘窗口(WS_EX_LAYERED + 无边框),可能忽略标准输入消息;这时得改用
PostMessage直接发WM_KEYDOWN/WM_LBUTTONDOWN,但仅限同进程或已知窗口句柄场景
调试时别只看返回值,要验证实际效果
SendInput 返回成功数 ≠ 输入被处理。很多新手卡在这一步:函数返回 2(表示两个事件都提交了),但目标程序毫无反应。
排查路径很实际:
- 先用记事本或资源管理器这种“裸窗口”测试,排除目标程序特殊性
- 用
GetForegroundWindow+GetWindowText确认焦点是否真的在预期窗口;焦点丢失会导致输入被丢弃 - 启用 Windows 事件查看器 → Windows 日志 → 应用程序,筛选来源为
Kernel-Input的日志(需提前开启审核策略) - 最简单粗暴:写个空循环每秒打一次
GetKeyState(VK_LBUTTON),确认鼠标左键状态是否真被触发(注意该函数返回的是**当前状态**,不是事件)
真正难的从来不是怎么调 API,而是判断“为什么没生效”——坐标错位、焦点不在、权限不够、DPI 不匹配、输入法干扰、目标进程过滤消息……这些环节任何一个出问题,都会让模拟输入变成静默失败。

















