深圳幻海软件技术有限公司 欢迎您!

使用消息过滤器找回丢失的线程消息

2023-02-28

​线程消息在模态循环中会丢失,因为消息分发器(MessageDispatcher)不知道应该如何分发此消息。但是,如果模态循环能支持的话,我们有一种方法可以在它们消失之前看到它们。WH_MSGFILTER消息钩子可以用来接收传递给CallMsgFilter函数的消息。幸运的是,窗口管理器中的所有模态

​线程消息在模态循环中会丢失,因为消息分发器(Message Dispatcher)不知道应该如何分发此消息。但是,如果模态循环能支持的话,我们有一种方法可以在它们消失之前看到它们。

WH_MSGFILTER 消息钩子可以用来接收传递给 CallMsgFilter 函数的消息。幸运的是,窗口管理器中的所有模态循环都使用 CallMsgFilter 来允许线程在线程消息丢失之前捕获它们。 因此,这为我们提供了一种方法,可以在消息通过模态循环时对它们进行监控。

让我们在上次编写的程序中添加一个消息过滤器,看看消息是如何通过消息过滤器的。但是,请注意,这是不是解决之前问题的正确方法。 我们在上一篇文章中说明了正确的解决方法。我用错误的方式来说明消息过滤器,主要是因为它没有被开发人员很好地理解。 (例如,消息过滤器的正当理由是,阻止菜单循环看到某些输入消息。)

从上一个程序开始,在我们将 PostThreadMessage 更改为 PostMessage 之前,然后进行以下更改:

在这里,我们在线程上安装了一个消息过滤器钩子,以便我们可以在消息通过模态循环时显示它们。 code 参数告诉我们什么类型的模态循环检索到了消息; 我们在这里忽略它,因为我们想对所有模态循环进行过滤。

运行这个程序并观察蜂鸣器声不再丢失,因为我们的消息过滤器有机会看到它们并对它们做出反应。

消息过滤器技巧依赖于所有模态循环,它们在分发它们之前,通过消息过滤器发送它们检索到的消息。 如果你正在编写要进入库的代码,并且你有一个模态循环,那么你也应该在分发消息之前调用消息过滤器,以防你的代码库的使用者想要对消息做一些事情。

MSGF_MYLIBRARY 可以是一个任意值,你可以选择并记录在库的头文件中。 在 commctrl.h 头文件中,我们会看到这样的示例代码:

上面这些是由外壳(Shell)公共控件库中的模式循环调用的消息过滤器。

你可能会问一个问题,“为什么使用消息过滤器挂钩而不是 GetMessage 挂钩?”

消息过滤器钩子比 GetMessage 钩子占用资源更少,因为它们仅在请求时调用,与 GetMessage 钩子相反,GetMessage 钩子为每个检索到的消息调用。 消息过滤器钩子还会告诉你哪个模态循环正在执行过滤,这样就可以调整相应的动作。

消息过滤器钩子的缺点是所有模式循环都需要记住调用 CallMsgFilter,将其作为其调度循环的一部分。但这个,应该问题不大,一句代码的事儿。

总结

早在研究 VNC 代码的那个年代,我就知道 有Windows 消息钩子这回事儿了。奈何,因为当时水平不济,一直没能透彻理解它并真正地写出一些代码,也就搁置了。通过今天的文章,我还是没太能完全理解它,但是,这有助于提醒我:Windows 基础设施中有很多需要我去发现的瑰宝。