昨天我遇到了我所见过的最奇怪的问题。我编写了一个模块,它应该在USB插件上得到通知。为此,我创建了一个虚拟窗口,并使用某些接口的GUID将其注册到设备更改通知。
调用PeekMessage时会发生奇怪的错误。此时,为什么会调用窗口的WndProc回调,只有当窥视消息是WM_DEVICECHANGE (我们在上面的代码中注册)时才会调用。在任何其他消息上,DispatchMessage都会像预期的那样触发回调。
代码:
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = guid;
not = RegisterDeviceNotification(
hWnd, // events recipient
&NotificationFilter, // type of device
DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
);为了将这个模块与我的其他异步代码结合起来,使用Reactor设计模式和Windows Events,并遵循堆栈溢出社区成员的建议,我合并了MsgWaitForMultipleObjects,以便侦听事件和windows消息。
代码:
for (;;)
{
dwRetval = MsgWaitForMultipleObjects(cntEvents, arrEvents, FALSE, INFINITE, QS_ALLINPUT);
switch (dwRetval)
{
case WAIT_FAILED:
// failed. TODO: status
break;
// TODO: handle abandoned.
default:
if (dwRetval == cntEvents)
{
// Message has popped.
BOOL x = PeekMessage(&tMsg, hWnd, 0, 0, PM_REMOVE); <---- WM_DEVICECHANGE triggers the callback
if (x)
{
TranslateMessage(&tMsg);
DispatchMessage(&tMsg);
}
}
else if (dwRetval < cntEvents)
{
// event signaled
}
else
{
// TODO: status. unexpected.
return FALSE; // unexpected failure
}
break;
}
}我分解了代码,并在调用NtUserPeekMessage之前比较了寄存器
成功呼叫时注册:
RAX = 00000059A604EFB0 RBX = 0000000000000000 RCX = 00000059A604EF18
RDX = 0000000000070C62 RSI = 00000059A604EF18 RDI = 0000000000070C62
R8 = 0000000000000000 R9 = 0000000000000000 R10 = 00007FF71A65D800
R11 = 0000000000000246 R12 = 0000000000000000 R13 = 0000000000000000
R14 = 0000000000000000 R15 = 0000000000000000 RIP = 00007FF954562AA1
RSP = 00000059A604EE70 RBP = 0000000000000000 EFL = 00000200 未知回调触发器调用的寄存器:
RAX = 00000059A604EFB0 RBX = 0000000000000000 RCX = 00000059A604EF18
RDX = 0000000000070C62 RSI = 00000059A604EF18 RDI = 0000000000070C62
R8 = 0000000000000000 R9 = 0000000000000000 R10 = 00007FF71A65D800
R11 = 0000000000000246 R12 = 0000000000000000 R13 = 0000000000000000
R14 = 0000000000000000 R15 = 0000000000000000 RIP = 00007FF954562AA1
RSP = 00000059A604EE70 RBP = 0000000000000000 EFL = 00000200 寄存器完全相同!(堆栈上没有传递参数,64位.)
在这两种情况下(奇怪的错误和预期的流),我在NtUserPeekMessage上进行了一步,原来WndProc回调只从内部syscall触发!
00007FF954562A80 mov r10,rcx
00007FF954562A83 mov eax,1003h
00007FF954562A88 syscall 我在MSDN上找不到任何文件,也无法在互联网上解释这一现象。
我真的很想得到一些帮助,谢谢。
发布于 2015-03-13 13:18:07
这是预期的,并已记录在案。PeekMessage是分发发送消息的函数之一。来自文档
分发传入的发送消息,检查线程消息队列中的已发布消息,并检索消息(如果存在)。
随后在同一份文件中:
在此调用期间,系统传递挂起的、非排队的消息,即使用SendMessage、SendMessageCallback、SendMessageTimeout或SendNotifyMessage函数发送到调用线程拥有的窗口的消息。
文档 for SendMessage这么说(我的重点是):
如果指定的窗口是由调用线程创建的,则将立即作为子例程调用窗口过程。如果指定的窗口是由不同的线程创建的,则系统切换到该线程并调用适当的窗口过程。只有当接收线程执行消息检索代码时,才会处理线程之间发送的消息。
通过消息检索代码,文档意味着像GetMessage和PeekMessage这样的函数。还有几个,我手边没有一个完整的清单。
https://stackoverflow.com/questions/29033219
复制相似问题