程序后台运行时如何接收WM_INPUT消息获取鼠标原始输入数据
解决方法
你现有代码存在三个核心问题,修改后即可实现后台全局接收鼠标原始输入:
- 没有创建属于自己程序的消息窗口,WM_INPUT消息没有投递目标
- 注册RAWINPUT设备时未开启后台接收标志,也未绑定目标窗口
- 主线程死循环打印前台窗口,没有运行Windows消息队列,无法接收分发消息
修改后的完整实现代码
#include <windows.h> #include <iostream> #include <memory> // 窗口过程声明 LRESULT CALLBACK targetWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int main() { // 1. 注册窗口类,创建隐藏消息窗口 const wchar_t CLASS_NAME[] = L"RawInputHiddenWindowClass"; WNDCLASS wc = {0}; wc.lpfnWndProc = targetWindowProc; wc.hInstance = GetModuleHandle(NULL); wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); // 创建隐藏窗口,不需要显示出来 HWND hWnd = CreateWindowEx( WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE, // 扩展样式:不在任务栏显示、不激活 CLASS_NAME, L"RawInputMonitor", WS_POPUP, // 无标题栏样式 0, 0, 0, 0, // 大小位置都设为0 NULL, NULL, GetModuleHandle(NULL), NULL ); if (hWnd == NULL) { exit(-1); } ShowWindow(hWnd, SW_HIDE); // 隐藏窗口 // 2. 注册Raw Input设备,开启后台接收 RAWINPUTDEVICE rid; rid.usUsagePage = 0x01; // HID通用桌面页 rid.usUsage = 0x02; // 鼠标设备 // RIDEV_INPUTSINK标志:即使窗口在后台也能收到输入 rid.dwFlags = RIDEV_INPUTSINK; rid.hwndTarget = hWnd; // 绑定到我们创建的隐藏窗口 if (RegisterRawInputDevices(&rid, 1, sizeof(rid)) == FALSE) { exit(-1); } // 3. 替换原有死循环为Windows消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); // 如果你还需要打印前台窗口,可以加在这里 HWND foregroundWnd = GetForegroundWindow(); std::cout << "当前前台窗口句柄:" << foregroundWnd << '\n'; } return 0; } LRESULT CALLBACK targetWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INPUT: { UINT dataSize; GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, NULL, &dataSize, sizeof(RAWINPUTHEADER)); // 可以通过这个判断是前台还是后台收到的输入 int inputCode = GET_RAWINPUT_CODE_WPARAM(wParam); if (inputCode == RIM_INPUTSINK) { std::cout << "[后台收到输入] "; } else if (inputCode == RIM_INPUT) { std::cout << "[前台收到输入] "; } if (dataSize > 0) { std::unique_ptr<BYTE[]> rawdata = std::make_unique<BYTE[]>(dataSize); if (GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, rawdata.get(), &dataSize, sizeof(RAWINPUTHEADER)) == dataSize) { RAWINPUT* raw = reinterpret_cast<RAWINPUT*>(rawdata.get()); if (raw->header.dwType == RIM_TYPEMOUSE) { std::cout << "X偏移:" << raw->data.mouse.lLastX << " Y偏移:" << raw->data.mouse.lLastY << std::endl; } } } return 0; } case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } }
关键修改说明
RIDEV_INPUTSINK是实现后台接收的核心参数,只要注册设备时加上这个标志并绑定目标窗口,不管窗口是否处于前台,都能收到所有全局的鼠标原始输入消息- 必须运行标准Windows消息循环才能收到WM_INPUT消息,原代码的死循环会阻塞消息队列,导致窗口过程永远无法被调用
- 隐藏窗口只用来接收消息,不会显示到桌面或任务栏,完全不影响前台操作
内容的提问来源于stack exchange,提问作者Lenny
相关产品推荐
相关产品推荐

