首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PeekMessage触发WndProc回调

PeekMessage触发WndProc回调
EN

Stack Overflow用户
提问于 2015-03-13 13:16:47
回答 1查看 1.2K关注 0票数 5

昨天我遇到了我所见过的最奇怪的问题。我编写了一个模块,它应该在USB插件上得到通知。为此,我创建了一个虚拟窗口,并使用某些接口的GUID将其注册到设备更改通知。

调用PeekMessage时会发生奇怪的错误。此时,为什么会调用窗口的WndProc回调,只有当窥视消息是WM_DEVICECHANGE (我们在上面的代码中注册)时才会调用。在任何其他消息上,DispatchMessage都会像预期的那样触发回调。

代码:

代码语言:javascript
复制
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消息。

代码:

代码语言:javascript
复制
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之前比较了寄存器

成功呼叫时注册:

代码语言:javascript
复制
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 

未知回调触发器调用的寄存器:

代码语言:javascript
复制
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触发!

代码语言:javascript
复制
00007FF954562A80  mov         r10,rcx  
00007FF954562A83  mov         eax,1003h  
00007FF954562A88  syscall  

我在MSDN上找不到任何文件,也无法在互联网上解释这一现象。

我真的很想得到一些帮助,谢谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-03-13 13:18:07

这是预期的,并已记录在案。PeekMessage是分发发送消息的函数之一。来自文档

分发传入的发送消息,检查线程消息队列中的已发布消息,并检索消息(如果存在)。

随后在同一份文件中:

在此调用期间,系统传递挂起的、非排队的消息,即使用SendMessage、SendMessageCallback、SendMessageTimeout或SendNotifyMessage函数发送到调用线程拥有的窗口的消息。

文档 for SendMessage这么说(我的重点是):

如果指定的窗口是由调用线程创建的,则将立即作为子例程调用窗口过程。如果指定的窗口是由不同的线程创建的,则系统切换到该线程并调用适当的窗口过程。只有当接收线程执行消息检索代码时,才会处理线程之间发送的消息。

通过消息检索代码,文档意味着像GetMessagePeekMessage这样的函数。还有几个,我手边没有一个完整的清单。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29033219

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档