我希望使用WM_INPUT消息使用Raw Input API处理游戏中的鼠标单击。 我只对我的应用程序处于活动状态时的鼠标单击感兴趣。
当窗口处于非活动状态并且单击进入焦点窗口时,不会为该点击生成WM_INPUT消息。我可以处理其他所有单击,但可以使窗口聚焦的单击除外。
使用RIDEV_INPUTSINK标志,我可以捕获到一个丢失的事件,但是当我的应用程序处于非活动状态时,我将不得不忽略我收到的所有其他消息。
大多数应用程序和窗口程序都像正常操作一样处理“激活单击”,但它们不会忽略它。他们可能会在正确到达时使用“旧版”消息。
我应该将原始输入与传统鼠标处理混合在一起吗?我应该使用INPUTSINK标志并选择性地忽略消息吗?如果可以,怎么办?
由于Microsoft建议为游戏提供原始输入,所以我希望有一个通用的解决方案,但到目前为止我还找不到。
相关代码段
注册原始输入设备
RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x02;
Rid[0].dwFlags = 0; // adds HID mouse
Rid[0].hwndTarget = 0;
RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]))
为方便起见,这里有一个完整的示例程序来展示我的问题。
#include <windows.h>
#include <strsafe.h>
const char g_szClassName[] = "myWindowClass";
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_INPUT:
{
UINT dwSize;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize,
sizeof(RAWINPUTHEADER));
LPBYTE lpb = new BYTE[dwSize];
if (lpb == NULL)
{
return 0;
}
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize,
sizeof(RAWINPUTHEADER)) != dwSize)
OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
RAWINPUT* raw = (RAWINPUT*)lpb;
size_t const cchDest = 1000;
TCHAR pszDest[cchDest];
if (raw->header.dwType == RIM_TYPEMOUSE && raw->data.mouse.ulButtons)
{
auto hResult = StringCchPrintf(pszDest, STRSAFE_MAX_CCH, TEXT("Mouse: usFlags=%04x ulButtons=%04x usButtonFlags=%04x usButtonData=%04x ulRawButtons=%04x lLastX=%04x lLastY=%04x ulExtraInformation=%04x\r\n"),
raw->data.mouse.usFlags,
raw->data.mouse.ulButtons,
raw->data.mouse.usButtonFlags,
raw->data.mouse.usButtonData,
raw->data.mouse.ulRawButtons,
raw->data.mouse.lLastX,
raw->data.mouse.lLastY,
raw->data.mouse.ulExtraInformation);
if (FAILED(hResult))
{
// TODO: write error handler
}
OutputDebugString(pszDest);
}
delete[] lpb;
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x02;
Rid[0].dwFlags = 0; // adds HID mouse
Rid[0].hwndTarget = 0;
if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
//registration failed. Call GetLastError for the cause of the error
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
更新
目前,我发现了将旧输入和原始输入混合使用的解决方法。在WM_ACTIVATE消息中,我检查wParam是否为2。如果是,则表明该窗口是通过单击鼠标激活的。
在这种情况下,我实际上处理了下一个WM_LBUTTONDOWN,但是只有一次(还有其他按钮)。
这是一个可怕的骇客,所以我仍在寻找合适的解决方案。