sendinput 是 windows 后台模拟鼠标拖拽唯一可靠方案,需满足 uiaccess 签名、目标窗口非高 dpi 兼容问题、小步长循环移动、前台激活窗口及精确坐标换算(含 dpi 校正与绝对坐标映射)等条件。
用 sendinput 模拟鼠标拖拽,别碰 mouse_event
windows 后台模拟拖拽,sendinput 是唯一靠谱的选择;mouse_event 已废弃,win10/11 下常失效或被 uac/桌面隔离拦截。
关键不是“能不能点”,而是“系统认不认这个操作”——后台进程默认没权限投递真实输入事件,必须满足两个条件:
- 进程需以
uiAccess="true"签名并放在受信任路径(如C:\Windows\System32\),否则SendInput返回 0 且无错误提示 - 目标窗口不能是高 DPI 缩放且未声明兼容性的进程(比如某些 Electron 应用),否则坐标会偏移
- 拖拽本质是:按下 → 移动多段 → 抬起;不能只发一次移动,得用小步长循环(比如每次
dx=1, dy=1)并加Thread.Sleep(1–5),否则系统当瞬移处理,不触发拖拽逻辑
SetForegroundWindow + SendInput 组合才能让拖拽生效
纯后台调用 SendInput 对大多数 GUI 程序无效——它们只响应“当前前台窗口”的输入。所以必须先激活目标窗口,再发送输入。
但注意:SetForegroundWindow 在 Win10+ 上受限严重,尤其跨用户会话(如服务中调用)必然失败。实操建议:
- 只在同用户、同桌面会话下使用(比如你自己的 WPF/WinForms 主程序里控制另一个自己的窗体)
- 调用前先用
IsIconic(hWnd)判断是否最小化,是则先ShowWindow(hWnd, SW_RESTORE) - 调用后等 100ms 再发
SendInput,避免窗口刚激活但输入队列还没就绪 - 若目标是浏览器或游戏,大概率要换方案(见下一条)
对 Chrome / Edge / Unity 等程序,SendInput 基本无效
这些程序要么自己接管输入(Chromium 的 OOP renderer),要么走 DirectX/OpenGL 渲染路径,完全绕过 Windows 消息循环。此时 SendInput 发的 WM_MOUSEMOVE 根本收不到。
可行替代路径只有两条:
- 用 UI Automation(
UIAutomationClient)找控件后调用IInvokeProvider.Invoke()或ISelectionProvider.Select(),但仅适用于标准控件(按钮、列表项),对 Canvas/自绘区域无效 - 注入 DLL 到目标进程,用
GetAsyncKeyState+ 直接内存写入方式模拟(如 Cheat Engine 常用手法),但需要目标进程非强制签名验证(RequireSignedSystemDrivers关闭)、且极易被杀软拦截 - 最稳的是改用外部工具:通过
AutoItX3.dll的 COM 接口调用(C# 可直接引用),它内部做了多层 fallback,对浏览器拖拽支持较好
坐标换算错误是拖拽偏移的最常见原因
你传给 SendInput 的 dx/dy 是相对坐标,单位是“鼠标移动微单位(mickey)”,不是像素;而 GetCursorPos 返回的是屏幕绝对像素坐标。两者混用必出错。
正确做法分三步:
- 用
GetWindowRect获取目标窗口客户区左上角屏幕坐标 - 用
ScreenToClient把你要拖拽的像素点转成客户区相对坐标 - 用
GetSystemMetrics(SM_CXVIRTUALSCREEN)和 DPI 缩放因子(GetDpiForWindow)校正——特别是高 DPI 下,1 像素可能对应 2 个 mickey - 最后把客户区坐标喂给
SendInput时,必须设dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,并把坐标映射到 0–65535 范围(x * 65535 / screen_width)
漏掉 DPI 校正,4K 屏上拖拽位置会偏移 2–4 倍;漏掉 MOUSEEVENTF_ABSOLUTE,就只能靠相对移动硬凑,精度和稳定性全垮。











