C++如何获取鼠标原始移动输入而非光标位置(Windows10环境)
Windows 10 环境下C++原始鼠标输入移动检测实现
使用Windows原生Raw Input API即可实现需求,该API直接从输入设备驱动层获取鼠标上报的相对位移数据,完全不受屏幕边界、程序锁定光标逻辑的影响,适配你提到的两类光标检测失效场景。
核心实现逻辑
- 第一步:向系统注册鼠标类型的原始输入设备,指定接收原始输入消息的窗口句柄
- 第二步:截获窗口的
WM_INPUT消息,这是系统投递原始输入数据的标准消息 - 第三步:解析消息中携带的原始鼠标数据,提取X/Y轴的相对位移增量,根据增量正负判断移动方向
注意:Windows系统中鼠标坐标系Y轴向下为正,因此Y轴增量为负代表鼠标向上移动,Y轴增量为正代表向下移动;X轴向右为正,X轴增量为负代表向左移动。
完整可运行代码
#include <Windows.h> #include <iostream> #include <cstdlib> // 保存控制台原窗口过程指针 WNDPROC originalConsoleProc = nullptr; // 自定义窗口过程,用于截获WM_INPUT消息 LRESULT CALLBACK RawInputProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_INPUT) { UINT dataSize = 0; // 先获取原始输入数据的大小 GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dataSize, sizeof(RAWINPUTHEADER)); if (dataSize > 0) { // 分配缓冲区存储数据 LPBYTE buffer = new BYTE[dataSize]; if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &dataSize, sizeof(RAWINPUTHEADER)) == dataSize) { RAWINPUT* rawData = (RAWINPUT*)buffer; // 判断是否为鼠标输入 if (rawData->header.dwType == RIM_TYPEMOUSE) { RAWMOUSE mouseData = rawData->data.mouse; // 判断是否为相对移动事件 if (mouseData.usFlags & MOUSE_MOVE_RELATIVE) { LONG deltaX = mouseData.lLastX; LONG deltaY = mouseData.lLastY; // 可根据需求调整阈值过滤鼠标微抖动 const LONG MOVE_THRESHOLD = 1; if (abs(deltaY) >= MOVE_THRESHOLD) { if (deltaY < 0) { std::cout << "the mouse is moving up\n"; } else { std::cout << "the mouse is moving down\n"; } } if (abs(deltaX) >= MOVE_THRESHOLD) { if (deltaX < 0) { std::cout << "the mouse is moving left\n"; } else { std::cout << "the mouse is moving right\n"; } } } } } delete[] buffer; } } // 调用原窗口过程保证其他消息正常处理 return CallWindowProc(originalConsoleProc, hwnd, msg, wParam, lParam); } int main() { // 获取当前控制台窗口句柄 HWND consoleHwnd = GetConsoleWindow(); if (!consoleHwnd) { std::cerr << "获取控制台窗口句柄失败\n"; return 1; } // 子类化控制台窗口,替换窗口过程以接收WM_INPUT originalConsoleProc = (WNDPROC)SetWindowLongPtr(consoleHwnd, GWLP_WNDPROC, (LONG_PTR)RawInputProc); // 注册鼠标原始输入设备 RAWINPUTDEVICE rid; rid.usUsagePage = 0x01; // 通用桌面设备类 rid.usUsage = 0x02; // 鼠标设备 rid.dwFlags = RIDEV_INPUTSINK | RIDEV_NOLEGACY; // 即使窗口不在前台也接收消息,屏蔽legacy鼠标消息 rid.hwndTarget = consoleHwnd; if (!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))) { std::cerr << "注册原始输入设备失败,错误码:" << GetLastError() << "\n"; return 1; } std::cout << "原始鼠标输入检测已启动,移动鼠标查看输出...\n"; // 标准消息循环 MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
编译注意事项
- 编译时需要链接
user32.lib库,Visual Studio环境下默认会链接该库,MinGW环境如果出现链接错误可手动添加链接参数-luser32 - 代码中
MOVE_THRESHOLD为移动判定阈值,可根据实际需求调整,数值越大越不容易被鼠标微小抖动触发,数值越小检测灵敏度越高 - 该实现获取的相对位移不受光标位置、屏幕边界、程序锁定光标逻辑影响,只要鼠标物理移动就会正常上报位移增量
内容的提问来源于stack exchange,提问作者EukkMaru
相关产品推荐
相关产品推荐

