在MFC程序设计的学习过程中最令人感到难受,甚至于有时会动摇学习者信心的就是一种对于程序的一切细节都没有控制权的感觉。这种感觉来源于学习者不知道一个MFC程序是如何运行起来的(即一个MFC程序的执行流程)和MFC程序的设计思想和机制,即使是写过Windows程序的学习者,也会感到非常迷惘并且无从下手。而这种感觉的出现会使大家认为自己离开了书本上的例子就无法设计编制程序。下面我就来说一说一个MFC具体是如何被执行的。在阅读本文之前,你要有一定的Windows程序设计基础,知道Windows程序的运行流程,如不清楚,可先看看我写的这篇文章——解说一个简单的Win32程序。
 
一、单文档项目特点简述
以一个在VS2010中建立的一个单文档MFC程序来例子,深入跟踪MFC执行流程。工程名为MFCSDI,工程建立步骤不在这样详述。
 
当一个SDI(单文档)程序建立之后,我们会看到程序为我们生成了四个类:CAboutDlg、CChildView、CMainFrame、CMFCSDIApp。在它们的头文件中可以看到CCAboutDlg是从CDialogEx类派生出来的,用来显示一个对话框窗口,该对话框用来显示与此程序相关的版本信息。CMainFrame由类CFrameWndEx派生而来,用为表示一个程序的框架。CHildView类由类CWnd派生而来,用于单文档程序的显示。CMFCSDIApp类由类CWinAppEx派生而来,用于表示一个MFC程序,在每一个MFC程序中都有一个C+工程名+App的类(本例子中为CMFCSDIApp),它定义了一个全局对象theApp,它是一个应用程序对象,它就代表着这个程序。一个MFC程序有且只有一个这样的从CWinAppEx派生出来的类,也有且仅有一个从从CWinAppEx派生出来的类(如这里的CMFCSDIApp)所实例化的对象。
 
由此可见,这个MFC单文档程序并不像之前所说的Win32程序那样有一条清晰的主线。一个Windows程序从WinMain函数开始,经过注册窗口类、创建窗口、显示和刷新窗口才使得该程序的窗口界面为用户可见,然后建立进行消息循环,用户对此界面所作的任何操作都会被Windows作为消息传递给程序的窗口函数,并由窗口函数对消息进行分类处理,这些工作都是被 WinMain函数独自包办的。但在MFC程序中WinMain函数的地位被CWinApp类取代了,它所负责的全部初始化工作和对消息解释及分派都有 CWinApp类的内部函数来完成,但是WinMain仍然存在,并且扮演着驾驭CWinApp的角色。但我们在生成的所有文件的代码中,也找不到WinMain函数。而且这几个类之间是通过什么联系起来,组成一个Windows程序的呢?
 
二、在WinMain执行前初始化的全局变量theApp
前面说过,theApp是一个应用程序对象,它就代表着这个程序。一个MFC程序有且只有一个这样的从CWinAppEx派生出来的类,也有且仅有一个从从CWinAppEx派生出来的类(如这里的CMFCSDIApp)所实例化的对象。因为它是一个全局变量,根据C++的特点,它可以在WinMain函数执行前进行自己的初始化。
 
所以,要构造theApp对象就要调用其构造函数,由于CMFCSDIApp的基类为CWinAppEx,CWinAppEx的基类为CWinApp,由于要构造子类,就要先构造父类,即要构造theApp对象就要先调用CWinApp的构造函数来构造父类,CWinApp的构造函数对theApp的一些参数作初始化。
 
三、调用WinMain函数
构造完theApp这个全局对象后,就进入WinMian函数,它的代码在mfc代码所在目录下的appmodul.cpp文件中,这个函数名为_tWinMain,咋一看与我们在Win32所用的WinMain函数的名字不一样,其实_tWinMain是一个宏,到它的定义处一看,就知道它代表的正是WinMain,它的写法与我们在Win32程序中的WinMain函数是一样的。这个_tWinMain会调用一个函数AfxWinMain,这个函数在文件winmain.cpp中定义,而这个函数会有一条语句pThread->InitInstance(),pThread是一个窗口线程的指针,它的值由函数AfxGetThread()所得,根据多态性的原理,pThread会获得一个指向子类的指针,所以它会调用CMFCSDIApp类的成员函数CMFCSDIApp::InitInstance(),这个函数会初始化一些程序运行所需要的资源。
 
四、注册窗口
初始化一些所需的资源之后,就要对窗口类进行注册。MFC会调用函数AfxEndDeferRegisterClass注册窗口类,该函数的定义在文件wincore.cpp中。在Win32中时,我们需要设计一个窗口类,但是MFC已为我们设计好一个默认的窗口类,这里我们进行注册就行。
 
五、产生窗口
注册完窗口类之后,就会调用CMainFrm类的成员函数CMainFrm::PreCreateWindow来创建窗口,而这个函数会去调用它的父类的成员函数CFrameWnd::PreCreateWindow来创建窗口,在这个函数中可以对一些MFC设计好的默认的窗口类作一些修改,然后会调用AfxEndDeferRegisterClass函数进行窗口类的注册。之后,就会调用CFrameWnd::Create函数进行窗口的创建,该函数的定义在文件winfrm.cpp中,而该在窗口创建过程中该函数又会调用CWnd::CreateEx函数来对窗口进行创建,CWnd::CreateEx定义在文件wincore.cpp中。窗口创建完成后,会调用ShowWindow函数和UpdateWindow函数显示窗口,这两个函数在函数CMFCSDIApp::InitInstance()中被调用(在文件MFCSDI.cpp中)。
 
在一个程序中可以见到PreCreateWindow会被调用很多次,这是因为我们产生一个程序时会注册很多个窗口,如工具栏,按钮等,每创建一个窗口都要调用AfxEndDeferRegisterClass函数来进行窗口类注册,所以这两个函数就被多次地调用了。
 
六、建立消息循环
回到一开始所说的AfxWinMain函数中,里面有一条语句nReturnCode = pThread->Run();其实这就是建立消息循环。在文件thrdcore.cpp中可以找到它的定义(CWinThread::Run),它会循环调用函数PumpMessage(同样定义在文件thrdcore.cpp中),PumpMessage函数又会调用函数AfxInternalPumpMessage(在文件thrdcore.cpp中),它会调用函数GetMessage,它就等同于我们Win32程序中的函数GetMessage,然后调用函数::TranslateMessage,::DispatchMessage这与我们所写的Win32程序是一致的。
 
七、窗口过程
MFC在窗口类注册时就给它指定了一个默认的窗口过程,在函数AfxEndDeferRegisterClass(wincore.cpp)中有如下语句,wndcls.lpfnWndProc = DefWindowProc;把窗口过程指定为默认的窗口过程,而MFC则会通过消息映射转换处理过程,使我们的程序能响应不同的消息。
 
八、窗口的销毁
MFC程序的死亡相对于初生来说要简单的多,主要是以下几步:
  1.使用者通过点击File/Close或程序窗口由上角的叉号发出WM_CLOSE消息。
  2.程序没有设置WM_CLOSE处理程序,交给默认处理程序。
  3.默认处理函数对于WM_CLOSE的处理方式为调用::DestoryWindow,并因而发出WM_DESTORY消息。
  4.默认的WM_DESTORY处理方式为调用::PostQuitMessage,发出WM_QUIT。
  5.CWinApp::Run收到WM_QUIT后结束内部消息循环,并调用ExinInstance函数,它是CWinApp的一个虚拟函数,可以由用户重载。
  6.最后回到AfxWinMain,执行AfxWinTerm,结束程序。
 
本文中,我用到的文件的路径如下,写出来给大家参考一下,大家可以找到这些函数,设置断点,调试运行看一看程序的具体执行流程。
 
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\appmodul.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\winmain.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\winfrm.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\appcore.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\wincore.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\thrdcore.cpp

本文来源于编程启航吧
原文地址:http://www.prm8.com/a/bianchengjingyan/vc/1346/

vc++深入跟踪MFC程序的执行流程的更多相关文章

  1. 深入跟踪MFC程序的执行流程

    来源: http://blog.csdn.net/ljianhui/article/details/8781991 在MFC程序设计的学习过程中最令人感到难受,甚至于有时会动摇学习者信心的就是一种对于 ...

  2. Android开发第一讲之目录结构和程序的执行流程

    1.如何在eclipse当中,修改字体 下面的这种办法,可以更改xml的字体 窗口--首选项--常规--外观--颜色和字体--基本--文本字体--编辑Window --> Preferences ...

  3. 3-1Java程序的执行流程

    3-1Java程序的执行流程 用记事本写一个简单的程序 存到:E:\java路径下 class HelloImooc{    public static void main(String[] agrg ...

  4. 003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程

    003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程 Java程序长啥样? 首先编写一个Java程序 记事本编写程序 打开记事本 1.wi ...

  5. Spring Boot程序的执行流程

    Spring Boot的执行流程如下图所示:(图片来源于网络) 上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分,第一部分进行SpringApplication的初始化模块,配置 ...

  6. 微信小程序开发01-小程序的执行流程是怎么样的?

    前言 我们这边最近一直在做基础服务,这一切都是为了完善技术体系,这里对于前端来说便是我们需要做一个Hybrid体系,如果做App,React Native也是不错的选择,但是一定要有完善的分层: ① ...

  7. 微信小程序的开发——01小程序的执行流程是怎样的?

    作者:叶小钗 转载至:https://www.cnblogs.com/yexiaochai/p/9346043.html 我们这边最近一直在做基础服务,这一切都是为了完善技术体系,这里对于前端来说便是 ...

  8. MFC 程序的运行流程

    CWinApp::InitApplication CMyWinApp::InitInstance CMyFrameWnd::CMyFrameWnd CFrameWnd::Create CWnd::Cr ...

  9. 【转载】MFC 程序入口和执行流程

    原文链接: http://www.cnblogs.com/liuweilinlin/archive/2012/08/16/2643272.html 一 MFC程序执行过程剖析 1)我们知道在WIN32 ...

随机推荐

  1. IBM Minus One(water)

    IBM Minus One Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  2. 编译安装mysql5.7.9

    第一步:安装一些可能会用到的依赖 yum -y install gcc-c++ ncurses-devel cmake make perl gcc autoconf automake zlib lib ...

  3. Git学习03 --远程仓库

    把本地库的内容推送到远程(github), 用git push命令,实际上是把当前分支master推送到远程. 由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的 ...

  4. Nginx 变量漫谈(一)

    Nginx 的配置文件使用的就是一门微型的编程语言,许多真实世界里的 Nginx 配置文件其实就是一个一个的小程序.当然,是不是“图灵完全的”暂且不论,至少据我观察,它在设计上受 Perl 和 Bou ...

  5. 阿里云 centos 修改iptables

    一.检查iptables服务状态 首先检查iptables服务的状态 [root@woxplife ~]# service iptables status iptables: Firewall is ...

  6. Inno Setup 创建站点,创建虚拟目录

    原文 http://hi.baidu.com/0531_sunmiles/item/ce22554ab7d33d0be9350477 下面的这段代码是用Inno Setup 做安装包的时候创建IIS新 ...

  7. 切图教程,APP切图实例

  8. hdr_beg(host) 主机名开始

    What does "acl cdn_name hdr_beg(host) -i foor.bar.com" mean in HAProxy's configuration? 在H ...

  9. Tengine笔记1:安装Tengine和Tengine说明

    什么是Tengine 官方帮助文档:http://tengine.taobao.org/nginx_docs/cn/   Tengine的安装   新建tengine用户组 groupadd -r n ...

  10. Zipper

      Zipper Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...