winapi - 通过 GetRawInputBuffer 读取 RAWINPUT 时,WM_INPUT_DEVICE_CHANGE 消息会丢失
问题描述
我试图编写与GetRawInputBuffer API 一起正常工作的程序,并在单独的线程上消耗所有输入而几乎没有开销。
但是我发现通过GetRawInputBuffer而不是通常的WM_INPUT方法读取RAWINPUT时, WM_INPUT_DEVICE_CHANGE消息会丢失。
我的代码(我删除了一些错误检查):
void RawInputDeviceManager::RawInputManagerImpl::ThreadRun()
{
// if I set it to true then WM_INPUT_DEVICE_CHANGE does not come
constexpr bool buffered = false;
// prepare buffer for up to 32 raw input messages
m_InputDataBuffer.resize(std::max({ sizeof(RAWKEYBOARD), sizeof(RAWMOUSE), sizeof(RAWHID) }) * 32);
m_WakeUpEvent = ::CreateEventExW(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
WNDCLASSEXW wc{};
wc.cbSize = sizeof(wc);
wc.lpszClassName = L"Message";
wc.lpfnWndProc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT
{
RawInputManagerImpl* manager = reinterpret_cast<RawInputManagerImpl*>(::GetWindowLongPtrW(hWnd, 0));
if (manager)
{
switch (message)
{
case WM_INPUT_DEVICE_CHANGE: // <== get lost after ::GetRawInputBuffer(..) call
{
manager->OnInputDeviceChange();
return 0;
}
case WM_INPUT:
{
manager->OnInput(reinterpret_cast<RAWINPUT*>(lParam));
return 0;
}
}
}
return ::DefWindowProcW(hWnd, message, wParam, lParam);
};
wc.cbWndExtra = sizeof(RawInputManagerImpl*); // add some space for this pointer
wc.hInstance = ::GetModuleHandleW(nullptr);
ATOM classAtom = ::RegisterClassExW(&wc);
HWND hWnd = ::CreateWindowExW(0, reinterpret_cast<LPCWSTR>(classAtom), nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, wc.hInstance, 0);
::SetWindowLongPtrW(hWnd, 0, reinterpret_cast<LONG_PTR>(this));
Register(hWnd);
// enumerate devices before start
OnInputDeviceChange();
// main message loop
while (m_Running)
{
MSG msg;
if (buffered)
OnInputBuffered();
while (true)
{
bool haveMessage = false;
if (buffered)
{
// retrieve any message but WM_INPUT
haveMessage = ::PeekMessageW(&msg, 0, 0, WM_INPUT - 1, PM_REMOVE) ||
::PeekMessageW(&msg, 0, WM_INPUT + 1, 0, PM_REMOVE);
}
else
{
// retrieve any message
haveMessage = ::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE);
}
if (!haveMessage)
break;
// not needed since we are not interested in WM_CHAR or WM_DEADCHAR
//::TranslateMessage(&msg);
// dispatch message to WndProc
::DispatchMessageW(&msg);
}
// wait for new messages
::MsgWaitForMultipleObjectsEx(1, &m_WakeUpEvent, INFINITE, QS_ALLEVENTS, MWMO_INPUTAVAILABLE);
}
Unregister();
::DestroyWindow(hWnd);
::UnregisterClassW(reinterpret_cast<LPCWSTR>(classAtom), wc.hInstance);
}
bool RawInputDeviceManager::RawInputManagerImpl::Register(HWND hWnd)
{
RAWINPUTDEVICE rid[] =
{
// register for all HID device generic types (keyboard/mouse/joystick etc)
{
HID_USAGE_PAGE_GENERIC,
0,
RIDEV_DEVNOTIFY | RIDEV_INPUTSINK | RIDEV_PAGEONLY,
hWnd
}
};
return ::RegisterRawInputDevices(rid, ARRAYSIZE(rid), sizeof(RAWINPUTDEVICE));
}
void RawInputDeviceManager::RawInputManagerImpl::OnInputBuffered()
{
// Processing all pending WM_INPUT messages in message queue
while (true)
{
UINT size = static_cast<UINT>(m_InputDataBuffer.size());
RAWINPUT* input = reinterpret_cast<RAWINPUT*>(m_InputDataBuffer.data());
UINT result = ::GetRawInputBuffer(input, &size, sizeof(RAWINPUTHEADER));
if (result == 0 || result == static_cast<UINT>(-1))
return;
// hack for a undefined QWORD used in NEXTRAWINPUTBLOCK macro
using QWORD = __int64;
for (; result; result--, input = NEXTRAWINPUTBLOCK(input))
{
OnInput(input);
}
}
}
这是Windows中的错误吗?
PS:此WM_INPUT_DEVICE_CHANGE
事件是在 Windows Vista 中添加的,并且已经证明它在其实现中存在一些错误。
PPS:作为一种解决方法,我可以通过RegisterDeviceNotification订阅WM_DEVICECHANGE消息,但我不确定在这种情况下我是否做错了什么。
解决方案
推荐阅读
- mysql - Pentaho 设计器无法识别时间戳数据
- typescript - 如何防止 TypeScript 中的递归条件无限?
- php - 谷歌云存储 - 计算非常大的桶中的对象
- excel - 如何通过Excel中的答案对具有不同分数的测试进行评分?
- azure - 如何限制/停止对 Azure 功能的公共(互联网)访问?
- python - tk 根调整大小事件在窗扇移动时触发
- javascript - 对具有数字作为属性名称的对象使用过滤器功能
- python-3.x - 尝试在 Python 中链接功能时出现帧错误
- python - 输入 repl/无限循环类型
- shader - 为什么 EGL RGBA32 纹理总是有 1.0 英寸的 alpha 通道?