MFC窗口销毁过程

2023-11-14

MFC窗口销毁过程
考虑单窗口情况:

  假设自己通过new创建了一个窗口对象pWnd,然后pWnd->Create。则销毁窗口的调用次序:

  1. 手工调用pWnd->DestroyWindow();

  2. DestroyWindow会发送WM_DESTROY;

  3. WM_DESTROY对应的消息处理函数是OnDestroy();

  4. DestroyWindow会发送WM_NCDESTROY;

  5. WM_NCDESTROY对应的消息处理函数是OnNcDestroy;

  6. OnNcDestroy最后会调用PostNcDestroy;

  7. PostNcDestroy经常被用户重载以提供释放内存操作。例如可以使用delete this;

  通过这种方式,窗口对象对应的窗口和窗口对象本身都被释放了。

  如果含有子窗口:

  如果含有子窗口,则调用父窗口的DestroyWindow时,它会向子窗口发送WM_DESTROY和WM_NCDESTROY消息。

  具体调用顺序参考下文的例子。

   DestroyWindow delete 的影响:

  应该说前者对后者并没有什么影响。但经常在DestroyWindow间接导致执行的PostNcDestroy中delete窗口对象指针,即delete this。

  CView::PostNcDestroy中唯一的操作就是delete this;CframeWnd::PostNcDestory也是如此。而默认的CWnd::PostNcDestroy是空操作,CDialog中也没有对其进行重载,即也是空。

   delete Destroy 的影响:

  delete会导致析构函数。CWnd的析构函数中有对DestroyWindow的调用,但必须保证:

  m_hWnd != NULL &&

  this != (CWnd*) &wndTop &&this != (CWnd*)&wndBottom &&

  this != (CWnd*)&wndTopMost &&this != (CWnd*)&wndNoTopMost。

  Cdialog的析构函数中也有对DestroyWindow的调用,但条件比较松,只需要m_hWnd != NULL。另外Cdialog::DoModal也会调用DestroyWindow。

  CFrameWnd的OnClose中会调用DestroyWindow,但其析构中不会调用DestroyWindow。

  CView的析构也不会调用DestroyWindow。

   一个 SDI 程序的销毁过程

  有CMainFrame类、CMyView类。并且CMyView有两个子窗口CMyDlg和CmyWnd的实例。

  点击退出按钮,CMainFrame会收到WM_CLOSE消息。CframeWnd(CMainFrame的父类)间接会调用CWnd::DestroyWindow;它首先向CMyView发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数;然后向CMyDlg发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数;然后向CMyWnd发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数。

  具体的执行顺序是:

  1. 调用CMainFrame::DestroyWindow

  2. CFrameWnd::OnDestroy

  3. CMyView::OnDestroy

  4. CmyWnd::OnDestroy

  5. CmyDlg::OnDestroy

  6. CmyWnd::PostNcDestroy

  7. CmyWnd的析构

  8. CmyDlg::OnDestroy

  9. CmyDlg的析构

  10. CMyView::PostNcDestroy

  11. CmyView的析构

  12. CMainFrame的析构

  13. CMainFrame::DestroyWindow退出

  上面情况是假设我们在CmyWnd和CmyDlg的PostNcDestroy中添加了delete this。如果没有添加,则7,10不会执行。

  因为CView::PostNcDestroy中调用了delete this,所以然后会执行CMyView的析构操作。因为CframeWnd::PostNcDestroy中调用了delete this,所以最后执行CMainFrame的析构操作。

  如果自己的CmyDlg和CmyWnd在PostNcDestroy中有delete this;则二者会被析构。否则内存泄漏。当然delete也可以放在CMyView的析构中做,只是不够OO。

  总结

  可以有两种方法销毁窗口对象对应的窗口和释放窗口对象指针。一种是通过DestroyWindow。这是比较好的方法,因为最后MFC会自动相应WM_CLOSE导致CframWnd::DestroyWindow被调用,然后会一次释放所有子窗口的句柄。用户需要做的是在PostNcDestroy中释放堆窗口对象指针。但因为某些对象是在栈中申请的,所以delete this可能出错。这就要保证写程序时自己创建的窗口尽量使用堆申请。

  另一种是delete。Delete一个窗口对象指针有的窗口类(如CWnd,Cdialog)会间接调用DestroyWindow,有的窗口类(如CView,CframeWn)不会调用DestroyWindow。所以要小心应对。

  二者是相互调用的,很繁琐。

   一段很好的文章:(作者:闻怡洋)

  一个MFC窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在m_hWnd成员中的HWND(窗口句柄),二是窗口对象本身是一个C++对象。要删除一个MFC窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。

  删除窗口最直接方法是调用CWnd::DestroyWindow或::DestroyWindow,前者封装了后者的功能。前者不仅会调用后者,而且会使成员m_hWnd保存的HWND无效(NULL)。如果DestroyWindow删除的是一个父窗口或拥有者窗口,则该函数会先自动删除所有的子窗口或被拥有者,然后再删除父窗口或拥有者。在一般情况下,在程序中不必直接调用DestroyWindow来删除窗口,因为MFC会自动调用DestroyWindow来删除窗口。例如,当用户退出应用程序时,会产生WM_CLOSE消息,该消息会导致MFC自动调用CWnd::DestroyWindow来删除主框架窗口,当用户在对话框内按了OK或Cancel按钮时,MFC会自动调用CWnd::DestroyWindow来删除对话框及其控件。

  窗口对象本身的删除则根据对象创建方式的不同,分为两种情况。在MFC编程中,会使用大量的窗口对象,有些窗口对象以变量的形式嵌入在别的对象内或以局部变量的形式创建在堆栈上,有些则用new操作符创建在堆中。对于一个以变量形式创建的窗口对象,程序员不必关心它的删除问题,因为该对象的生命期总是有限的,若该对象是某个对象的成员变量,它会随着父对象的消失而消失,若该对象是一个局部变量,那么它会在函数返回时被清除。

  对于一个在堆中动态创建的窗口对象,其生命期却是任意长的。初学者在学习C++编程时,对new操作符的使用往往不太踏实,因为用new在堆中创建对象,就不能忘记用delete删除对象。读者在学习MFC的例程时,可能会产生这样的疑问,为什么有些程序用new创建了一个窗口对象,却未显式的用delete来删除它呢?问题的答案就是有些MFC窗口对象具有自动清除的功能。

  如前面讲述非模态对话框时所提到的,当调用CWnd::DestroyWindow或::DestroyWindow删除一个窗口时,被删除窗口的PostNcDestroy成员函数会被调用。缺省的PostNcDestroy什么也不干,但有些MFC窗口类会覆盖该函数并在新版本的PostNcDestroy中调用delete this来删除对象,从而具有了自动清除的功能。此类窗口对象通常是用new操作符创建在堆中的,但程序员不必操心用delete操作符去删除它们,因为一旦调用DestroyWindow删除窗口,对应的窗口对象也会紧接着被删除。

  不具有自动清除功能的窗口类如下所示。这些窗口对象通常是以变量的形式创建的,无需自动清除功能。

  所有标准的Windows控件类。

  1. 从CWnd类直接派生出来的子窗口对象(如用户定制的控件)。

  2. 切分窗口类CSplitterWnd。

  3. 缺省的控制条类(包括工具条、状态条和对话条)。

  4. 模态对话框类。

  

  具有自动清除功能的窗口类如下所示,这些窗口对象通常是在堆中创建的。

  1. 主框架窗口类(直接或间接从CFrameWnd类派生)。

  2. 视图类(直接或间接从CView类派生)。

  

  读者在设计自己的派生窗口类时,可根据窗口对象的创建方法来决定是否将窗口类设计成可以自动清除的。例如,对于一个非模态对话框来说,其对象是创建在堆中的,因此应该具有自动清除功能。

  综上所述,对于MFC窗口类及其派生类来说,在程序中一般不必显式删除窗口对象。也就是说,既不必调用DestroyWindow来删除窗口对象封装的窗口,也不必显式地用delete操作符来删除窗口对象本身。只要保证非自动清除的窗口对象是以变量的形式创建的,自动清除的窗口对象是在堆中创建的,MFC的运行机制就可以保证窗口对象的彻底删除。

  如果需要手工删除窗口对象,则应该先调用相应的函数(如CWnd::DestroyWindow)删除窗口,然后再删除窗口对象.对于以变量形式创建的窗口对象,窗口对象的删除是框架自动完成的.对于在堆中动态创建了的非自动清除的窗口对象,必须在窗口被删除后,显式地调用delete来删除对象(一般在拥有者或父窗口的析构函数中进行).对于具有自动清除功能的窗口对象,只需调用CWnd::DestroyWindow即可删除窗口和窗口对象。注意,对于在堆中创建的窗口对象,不要在窗口还未关闭的情况下就用delete操作符来删除窗口对象.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MFC窗口销毁过程 的相关文章

  • NfcAdapter.getDefaultAdapter(this) 返回 null 但 NFC 可以工作

    我的应用程序在后台和前台使用 NFC 读取 对于用户信息 我在活动中使用 CountDownTimer 120 1000 5 1000 和方法 onTick long l 每 5 秒检查一次 NFC 状态 有时 在 Android 4 2
  • 当窗口未最大化时缺少 WM_NCLBUTTONUP 消息的奇怪问题

    我有一个处理 WM NCLBUTTONUP 消息的窗口 以便处理标题栏中自定义按钮的点击 当窗口最大化时 这非常有效 但当窗口未最大化时 WM NCLBUTTONUP 消息永远不会到达 不过我确实收到了 WM NCLBUTTONDOWN 消
  • mysql 中的 desc 表说 Null 为 No 但默认为 NULL?

    我在用mysql对于一个项目 当我执行 desc table name 时 我看到表格输出显示了Null该字段的值设置为No 但随后 默认 设置为NULL 一个字段怎么可以没有Null但默认是Null itself mysql gt des
  • 如何在现有 Windows 应用程序中获得 ATL 支持

    我正在 Visual Studio 2012 中使用 Qt 5 3 1 构建一个应用程序 我还想使用一个硬件库 这需要我向项目添加一个简单的 ATL 对象 这可以通过使用 Visual Studio 向导来完成 该向导抱怨我的项目既不是 M
  • 什么时候使用Q_NULLPTR?

    I see Q NULLPTR在 Qt 源代码和示例中被广泛使用 但我没有找到关于它到底是什么以及何时应该使用的文档 例如在这个官方示范 http doc snapshots qt io qt5 5 6 qtserialbus can ma
  • Moshi/Kotlin - 如何将 NULL JSON 字符串序列化为空字符串?

    我正在尝试编写一个空安全字符串适配器来序列化此 JSON nullString null 进入这个 Model nullString 这样我希望是字符串的任何带有 null 值的 JSON 都将被替换为 假设存在这样的数据类 data cl
  • 无法检查 int 是否为 null

    我正在尝试使用字典 每当我想检查字典中是否存在某个元素时 我都会这样做 int value results get aKeyThatMayOrMayNotBePresent if value null 但编译器说我无法比较int to a
  • 如何将命名管道字符串从非托管代码空间发送到托管代码空间?

    我似乎遇到了命名管道 101 问题 我有一个非常简单的设置来连接从 C 非托管应用程序传输到 C 托管应用程序的单工命名管道 管道已连接 但我无法通过管道发送 消息 除非我关闭似乎刷新缓冲区并传递消息的句柄 就好像消息被屏蔽了一样 我尝试颠
  • 如何通过 JOIN 组合(合并)相似的列以删除 NULL

    问题简介 我有一个名为 客户 的超类表和两个从客户继承的名为 个人 和 公司 的子表 因此 客户实体与 人 或 公司 具有一对一的关系 客户只能是 个人 或 公司 但不能同时是两者 如下所示 Customer Person Company
  • 想要将 ColeDateTime 转换为 CTime

    我正在从数据库中读取日期时间ColeDateTime格式 我想将其转换为CTime获取日期 月份 年份和时间 CString repDt this will hold the datetime which i read from Datab
  • 在C语言中,NULL指针和指向0的指针有区别吗?如果是这样,那又怎样?

    在C语言中 NULL指针和指向0的指针有什么区别 ISO IEC 9899 TC2 中规定6 3 2 3 Pointers 3 值为 0 的整型常量表达式 或这样的表达式 强制转换为 void 类型 称为空指针常量 55 如果 null 指
  • _CrtDumpMemoryLeaks( ) == 1 在第一行代码上?

    我正在开发一个 MFC Visual C 项目 据我了解MSDN http msdn microsoft com en us library d41t22sb 28v VS 100 29 aspx CrtDumpMemoryLeaks 应该
  • 实现批量记录获取

    在程序开始时 我需要将数据从 MS Access 数据库 mdb 读取到下拉控件中 这样做是为了每当用户在该控件中键入内容时 应用程序都可以自动完成 不管怎样 从数据库中读取数据花了很长时间 所以我想我应该实现批量行获取 这是我的代码 CS
  • 获取正在运行的程序的属性

    我想开发一个程序 其 ID 是一张牌 因为它在另一个正在运行的程序 例如扑克或红心游戏或其他程序 中播放 我首先尝试获取有关已运行的游戏程序的所需信息 但我从一开始就遇到了问题 我正在运行 MSVC 2013 并开发 MFC 应用程序 现在
  • 在 C# 中追加到空字符串是如何工作的?

    我很惊讶地看到一个字符串被初始化为 null 然后在生产环境中附加一些内容的示例 只是闻起来不对劲 我确信它会抛出空对象异常 但这个大大简化的示例也有效 string sample null sample test sample equal
  • 捕获由纯 C++ dll 中的 MFC 应用程序生成的 Windows 消息

    首先 这可能吗 我有一个与某些硬件接口的第三方 dll 它是用MFC编写的 我 从 dll 供应商处 收到了一个示例 Visual Studio 2010 解决方案 该解决方案只有一个项目 调用相关第三方 dll 的 MFC 应用程序 ex
  • SQL - 用 varchar 替换 is null 整数

    我正在尝试用新的列替换列varchar如果 select 语句中存在空值 则为字符串 personid ISNULL personid no person 我不想更新它 只是在查询结果中将值显示为 无人 但我收到一条错误消息 将 varch
  • 屏幕截图忽略了一些窗口

    我正在 MFC 中工作 我正在尝试捕获桌面的 bmp 我正在使用 GetDC NULL 来执行此操作 但它似乎忽略了特殊的皮肤窗口 它似乎忽略了用 UpdateLayeredWindow 绘制的窗口 此行为似乎仅发生在 Vista x64
  • 调用不通过空指针访问成员的非静态方法是否合法/定义良好的 C++?

    我最近遇到了以下代码 class Foo public void bar other stuff void Foo bar if this do some stuff without accessing any data members r
  • 块执行后变量返回 null

    我正在调度一个队列来在单独的线程上下载一些 flickr 照片 在 viewWillAppear 中 当我记录块内数组的内容时 它完美地显示了所有内容 dispatch queue t photoDowonload dispatch que

随机推荐

  • Python自学笔记2-语法

    这里介绍Python的基本语法和编程风格 Python的保留字 如下表 不能以这些名字给函数或变量命名 and exec not assert finally or break for pass class from print conti
  • UE5 C++插件开发指南目录

    这一篇原本的标题是 如何将插件上架到UE虚幻商城 但是Up主聆枫LingFeng已经分享了相关议题 而且非常详细 UE 虚幻商城上架指南 所以这一篇就改写目录了 其实由谁来讲并不重要 重要的是讲的内容是否是读者需要的 希望大家可以从中受益
  • SQL练习(less-5\8)延时注入

    本文为学习笔记 仅限学习交流 不得利用 从事危害国家或人民安全 荣誉和利益等活动 SQL注入 字符型 延时注入 延时型语句 sleep 参数 任意正整数 一般为秒 If a b c 它的意思就是如果条件A成立 则输出结果B 否则输出结果C
  • HTML 好看界面

    无聊逛外网的时候 突然看见一个用HTML写的界面 我觉得挺好看 对于我这个才接触这个的学生来说 挺厉害的 所以我也把他分享出来 你们可以去参考参考
  • 第50讲:Scrapy 部署不用愁,Scrapyd 的原理和使用

    上节课我们的分布式爬虫部署完成并可以成功运行了 但是有个环节非常烦琐 那就是代码部署 我们设想下面的几个场景 如果采用上传文件的方式部署代码 我们首先需要将代码压缩 然后采用 SFTP 或 FTP 的方式将文件上传到服务器 之后再连接服务器
  • linux磁盘分区以及配置文件设置

    硬盘分区有三种 主磁盘分区 83 扩展磁盘分区 5 逻辑分区 包括swap交换分区82 一个硬盘主分区至少有1个 最多4个 扩展分区可以没有 最多1个 且主分区 扩展分区总共不能超过4个 逻辑分区可以有若干个 交换分区必须存在但一般不用 补
  • hdu 6121 Build a tree

    Problem acm hdu edu cn showproblem php pid 6121 Meaning 一棵 n 个点的完全 k 叉树 结点标号从 0 到 n 1 求以每一棵子树的大小的异或和 Analysis 一层层地统计答案 找
  • LED 数码管共阴共阳的区别+静态/动态显示

    51单片机 数码管动态显示 1 共阴共阳定义 LED 共阴极指的是LED共同的接点是GND 接地 而共阳极指的是LED共同的接点是电源 LED亮灯的条件是两端有电势差 最后一段h dp小数点在高位 第一段a在低位 hgfedcba xxxx
  • 【算法学习笔记】19:拓扑排序

    1 简述 计算拓扑序列的一个方式是 用BFS来尝试访问所有的节点 但是有一个约束就是只有入度为 0 0 0的节点才能被加入到扩展队列里 每次从队列里取出一个节点 也就同时在图中将这个节点拆除 所以它的所有后继的节点都减少 1 1 1 如果已
  • STM32使用串口(空闲中断IDLE+DMA)接收ESP8266数据

    串口空闲中断 ESP8266收发数据 一 在使用ESP8266模块时遇到的一些问题 首先是对模块数据的收发 我们在发送AT指令时会收到模块发送的反馈数据 在我们没有使用实时操作系统的情况下 通过HAL库的串口收发函数是比较难以完成工作的 我
  • aaa计费请求_什么是AAA(身份验证,授权和计费)?

    aaa计费请求 AAA or Authentication Authorization and Accounting is a term used to describe 3 functions in IT Mainly AAA is us
  • 导航样式

    鼠标滑过 bottom 黄线从中间展开到两边
  • NLP: 0基础应用T5模型进行文本翻译代码实例~

    文章目录 前言 一 目标文本是什么 二 模型调用步骤 1 引入库 2 导入模型 本文使用 t5 base 3 使用分词器对目标文本进行分词 4 对刚刚生成的分词结果进行目标语言的生成工作 5 对生成的目标语言进行解码工作 就可得到目标语言的
  • 一文读懂Matter协议的前世今生和未来

    从事Zigbee行业的应该都知道今年Zigbee联盟已经改名为CSA联盟 并推出一个全新的 定位于解决IOT碎片化的统一协议 即Matter协议 Matter协议的由来 Matter协议的前身CHIP Connected Home Over
  • 从一个数组中随机取出若干个数

    随机取数 下面给出从一个数组随机取出若干数字组成新书组和从一个数组随机取出一个数字的方法 代码如下 从一个数组中随机取出若干个元素组成数组 param Array arr 原数组 param Number count 需要随机取得个数 co
  • 如何确保事务提交后才执行异步操作

    参考博客TransactionSynchronizationManager和TransactionSynchronizationAdapter 场景 业务流程背景 对于 法律法规 法规库 标签管理 列表中的某一条数据 操作完标注和解析按钮后
  • Angular离线API文档安装指南

    需要的材料 nginx 官方angularjs zip 完整包 步骤 1 先上www angular org 下载个完整的zip包 2 到nginx 网站下载 nginx 3 修改 nginx 1 6 2 conf nginx conf 文
  • 利用win10自带的工具测硬盘读写速度

    利用win10自带的硬盘测试工具测读写速度 一 win q 打开搜索框 输入 cmd 找到命令提示符 右击以管理员身份运行 二 在命令框里输入 winsat disk 是默认测试系统盘的速度 不出意外都是C盘 三 当我们要想测试其他盘的时候
  • MySQL学习笔记——MySQL数据类型(拉勾教育数据分析实战训练营学习笔记)

    MySQL学习笔记 MySQL数据类型 MySQL数据库中 每一条数据都有其数据类型 主要可以分为数值型 字符串型和日期时间型三大类 说明如下所示 数值类型 TINYINT 一个非常小的整数 占1字节 如果是有符号 范围是 128 127
  • MFC窗口销毁过程

    MFC窗口销毁过程 考虑单窗口情况 假设自己通过new创建了一个窗口对象pWnd 然后pWnd gt Create 则销毁窗口的调用次序 1 手工调用pWnd gt DestroyWindow 2 DestroyWin