MFC 应用程序框架-程序入口和执行流程

第一部分转自木木的家园,在此表示感谢!

一 MFC程序执行过程剖析

1)我们知道在WIN32API程序当中,程序的入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用程序的窗口函数。而在MFC程序当中我们不在能找到类似WinMain这样的程序入口,取而代之的是一系列派生类的声明和定义以及一个冲CWinApp类派生而来的类的全局对象。CWinApp类被称之为应用程序对象,在一个MFC程序当中只允许有一个应用程序对象。由于CWinApp的派生对象是全局的,因此这个对象的构造函数会在所有的其他代码运行之前被调用,而由于CWinApp类当中包含了HWND、HINSTANCE等句柄的存在,其构造函数就执行了对这些成员数据的初始化操作,这里的所谓初始化仅仅是把所有的句柄对象赋值为NULL。
2)在调用完CWinApp的构造函数以后由连接器向程序内自动链接的AfxWinMain函数将被调用,而这个函数可以被看作MFC程序的入口函数。在这个函数当中调用全局AfxGetApp()函数获得应用程序对象,这时将调用AfxInit全局函数,这个函数的功能是使用操作系统传递给AfxWinMain函数的参数初始化应用程序对象当中的相关句柄数据成员。
3)之后AfxWinMain函数调用CWinApp::InitApplication成员函数,这个成员函数用来初始化应用程序对象当中的关于文档部分的内容。
4)随后调用CWinApp::InitInstance成员函数,在这个成员函数当中,使用new操作在堆上声明一个框架窗口对象,由此导致框架窗口对象的构造函数被调用,在框架窗口构造函数当中调用Create函数来创建窗口,而调用的Create函数一般将WNDCLASS参数设置成NULL,这样就由MFC内部调用PreCreateWindow函数,在这个函数当中由MFC注册几个默认的WNDCLASS供框架窗口的Create使用。这时程序控制权交还给CWinApp::InitInstance成员函数内部,由这个函数调用CWnd::ShowWindow显示窗口并且调用CWnd::UpdateWindow向窗口发送WM_PAINT消息。调用完CWinApp::InitInstance成员函数后由AfxWinMain函数调用CWinApp::Run成员函数,并由这个函数来创建和处理消息循环,并且在没有消息的时候处理OnIdle空闲处理。至此整个程序的创建过程完成。
5)在程序的运行过程当中,由操作系统源源不断的发送消息给应用程序,并且由CWinApp::Run当中的消息循环处理并且分发给相关的窗口对象的DefWindowProc成员函数,并由这个成员函数查询窗口对象的消息映射表,如果查到对应项,则由登记在消息映射表当中的类成员函数处理,否则则按照Message Route当中的顺序象父层类发送。
6)在消息运行结束,用户按下关闭按钮后,操作系统向程序发送WM_CLOSE消息,默认状况下程序调用DestoryWindow并且发送WM_DESTORY消息,应用程序接受到这个消息以后的默认操作是调用PostQuitMessage函数,由这个函数发送WM_QUIT消息。当程序对象接受到WM_QUIT消息后消息循环结束,由AfxWinMain函数调用AfxTerm函数清理程序使用过的资源并且结束整个程序。
小结:以上的所有描述涵盖了一个程序从开始、运行到结束的所有过程。 相信大家有点晕点了吧,实际编程中没有必要深刻理解这么多,这些大都是由MFC内部自动帮我们完成的。实际MFC编程过程中,其实懂得MFC程序中各个函数的执行流程即可。有时候过于追究MFC细节会白白浪费我们的精力,应该将主要精力放在使用MFC解决实际问题上。


二 VC6中SDI程序的执行流程

     下面以VC6中的sdi工程为例,通过给每个函数前设置断点后调式执行,可以看出MFC的SDI的执行流程。记录如下,希望对MFC执行有疑惑的人有帮助。

1)CSdiApp theApp;          //sdi.cpp
2)CSdiApp::CSdiApp()             //sdi.cpp
3)BOOL CSdiApp::InitInstance()         //sdi.cpp
4)CSdiDoc::CSdiDoc()                   //sdiDoc.cpp
5)CMainFrame::CMainFrame()          //MainFrm.cpp
6)BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)    //MainFrm.cpp
7)int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)    //MainFrm.cpp
8)CSdiView::CSdiView()                     //sdiView.cpp
9)BOOL CSdiView::PreCreateWindow(CREATESTRUCT& cs)         //sdiView.cpp
10)BOOL CSdiDoc::OnNewDocument()                //sdiDoc.cpp
11) void CSdiView::OnDraw(CDC* pDC)                //sdiView.cpp
// ---------------- 关闭窗口后-------------------------------------
12) CSdiView::~CSdiView()
13) CMainFrame::~CMainFrame()
14) CSdiDoc::~CSdiDoc()


第二部分转自GO GO GO!Fire in the hole!

窗口
    即我们在电脑桌面上看到的应用程序界面,系统为每一个窗口维护了一个WNDCLASS结构体用来描述这个窗口的各种属性(如窗口风格、图标、画刷、菜单和窗口过程等)。系统还会为这个窗口保存它的位置、大小状态以及窗口所属的进程。如果你想查询和操作这些属性,你无法直接操作他们,因为你不知道他们的地址。它们是系统负责维护的。但是系统提供和很多函数让你去操作它们。系统为每个窗口都维护了这些数据结构,你必须指明你要操作哪个窗口。所以窗口句柄呼之欲出了,窗口句柄就是用来标识一个窗口的(以及系统为这个窗口维护的数据结构)。所以Windwos SDK中几乎每一个操作窗口的函数都需要窗口句柄作参数。窗口即是电脑桌面上的界面和系统为这个界面在背后维护的一系列结构体。
 
窗口类
    Windows SDK中创建一个窗口的过程,说到窗口类,还得回顾一下我们在Windows SDK编程时创建一个窗口的过程。设计窗口类(设置各种属性。注意这个窗口类只是一个结构体,不是我们要讨论的窗口类)


注册窗口类
    创建窗口(更加设计的窗口类结构体创建窗口)
    显示窗口
    更新窗口
    消息循环
    窗口过程(在窗口过程中你可以根据收到的消息调用不同的函数,而且每个消息都有默认的处理过程DefWindowProc (hwnd, message, wParam, lParam),DefWindowProc会根据消息调用不同的处理函数。)
    上面过程中,在窗口创建好后我们可以调用各种函数来操作窗口。多半是在窗口过程中操作串口。很多消息都会有默认的函数调用。
MFC用窗口类帮我们把窗口和上面这些繁琐的过程用一个C++类封装起来了,还根据窗口的不同提供了几种默认风格的窗口类。窗口句柄成了窗口类的公开成员变量m_hWnd,对窗口的操作函数成了窗口类的成员方法。还用宏定义把消息和默认的成员方法关联起来。


窗口类中的方法(函数)分类:
    对窗口进行操作的函数:这些函数就是SDK提供的函数的再次封装和升级,只是你再不需要传递窗口句柄给它们们。因为它们可以直接访问类的成员变量m_hWnd。有些是自动调用的,当然都可以显示调用它们。有些还可以重载。消息响应函数:这些函数都有消息映射(貌似VC是用宏实现的)。不同的消息会有不同的消息响应函数。这些响应函数会调用上面的对窗口进行操作的函数。为了让你改变这些消息的默认处理方式。这些函数都设置成了虚函数,你可以重载它们。
总结一下:窗口类就是一个C++类,它把窗口和对窗口的操作函数封装到了一起。
 
窗口类对象:
    就是上面窗口类生成的实例对象。
 
MFC中窗口类对象和窗口之间的区别和联系:
    根据前面的描叙,窗口类对象的一个成员变量m_hWnd是和某一个窗口关联的。m_hWnd是它们的纽带。窗口类对象通过窗口类的构造函数或析构函数创建或销毁。而窗口是通过调用窗口类对象的成员函数Create或CreateEx或DoModa创建的。创建后用m_hWnd标识它。窗口的销毁是通过调用窗口类对象的成员函数DestroyWindow来销毁的。DestroyWindow销毁窗口后窗口类对象依然存在。但是delete销毁窗口对象时,它附带的窗口也会被销毁。
 
MFC窗口的创建过程
    MFC不止封装好了窗口类。而且把窗口的创建、初始化工作以及消息循环也用一个WinApp类封装了起来。WinApp类有一个主要的成员函数InitInstance ()就是用来完成窗口的创建和初始化的。把消息循环机制封装在Run成员函数中。下面是MFC程序的启动步骤。一个MFC程序会定义一个全局的WinApp变量,然后在入口函数中得到这个全局WinApp变量的指针pApp。通过pApp调用WinApp::InitInstance()成员函数完成窗口的创建和初始化。(我们的窗口类就是在这里发挥作用的,WinApp有个窗口类的成员变量。这样WinApp就和窗口关联起来了)。通过pApp调用Run成员函数进入消息循环。到这里就和我们的win32 sdk程序的框架一样全了。看来MFC只是把原来的Win32 SDK框架封装了一下。

你可能感兴趣的:(windows,API,编程,Win32编程,windows内核)