窗口销毁消息 WM_DESTROY 的正确处理方式
上次,我提到了可能导致正常的消息循环被破坏的怪异之处。
有一位读者 Adrian 指出,WM_GETMINMAXINFO 消息在顶级窗口 WM_NCCREATE 之前到达。这确实很不幸,但(无论是否错误)十多年来一直如此,现在修改它会引入严重的兼容性风险。
但这不是我想到的怪异之处。
前段时间,我正在帮助调试一个使用 ListView 控件的程序的问题,该问题可追溯到 ListView 控件子类化的程序,并通过复杂的C++对象链,最终尝试销毁 ListView 控件,而它已经在销毁过程中。
让我们以新的例子程序为例,以一种更加明显的方式来说明我所提到的问题。
我在代码中添加了一些调试跟踪,以便更轻松地查看正在发生的事情。运行程序,然后关闭它,并观察会发生什么。
哎呀!发生了什么事?
当您单击窗口右上角的关闭按钮时,这将启动窗口销毁过程。正如预期的那样,窗口收到了一条 WM_DESTROY 消息,但程序通过尝试再次销毁窗口来响应此消息。请注意,IsWindow 报告此时该窗口仍然存在。这是真的:窗口仍然存在,尽管它恰好正在被破坏的过程中。在原始场景中,破坏窗口的代码类似于下面的代码:
if (IsWindow(hwndToDestroy))
{
DestroyWindow(hwndToDestroy);
}
无论如何,对 DestroyWindow 的递归调用导致一个新的窗口销毁周期开始,嵌套在第一个窗口内。这将生成一条新的 WM_DESTROY 消息,后跟一条 WM_NCDESTROY 消息。(请注意,此窗口现在已收到两条 WM_DESTROY 消息!然后,我们的代码对DestroyWindow 进行了另一个递归调用,从而开始了第三个窗口销毁周期。窗口获取其第三条 WM_DESTROY 消息,然后是第二条 WM_NCDESTROY 消息,此时返回对 DestroyWindow 的第二次递归调用。此时,窗口不再存在:DestroyWindow 已销毁窗口。
这就是程序崩溃的原因。基类通过销毁与窗口关联的实例变量来处理 WM_NCDESTROY 消息。因此,当最里面的 DestroyWindow 返回时,实例变量已被丢弃。然后,使用基类的 WM_NCDESTROY 处理程序恢复执行,该处理程序尝试访问实例变量并获取堆垃圾,然后使释放已释放的内存变得更糟,从而损坏堆。正是在这里,我们崩溃了,试图在已经破坏的对象上调用虚拟析构函数。
我有意选择使用新的例子程序(使用C++对象)而不是经典的例子程序(使用全局变量)来强调这样一个事实,即在递归 DestroyWindow 调用之后,所有实例变量都消失了,你正在已经释放的内存上运行代码,这很危险。
故事的寓意:了解你的窗口的生命周期,不要破坏一个你知道已经处于破坏过程中的窗口。
总结
在非托管平台上编写代码如履薄冰,你需要时时谨慎,因为,不再有人能暗中保护你了。
最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《When the normal window destruction messages are thrown for a loop》
相关文章
- 面试官:RocketMQ 如何保证消息不丢失,如何保证消息不被重复消费?
- 用最少的代码模拟gRPC四种消息交换模式
- kafka删除topic消息的四种方式[通俗易懂]
- 进程间通信方式——消息队列
- 微信小程序模板消息接口下线了,不用慌,调用统一服务消息接口来实现相同功能
- vue.js客服系统实时聊天项目开发(十一)处理发送消息enter事件以及实现ctrl+enter换行
- PC端微信聊天工具消息防撤回补丁
- 在线客服系统搜索访客功能-通过访客名称、访客VisitorId、访客IP地址、聊天消息来进行搜索
- Redis 应用实践-消息队列
- Redis 应用实践-消息队列-异步处理
- JavaScript 中创建三种消息框:警告框、确认框、提示框详解编程语言
- 消息称苹果在台积电预订A15处理器总量超1亿颗
- Linux 内核消息两则: 4.5 发布,历史最悠久的 LTS 内核 2.6.32 终止支持
- 监控Redis消息队列实施最佳实践(监听redis消费队列)
- 深入了解Redis消息队列的机制(查看redis消息队列)
- MySQL XA消息演示实现分布式事务的一种方式(mysql xa 演示)
- Redis队列独占方式让消息发送无阻碍(redis队列独占)
- 以Redis为中心 轻松实现PHP消息订阅(redis消息订阅php)
- C#微信公众号开发之接收事件推送与消息排重的方法
- DevExpress实现GridView当无数据行时提示消息