Windows截获系统鼠标消息转发到指定窗口
注册输入设备,截获系统底层的设备消息
先看关键代码:
RAWINPUTDEVICE rids[1];
rids[0].usUsagePage = HID_USAGE_PAGE_GENERIC; //0x01
rids[0].usUsage = HID_USAGE_GENERIC_MOUSE; //0x02
rids[0].dwFlags = RIDEV_INPUTSINK; //0x00000100
rids[0].hwndTarget = win->hwnd;
RegisterRawInputDevices(rids, 1, sizeof(rids[0]));
这段代码中,RegisterRawInputDevices 方法用于让程序注册一个或多个输入设备(代码中仅注册了一个),以便应用程序可以直接接收来自这些设备的原始输入数据。
参数RAWINPUTDEVICE的属性usUsagePage的值为HID_USAGE_PAGE_GENERIC,表示通用输入设备。
dwFlags属性的值为HID_USAGE_GENERIC_MOUSE,表示鼠标设备。
dwFlags属性的值为RIDEV_INPUTSINK ,表示原始输入设备的输入将发送到指定的目标窗口,而不是默认的窗口。这意味着即使目标窗口没有被激活,它仍然可以接收输入。
hwndTarget属性的值为目标窗口的句柄。
处理截获到的消息
如下代码所示:
LRESULT CALLBACK processMsg(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg != WM_INPUT) {
return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
UINT dwSize = sizeof(RAWINPUT);
static BYTE lpb[sizeof(RAWINPUT)];
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
auto raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE){
RAWMOUSE rawMouse = raw->data.mouse;
if (rawMouse.usButtonFlags == RI_MOUSE_WHEEL)
{
//鼠标滚轮消息
return 0;
}
switch (rawMouse.ulButtons)
{
case RI_MOUSE_LEFT_BUTTON_DOWN:
{
//鼠标左键按下消息
return 0;
}
case RI_MOUSE_LEFT_BUTTON_UP:
{
//鼠标左键弹起消息;
return 0;
}
default:
{
//鼠标移动消息
return 0;
}
}
}
return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
截获到的消息都是 WM_INPUT 消息,其他消息我们就交由窗口的原有消息处理函数处理了。
截获到的消息数据存储在lParam中,我们通过GetRawInputData方法把这些数据提取到一个RAWINPUT类型的对象中。
由于我们只截获了鼠标消息,所以又从RAWINPUT对象中提取了RAWMOUSE类型的对象,我们只处理鼠标消息。
当这个RAWMOUSE对象的usButtonFlags属性值为RI_MOUSE_WHEEL时,表示鼠标滚轮消息。
这个对象的ulButtons属性的值代表着鼠标消息的其他类型,比如鼠标左键按下:RI_MOUSE_LEFT_BUTTON_DOWN,鼠标左键弹起:RI_MOUSE_LEFT_BUTTON_UP等。
如果窗口是你自己的,此时你就可以执行具体的方法了,如果窗口不是你自己的,你还可以把这些消息发送给目标窗口。如下代码所示:
auto lParam = MAKELPARAM(point.x, point.y);
PostMessage(win->hwnd, WM_LBUTTONUP, MK_LBUTTON, lParam);
当然,鼠标消息要注意处理鼠标的坐标呦(你希望得到绝对坐标还是相对坐标呢?)