PeekMessage 触发 WndProc 回调
PeekMessage triggers WndProc callback
昨天我遇到了我见过的最奇怪的问题。
我写了一个模块,应该在 USB 插头上收到通知。
为此,我创建了一个虚拟 window 并使用某些接口的 GUID
.
将其注册到设备更改通知
调用PeekMessage
时出现奇怪的错误。
在这一点上,不知为什么,只有当看到的消息是 WM_DEVICECHANGE
(我们在上面的代码中注册了)时,才会调用 window 的 WndProc 回调。
在任何其他消息上,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
,并遵循 Whosebug 社区成员的建议,我合并了 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
寄存器完全一样!(栈上没有传递参数,64bit..)
在这两种情况下(奇怪的错误和预期的流程)我在NtUserPeekMessage
介入,结果是WndProc
回调仅由内部系统调用触发!
00007FF954562A80 mov r10,rcx
00007FF954562A83 mov eax,1003h
00007FF954562A88 syscall
我在 MSDN 上找不到任何文档或在 Internet 上找到对这种现象的解释。
我真的很想得到一些帮助,
提前致谢。
这符合预期,并且已记录在案。 PeekMessage
是调度已发送消息的函数之一。来自 documentation:
Dispatches incoming sent messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).
然后在同一文档中:
During this call, the system delivers pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function.
SendMessage
的 documentation 是这样说的(我着重强调):
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code.
通过消息检索代码,文档意味着 GetMessage
和 PeekMessage
等功能。还有一些其他的,我手头没有完整的列表。
昨天我遇到了我见过的最奇怪的问题。
我写了一个模块,应该在 USB 插头上收到通知。
为此,我创建了一个虚拟 window 并使用某些接口的 GUID
.
调用PeekMessage
时出现奇怪的错误。
在这一点上,不知为什么,只有当看到的消息是 WM_DEVICECHANGE
(我们在上面的代码中注册了)时,才会调用 window 的 WndProc 回调。
在任何其他消息上,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
,并遵循 Whosebug 社区成员的建议,我合并了 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
寄存器完全一样!(栈上没有传递参数,64bit..)
在这两种情况下(奇怪的错误和预期的流程)我在NtUserPeekMessage
介入,结果是WndProc
回调仅由内部系统调用触发!
00007FF954562A80 mov r10,rcx
00007FF954562A83 mov eax,1003h
00007FF954562A88 syscall
我在 MSDN 上找不到任何文档或在 Internet 上找到对这种现象的解释。
我真的很想得到一些帮助, 提前致谢。
这符合预期,并且已记录在案。 PeekMessage
是调度已发送消息的函数之一。来自 documentation:
Dispatches incoming sent messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).
然后在同一文档中:
During this call, the system delivers pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function.
SendMessage
的 documentation 是这样说的(我着重强调):
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code.
通过消息检索代码,文档意味着 GetMessage
和 PeekMessage
等功能。还有一些其他的,我手头没有完整的列表。