SendInput模拟鼠标移动时坐标不生效,常见原因在于:**坐标值未按绝对屏幕坐标归一化处理**。SendInput要求MOUSEEVENTF_ABSOLUTE标志下的dx/dy参数必须是0–65535范围的归一化值(对应全屏宽高),而非像素坐标。若直接传入屏幕像素(如x=100, y=200),系统会将其误判为微小相对位移(约0.15%屏幕宽度),导致“不动”或偏移极小。此外,若未同时设置MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,或遗漏dwExtraInfo、time字段(虽非必须但部分沙箱/安全策略下影响投递),也可能失败。还需注意:多显示器环境下需转换到主屏坐标系;UAC提权程序对桌面隔离(Session 0隔离)可能导致输入被拦截;以及部分远程桌面、游戏反作弊机制会过滤或重写SendInput事件。调试建议:先用GetCursorPos验证目标位置,再按公式`dx = x * 65535 / GetSystemMetrics(SM_CXSCREEN)`换算,并确保调用线程拥有前台权限(AllowSetForegroundWindow)。
1条回答 默认 最新
高级鱼 2026-02-26 03:00关注```html一、表层现象:SendInput鼠标移动“看似无响应”
开发者调用
SendInput设置dx=100, dy=200后,光标几乎静止或仅发生像素级抖动——这是最典型的误用表征。根本原因并非API失效,而是Windows输入子系统将未归一化的像素值(如100)直接解释为“相对位移的100个逻辑单位”,而该单位在MOUSEEVENTF_ABSOLUTE模式下被强制映射到0–65535的归一化坐标空间,导致实际位移仅为100 / 65535 ≈ 0.15%屏幕宽度。二、核心机制解析:绝对坐标的归一化契约
- 归一化公式:
dx = (int)((double)x * 65535.0 / GetSystemMetrics(SM_CXSCREEN)) - 纵轴同步换算:
dy = (int)((double)y * 65535.0 / GetSystemMetrics(SM_CYSCREEN)) - 双标志缺一不可:必须同时启用
MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE - 精度陷阱:整数截断需四舍五入(
round()),避免向下取整累积偏移
三、环境维度排查清单
维度 风险点 验证方法 多显示器 坐标未转换至主屏虚拟屏幕( GetSystemMetrics(SM_XVIRTUALSCREEN))EnumDisplayMonitors获取各屏边界,判断目标点所属MonitorUAC/Session隔离 高完整性进程运行于Session 0,无法向用户Session(如Session 1)投递输入 WTSQuerySessionInformation确认当前Session ID是否为交互式会话安全策略 EDR/AV拦截 SendInput或沙箱禁用dwExtraInfo字段显式设置 mi.dwExtraInfo = GetMessageExtraInfo()提升事件可信度四、深度调试路径(含代码片段)
// ✅ 正确归一化+全字段填充示例 POINT target = {1280, 720}; POINT current; GetCursorPos(¤t); // 验证初始位置 int dx = (int)round((double)target.x * 65535.0 / GetSystemMetrics(SM_CXSCREEN)); int dy = (int)round((double)target.y * 65535.0 / GetSystemMetrics(SM_CYSCREEN)); INPUT input = {0}; input.type = INPUT_MOUSE; input.mi.dx = dx; input.mi.dy = dy; input.mi.mouseData = 0; input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; input.mi.time = 0; // 或 GetTickCount() input.mi.dwExtraInfo = GetMessageExtraInfo(); // 关键防御字段 UINT result = SendInput(1, &input, sizeof(INPUT)); if (result != 1) { DWORD err = GetLastError(); // 检查ERROR_ACCESS_DENIED等 }五、反作弊与远程桌面兼容性图谱
graph LR A[SendInput调用] --> B{目标环境} B -->|本地标准桌面| C[高成功率] B -->|远程桌面RDP| D[需启用“重定向本地资源”且服务端允许UI Automation] B -->|EAC/BattlEye| E[99%概率被过滤
→ 改用UI Automation或低级驱动] B -->|Windows Sandbox| F[默认禁用输入模拟
→ 需组策略配置“AllowInputInjection”]六、进阶实践建议
- 始终用
GetCursorPos+ScreenToClient(若目标为特定窗口)双重校验坐标语义 - 对UAC敏感场景,采用
PostMessage(WM_MOUSEMOVE)作为降级方案(仅限同进程窗口) - 在自动化测试框架中封装
SafeSendAbsoluteMouse函数,内置Session检查、DPI缩放补偿(GetDpiForSystem())、多屏坐标归一化 - 记录
GetMessageExtraInfo()返回值变化趋势,识别输入事件是否被中间层劫持 - 针对游戏场景,预研
SetThreadExecutionState防止系统休眠中断输入流
七、历史演进视角
从Windows XP时代起,
```SendInput的归一化设计即为兼容多DPI、多显示器及未来硬件抽象而设。但2012年后UAC强化、2016年Windows Defender Exploit Guard引入输入模拟防护、2020年Windows Sandbox默认隔离策略,使得原本“可用”的代码在新环境中失效——这印证了底层API契约稳定性与上层安全策略演进间的张力关系。资深工程师需建立“API语义—系统策略—硬件抽象”三层认知模型。本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报- 归一化公式: