1. 应用程序的退出

    一个Windows应用程序启动之后,一般是进入消息循环,等待或者处理用户的输入,直到用户关闭应用程序窗口,退出应用程序为止。

    例如,用户按主窗口的关闭按钮,或者选择执行系统菜单“关闭”,或者从“文件”菜单选择执行“退出”,都会导致主窗口被关闭。

    当用户从“文件”菜单选择执行“退出”时,将发送MFC标准命令消息ID_APP_EXIT。MFC实现了函数CWinApp::OnAppExit()来完成对该命令消息的缺省处理。

    void CWinApp::OnAppExit()

    {

    // same as double-clicking on main window close box

    ASSERT(m_pMainWnd != NULL);

    m_pMainWnd->SendMessage(WM_CLOSE);

    }

    可以看出,其实现是向主窗口发送WM_CLOSE消息。主窗口处理完WM_CLOSE消息之后,关闭窗口,发送WM_QUIT消息,退出消息循环(见图5-3),进而退出整个应用程序。

    1. 边框窗口对WM_CLOSE的处理

MFC提供了函数CFrameWnd::OnClose来处理各类边框窗口的关闭:不仅包括SDI的边框窗口(从CFrameWnd派生),而且包括MDI的主边框窗口(从CMDIFrameWnd派生)或者文档边框窗口(从CMDIChildWnd派生)的关闭。

该函数的原型如下,流程如图6-1所示:

void CFrameWnd::OnClose()

从图6-1中可以看出,它首先判断是否可以关闭窗口(m_lpfnCloseProc是函数指针类型的成员变量,用于打印预览等情况下),然后,根据具体情况进行处理:

  • 如果是主窗口被关闭,则关闭程序的所有文档,销毁所有窗口,退出程序;
  • 如果不是主窗口被关闭,则是文档边框窗口被关闭,又分两种情况:若该窗口所显示的文档被且仅被该窗口显示,则关闭文档和文档窗口并销毁窗口;若该窗口显示的文档还被其他文档边框窗口所显示,则仅仅关闭和销毁文档窗口。

 

下面是处理 WM_CLOSE消息中涉及的一些函数。

  • BOOL CDocument::SaveModified()

该虚拟函数的缺省实现:首先调用IsModifed判断文档是否被修改,没有修改就返回,否则继续。

当询问用户是否保存被修改的文档时,若用户表示“cancel”,返回FALSE;若用户表示“no”,则返回TRUE;若用户表示“yes”,则存盘失败返回FALSE,存盘成功返回TRUE。存盘处理首先要得到被保存文件的名称,然后调用虚拟函数OnSaveDocument完成存盘工作,并使用SetModifidFlag(FALSE)设置文档为没有修改。

  • BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName)

该函数是虚拟函数,用来保存文件。其实现的功能和OpOpenDocument相反,但处理流程类似,描述如下:

    根据lpszPathName打开文件pFile;

    使用pFile构造一个用于写入数据的CArchive对象,此对象用来保存数据到文件;

    设置鼠标为时间瓶形状;

    使用Serialize函数完成序列化写;

    完毕,恢复鼠标的形状。

  • CWinApp::SaveAllModified()

    CWinApp::CloseAllDocuments(BOOL bEndSession)

这两个函数都遍历模板管理器列表,并分别对列表中的模板管理器对象逐个调用CDocManager的同名成员函数:

CDocManager::SaveAllModified()

CDocManager::CloseAllDocuments(BOOL bEndSession)

这两个函数都遍历其文档模板列表,并分别对列表中的模板对象逐个调用CDocTemplate的同名成员函数:

CDocTemplate::SaveAllModified()

CDocTemplate::CloseAllDocuments(BOOL bEndSession)

这两个函数都遍历其文档列表,并分别对列表中的文档对象逐个调用CDocuemnt的成员函数:

CDocument::SaveModified()

CDocument::OnCloseDocument()

  • CDocument::SaveModified()

CDocument::OnCloseDocument()

CDocument::SaveModified前面已作了解释。OnCloseDocument是一个虚拟函数,其流程如图6-2所示。

通过文档对象所对应的视,得到所有显示该文档的边框窗口的指针:在SDI程序关闭窗口时,获取的是主边框窗口;在MDI程序关闭窗口时,获取的是MDI子窗口。

然后,关闭并销毁对应的边框窗口。

如果文档对象的 m_bAutoDelete为真,则销毁文档对象自身。

 

  1. 窗口的销毁过程
    1. DestroyWindow

      从图6-1、图6-2可以看出,销毁窗口是通过调用DestroyWindow来完成的。DestroyWindow是CWnd类的一个虚拟函数。CWnd实现了该函数,而CMDIChildWnd覆盖了该函数。

      (1)CWnd::DestroyWindow()

      主要就是调用::DestroyWindow销毁m_hWnd(必须非空),同时销毁其菜单、定时器,以及完成其他清理工作。

      ::DestroyWindow使将被销毁的窗口失去激活、失去输入焦点,并发送WM_DESTROY、WM_NCDESTROY消息到该窗口及其各级子窗口。如果被销毁的窗口是子窗口且没有设置WM_NOPARENTNOTFIY风格,则给其父窗口发送WM_PARENTNOFITY消息。

      (2)CMDIChildWnd::DestroyWindow()

      因为MDI子窗口不能使用::DestroyWindows来销毁,所以CMdiChildWnd覆盖了该函数,CMDIChildWnd主要是调用成员函数MDIDestroy给客户窗口(父窗口)发送消息WM_MDIDESTROY,让客户窗口来销毁自己。

    2. 处理WM_DESTROY消息

消息处理函数OnDestroy处理WM_DESTROY消息,CFrameWnd、CMDIChildWnd、CWnd、CView及其派生类(如CEditView等等)、CControlBar等都提供了对该消息的处理函数。这里,主要解释边框、文档边框、视类的消息处理函数OnDestroy。

  1. CWnd::OnDestroy()

    调用缺省处理函数Default()。

  2. CFrameWnd::OnDestroy()

    首先,销毁工具栏的窗口;然后,设置菜单为缺省菜单;接着,如果要销毁的是主边框窗口,则通知HELP程序本应用程序将退出,没有其他程序使用WINHELP则关闭WINHELP;最后调用CWnd::OnDestroy。

  3. CMDIFrameWnd::OnDestroy()

    首先,调整客户窗口的边界类型;然后,调用基类CframeWnd的OnDestroy。这时,MDI子窗口的工具栏窗口列表为空,故没有工具栏窗口可以销毁。

  4. CView::OnDestroy()

首先,判断自身是否是边框窗口的活动视,如果是则调用边框窗口的SetActivateView使自己失去激活;然后,调用基类CWnd的OnDestroy。

  1. 处理WM_NCDESTROY消息

窗口的非客户区被销毁时,窗口接收WM_NCDESTROY消息,由OnNcDestroy处理WM_NCDESTROY消息。在MFC中,OnNcDestroy是Windows窗口被销毁时调用的最后一个成员函数。

CWnd、CView的某些派生类提供了对该消息的处理函数,这里只讨论CWnd的实现。

  1. CWnd::OnNcDestroy()

    首先判断当前线程的主窗口是否是该窗口,如果是且模块非DLL,则发送WM_QUIT消息,使得程序结束;

    然后,判断当前线程的活动窗口是否是该窗口,如果是则设置活动窗口为NULL;

    接着,清理Tooltip窗口,调用Default由Windows缺省处理WM_NCDESTROY消息,UNSUBCLASS,把窗口句柄和MFC窗口对象分离(Detach);

    最后,调用虚拟函数PostNcDestoy。

  2. PostNcDestoy

CWnd、CFrameWnd、CView、CControlBar等都覆盖了该函数。文档边框窗口和边框窗口都使用CFrameWnd::PostNcDestroy。

  • CWnd::PostNcDestroy()

MFC缺省实现空

  • void CFrameWnd::PostNcDestroy()

调用delete this销毁自身这个MFC对象。

  • void CView::PostNcDestroy()

调用delete this销毁自身这个MFC对象。

  1. 析构函数

delete this导致析构函数的调用。需要提到的是CFrameWnd和CView的析构函数。

  • CFrameWnd::~CFrameWnd()

边框窗口在创建时,把自身加入到模块-线程状态的边框窗口列表m_frameList中。现在,从列表中移走该窗口对象。

必要的话,删除m_phWndDisable数组。

  • CView::~CView()

在视创建时,把自身加入到文档对象的视列表中。现在,从列表中移走该视对象。

应用程序调用CloseAllDocument关闭文档时。参数为FALSE,它实际上并没有把视从列表中清除,而最后的清除是由析构函数来完成的。

至此,边框窗口关闭的过程讨论完毕。下面,结合具体情况──SDI窗口的关闭、MDI主窗口的关闭、MDI子窗口的关闭──描述对WM_CLOSE消息的处理。

  1. SDI窗口、MDI主、子窗口的关闭

参考图6-1分析SDI窗口、MDI主、子窗口的关闭流程。

  1. SDI窗口的关闭

    在这种情况下,主窗口将被关闭。首先,关闭应用程序的文档对象。文档对象的虚拟函数OnCloseDocument调用时销毁了主窗口(Windows窗口和MFC窗口对象),同时也导致视、工具条窗口的销毁。主窗口销毁后,应用程序的主窗口对象为空,故发送WM_QUIT消息结束程序。

  2. MDI主窗口的关闭

    首先,关闭应用程序的所有文档对象。文档对象的OnCloseDocument函数关闭文档时,将销毁文档对象对应的文档边框窗口和它的视窗口。这样,所有的MDI子窗口(包括其子窗口视)被销毁,但应用程序的主窗口还在。接着,调用DestroyWindow成员函数销毁主窗口自身,DestroyWindow发现被销毁的是应用程序的主窗口,于是发送WM_QUIT消息结束程序。

  3. MDI子窗口(文档边框窗口)的关闭

在这种情况下,被关闭的不是主窗口。判断与该文档边框窗口对应的文档对象是否还被其他一个或者多个文档边框窗口使用,如果是,则仅仅销毁该文档边框窗口(包括其子窗口视);否则,关闭文档,文档对象的OnCloseDocument将销毁该文档边框窗口(包括其子窗口视)。

MFC 教程【6_应用程序的退出 】相关推荐

  1. iOS Sprite Kit教程之编写程序以及Xcode的介绍

    iOS Sprite Kit教程之编写程序以及Xcode的介绍 Xcode界面介绍 一个Xcode项目由很多的文件组成,例如代码文件.资源文件等.Xcode会帮助开发者对这些文件进行管理.所以,Xco ...

  2. MFC 学习笔记(一):MFC单文档程序运行流程梳理与总结

    MFC 学习笔记(一):MFC单文档程序运行流程梳理与总结 1.MFC单文档程序运行流程 1.首先利用全局变量对象 theApp 启动应用程序 (这是因为这个全局对象,基类CWinApp中 this ...

  3. MFC + Access 用户验证程序(初级数据库编程)

    MFC + Access 用户验证程序(初级数据库编程) 转自:企业即时通讯 软件流程: 弹出对话框,要求输入用户及密码,正确则跳到主对话框,错误则要求重新输入,关闭则什么也不做退出. 三无程序,有用 ...

  4. MFC 教程【9_MFC的状态】

    MFC的状态 MFC定义了多种状态信息,这里要介绍的是模块状态.进程状态.线程状态.这些状态可以组合在一起,例如MFC句柄映射就是模块和线程局部有效的,属于模块-线程状态的一部分. 模块状态 这里模块 ...

  5. MFC 教程【7_MFC的DLL 】

    MFC的DLL 一般的,在介绍Windows编程的书中讲述DLL的有关知识较多,而介绍MFC的书则比较少地提到.即使使用MFC来编写动态链接库,对于初步接触DLL的程序员来说,了解DLL的背景知识是必 ...

  6. MFC 教程【5_MFC对象的创建】

    MFC对象的创建 前面几章介绍了MFC的核心概念和思想,即介绍了MFC对Windows对象的封装方法和特点:MFC对象的动态创建.序列化:MFC消息映射机制. 现在,考查MFC的应用程序结构体系,即以 ...

  7. k8s之Pod详解(五)【Kubernetes(K8S) 入门进阶实战完整教程,黑马程序员K8S全套教程(基础+高级)】

    参考于Kubernetes(K8S) 入门进阶实战完整教程,黑马程序员K8S全套教程(基础+高级) Pod Pod的结构 每个Pod中都可以包含一个或者多个容器 这些容器可以分为两类: 用户自定义用的 ...

  8. cview怎么close_6.应用程序的退出

    6.应用程序的退出 一个Windows应用程序启动之后,一般是进入消息循环,等待或者处理用户的输入,直到用户关闭应用程序窗口,退出应用程序为止. 例如,用户按主窗口的关闭按钮,或者选择执行系统菜单&q ...

  9. 应用MFC开发高级应用程序

    目次:一.使用C/C++及VC与VB之比较   二.MFC编程综述  三.使用单文档-多视结构 四.使用DDE服务  五.使用3D控制  六.使用自定义消息 七.使用不带文挡-视结构的MFC应用  八 ...

最新文章

  1. 最新版GMP规范全文
  2. react开发教程(十)redux结合react
  3. linux, configure --prefix=/有什么用
  4. python qq签到_Yii Framework 中文网每天签到 Python 脚本
  5. Mysql索引介绍及常见索引的区别
  6. C++之继承探究(一):继承的概念
  7. 也用C#做个视频监控客户端来玩玩
  8. [见得多了就懂了]食物链
  9. 在GeoServer里设置图层的默认自定义样式,出现不显示预览图的情况(不起作用)...
  10. Unity Shader 中获取屏幕坐标
  11. Producter:让产品从0到1
  12. 第16课:转型的难处
  13. 俏丽教师杂志俏丽教师杂志社俏丽教师编辑部2022年第9期目录
  14. 网络概念与常见问题全解析(网络面试题 学会这篇 基本都能答上来)
  15. 无处不在_电动汽车无处不在。 那丰田为什么还要继续投资氢呢?
  16. Self和self的区别
  17. 荣耀30可以升级鸿蒙系统,惊喜!荣耀手机也能升鸿蒙:这5款机型用户有福了
  18. arduino与语音模块LDV7(LD3320)的串口通信实现简单语音控制
  19. 使用win10自带的手机投屏功能
  20. python爬斗鱼直播_从零开始写Python爬虫 --- 爬虫应用: 利用斗鱼Api抓取弹幕

热门文章

  1. 洛谷——P1590 失踪的7
  2. margin负值的巧妙运用(HTML、CSS)
  3. JBDC操作事务源码解析
  4. mysql获取一行中多列的最大值_SQL 获取一行中多个字段的最大值
  5. python等比例压缩图片_python(PIL)图像处理(等比例压缩、裁剪压缩) 缩略(水印)图详解...
  6. OpenCV3.1.0+VS2013测试程序
  7. sentinel的资料整理
  8. linux ssh 推送文件_通过SSH实现Windows与linux之间传输文件
  9. jQuery原型方法first,last,eq,slice源码分析
  10. 本地仓库的基本操作与概念——Git的学习与使用(三)