SendInput模拟鼠标点击最可靠,因其走系统输入队列且兼容性强;需构造INPUT结构体,设dwType为INPUT_MOUSE,填充mi字段;绝对坐标需映射0~65535到屏幕分辨率,点击须成对发送DOWN与UP事件。

Windows平台用SendInput模拟鼠标点击最可靠
直接调用SendInput是Windows下模拟真实鼠标点击的首选,它走的是系统输入队列,能被几乎所有应用识别,不像mouse_event(已废弃)或直接写寄存器那样容易被拦截或失效。
关键点在于构造INPUT结构体,并正确设置dwType为INPUT_MOUSE,再填充mi(MOUSEINPUT)字段。坐标默认是屏幕绝对坐标(需启用MOUSEEVENTF_ABSOLUTE),但更常用的是相对移动+点击组合。
- 绝对坐标需将0~65535映射到当前屏幕分辨率,可用
GetSystemMetrics(SM_CXSCREEN)换算 - 点击必须成对发送:先
MOUSEEVENTF_LEFTDOWN,再MOUSEEVENTF_LEFTUP,间隔太短( - 如果目标窗口在后台,部分UWP或高DPI应用可能不响应,需配合
SetForegroundWindow或AttachThreadInput
Linux下用uinput设备需要root权限
uinput是Linux内核提供的用户空间输入设备接口,本质是创建一个虚拟的/dev/uinput设备节点,然后向它写入struct input_event。它比X11的XTestFakeButtonEvent更底层、更通用,但必须有cap_sys_admin或root权限。
常见错误是没正确初始化设备能力(UI_SET_EVBIT和UI_SET_KEYBIT)、忘记ioctl(fd, UI_DEV_CREATE),或者事件时间戳为0导致内核丢弃。
立即学习“C++免费学习笔记(深入)”;
- 必须按顺序调用:
open("/dev/uinput") → ioctl(..., UI_SET_EVBIT, EV_KEY) → ioctl(..., UI_SET_KEYBIT, BTN_LEFT) → ioctl(..., UI_DEV_CREATE) - 每次点击至少发3个事件:按下(
EV_KEY/BTN_LEFT/1)、同步(EV_SYN/SYN_REPORT)、释放(EV_KEY/BTN_LEFT/0) - 普通用户可通过
udev规则赋予权限:SUBSYSTEM=="uinput", MODE="0660", GROUP="input",然后把用户加进input组
macOS用CGEventCreateMouseEvent要处理权限和沙盒
macOS从10.14开始强制要求辅助功能权限,且App必须签名并启用com.apple.security.temporary-exception.apple-events等例外才能发送鼠标事件。绕过权限基本不可能,强行调用会静默失败或弹出系统提示。
CGEventCreateMouseEvent生成事件后,必须用CGEventPost投递到kCGHIDEventTap(需权限)或kCGSessionEventTap(仅当前会话)。直接投递kCGAnnotatedSessionEventTap会被沙盒拦截。
- 调试时先检查权限:
csrutil status确认SIP未禁用,再在“系统设置→隐私与安全性→辅助功能”中手动勾选你的程序 - 事件坐标是全局屏幕坐标(左上角为原点),单位是点(points),不是像素,高DPI下需用
CGDisplayScaleFactor换算 - 如果App是命令行工具,必须打包为
.appbundle,否则无法在辅助功能列表中显示
跨平台方案别硬刚,优先考虑pyautogui或robotjs
C++本身没有标准跨平台鼠标控制API,自己封装三套逻辑成本远高于引入轻量绑定库。比如pyautogui(Python)或robotjs(Node.js)背后就是调用各平台原生API,且已处理好坐标换算、权限提示、事件节流等细节。
如果非要用C++,推荐基于libuiohook——它封装了Windows SendInput、X11 XTest、macOS CGEvent,但注意其macOS后端仍依赖辅助功能权限,且最新版对ARM64 macOS支持不稳定。
-
libuiohook的hook_mouse_click是监听,不是模拟;模拟得用uiohook_post_event+EVENT_MOUSE_CLICKED - 静态链接
libuiohook时,macOS需额外链接-framework ApplicationServices -framework CoreGraphics - 不要试图用C++调用Python的
pyautogui——进程间通信开销大,且pyautogui的fail-safe机制可能意外中断你的流程
真正难的从来不是发一个点击事件,而是确保它落在目标控件上、不被DPI缩放偏移、不触发安全软件拦截、不卡在权限弹窗里。每个平台的“正常工作”,背后都有一堆条件要满足。


















