昨天我遇到了我见过的最奇怪的问题。 我写了一个应该得到USB插头通知的模块。 为此,我创建了一个虚拟窗口,并使用一些界面的GUID
将其注册到设备更改通知。PeekMessage触发WndProc回调
当调用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
回调从才会触发内部系统调用!
00007FF954562A80 mov r10,rcx
00007FF954562A83 mov eax,1003h
00007FF954562A88 syscall
我找不到MSDN上的任何文档或在互联网上解释现象。
我真的很喜欢一些帮助, 在此先感谢。
WM_DEVICECHANGE总是发送,绝不会发布。 SendMessage()不会任意中断程序的UI线程,这会导致可怕的重入问题。它需要等待,直到它得到一个线程闲置的信号,并直接调用窗口过程不会引起任何麻烦。 Get/PeekMessage()是那个信号。 – 2015-03-13 13:43:10
@HansPassant非常感谢你:) – CodeNinja 2015-03-13 13:58:50