问题描述
我尝试编写与 GetRawInputBuffer API 一起正常工作的程序,并在单独的线程上以很少的开销消耗所有输入。
但我发现通过 WM_INPUT_DEVICE_CHANGE 而不是通常的 RAWINPUT 方法阅读 GetRawInputBuffer 时 WM_INPUT 消息会丢失。
My code(我删除了一些错误检查):
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,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),HWND_MESSAGE,wc.hInstance,0);
::SetWindowLongPtrW(hWnd,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,WM_INPUT - 1,PM_REMOVE) ||
::PeekMessageW(&msg,WM_INPUT + 1,PM_REMOVE);
}
else
{
// retrieve any message
haveMessage = ::PeekMessageW(&msg,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,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 中,并已证明它has some bugs in its implementation。
PPS:作为一种解决方法,我可以通过 WM_DEVICECHANGE 订阅 RegisterDeviceNotification 消息,但我不确定在这种情况下我是否做错了什么。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)