MFC 学习笔记(一):MFC单文档程序运行流程梳理与总结
1.MFC单文档程序运行流程
1.首先利用全局变量对象 theApp 启动应用程序
(这是因为这个全局对象,基类CWinApp中 this 的指针才能指向这个对象。)
2.调用全局应用程序对象 theApp 的构造函数
(需要先调用其基类CWinApp的构造函数,
完成应用程序的一些初始化工作,并将应用程序对象的指针保存起来。)
3.进入WinMain函数
(CWinApp的构造函数中调用 AfxWinMain 函数)
4.调用虚函数:InitInstance()
(利用在AfxWinMain函数中可以获取子类的指针,并调用InitInstance函数 如下图)
(③ 指令实际上调用的是子类(CTestApp)的InitInstance函数)
5.子类(CTestApp)中的InitInstance()函数完成应用程序的一些初始化工作
(包括窗口类的注册,创建,窗口的显示和更新。
期间会多次调用CreateEX函数,因为一个单文档MFC应用程序有多个窗口,包括框架窗口.工具条.状态条等。)
初始化举例
例如:
1)CTestApp::Initstance()函数中定义了一个单文档模板对象指针,
2)该对象就把文档类对象、框架类对象、视窗类对象有机地组织在一起,
3)接着利用AddDocTemplate函数把这个单文档模板添加到文档模板中,从而把这个三个类组织成为一个整体,如图:
又如:
1)m_pMainWnd 表示指向程序框架窗口的指针(CMainFrame)
2)通过该指针显示和更新程序框架窗口
等
4.进入消息循环。
上图:pThread->Run()
(虽然也设置了默认的窗口过程函数,但是,MFC应用程序实际上是采用消息映射机制来处理各种消息。当收到WM_QUIT消息时,退出消息循环,程序结束。)
参考博客:
https://blog.csdn.net/c_base_jin/article/details/52345563
PS:
1.Afx前缀的函数代表应用程序框架(Application Framework)函数,属于全局函数,它们可以在程序的任何地方被调用。
2.以域作用符“::”开始的表示的函数,表明该函数是一个全局函数。
2.总结补充
1.由于theApp是全局对象,因此会在进入WinMain()之前完成构造
theApp的构造动作会导致一系列的对theApp的初始化动作:
pModuleState->m_pCurrentWinApp = this;
2.接着进入WinMain()
WinMain()(_tWinMain(),这是为了UniCode而准备的一个宏)直接调用AfxWinMain(),AfxWinMain()发挥真正应该由WinMain()发挥的作用,它取得theApp,然后必须先调用AfxWinInit(),再用取得的指向theApp的指针pApp调用InitApplication()、InitInstance()和Run(),最后调用AfxWinTerm()终止程序。
即AfxWinMain()中的动作相当于:
AfxWinInit();
CWinApp::InitApplication();
CMyWinApp::InitInstance();
CWinApp::Run();
3.AfxWinInit()做什么?
首先将WinMain()传进来的四个参数保留在theApp的成员变量里。
调用AfxInitThread(),此函数又调用::SetWindowHookEx(),不知干啥。另外此函数还将消息队列尽量加大到96。
4.CWinApp::InitApplication()做什么?
会做一些与CDocManager()和Document Template相关的工作,是MFC的内部管理范畴。
5.CMyWinApp::InitInstance()做什么?
终于来到了我们可以改写的InitInstance(),这个函数是CWinApp的虚函数,CMyWinApp通常要改写它,其实在CWinApp中InitInstance()是一个空函数。
InitInstance()的动作如下:
m_pMainWnd = new CMyFrameWnd();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
【(InitInstance()首先会创建一个CMyFrameWnd对象,并将指针保留在m_pMainWnd中。这个CMyFrameWnd对象的创建过程就会导致主窗口的登记和建立,因为CFrameWnd的构造函数中会调用Create()函数,而这个函数负责创建窗口(通过调用CreateWindowEx),窗口风格使用最常见的WS_OVERLAPPEDWINDOW。
但是这仅仅是创建窗口,窗口类注册的动作(必须先于窗口的创建)又发生在哪里呢?
其实Create()的调用会导致CWnd::CreateEx()的调用,此函数才是真正调用CreateWindowEx()创建窗口者,在调用CreateWindowEx()之前会调用CFrameWnd::PreCreateWindow(),此函数判断传入的窗口类名lpszClassName是否为NULL,若是,则通过一系列的调用(非常之绕)为其注册默认窗口类别。由于每个窗口创建过程中PreCreateWindow()都只在创建之前被调用,因此窗口的注册大多在创建之前发生。CWnd及其派生类的各个不同)
PreCreateWindow()函数为不同窗口指定了不同的默认窗口类别:
CWnd使用的窗口类别是_afxWnd
CFrameWnd和CMDIChildWnd使用的窗口类别是_afxWndFrameOrView
CMDIFrameWnd使用的窗口类别是_afxWndMDIFrame】
综上,InitInstance()中的第一个动作――创建CMyFrameWnd对象――完成了窗口的创建。之后的动作就很直观了,ShowWindow()显示窗口,UpdateWindow()发送WM_PAINT消息更新窗口。
6.回到AfxWinMain()中,接下来执行的是CWinApp::Run()。
Run()所做的正是维持程序运行的“消息循环”。CWinApp::Run()会调用CWinThread::Run(),此Run()中有消息循环,通过PumpMessage()得到并转发消息(调用::GetMessage()、::TranslateMessage()、::DispatchMessage(),一如SDK!)。
**但是转发到哪里呢?窗口函数呢?**原来在MFC为我们注册默认窗口类的时候已经指定了窗口函数为DefWindowProc(),但是真正处理消息的又不是它,而是一个全局函数AfxWndProc(),这是MFC通过hook和subclassing技术做到的,暂且不提。
综上,MFC为我们的程序提供了WinMain()、注册了窗口类、创建了窗口甚至提供了窗口函数,留给我们做的只是写出响应消息的处理函数,而这些消息和处理函数如何对应起来还要靠MFC六大关键技术之一的Message Mapping(消息映射)。消息映射机制的目的是首先搭建起消息和消息处理函数对应的大框架,再通过宏的机制让程序员能够方便地添加消息和消息处理函数之间的对应关系。
7.在程序运行的过程中,程序不断地由消息所驱动,直到用户动作发出了WM_CLOSE消息,程序即将关闭
过程是:由于CMyFrameWnd没有设立WM_CLOSE的处理函数,因此该消息被送往预设处理函数,预设函数对WM_CLOSE的处理是调用::DestroyWindow(),进而发送WM_DESTROY消息,WM_DESTROY消息同样会被送到预设处理函数,预设函数对WM_DESTROY的处理方法是调用::PostQuitMessage(),发送WM_QUIT,CWinApp::Run()在收到WM_QUIT后会结束自己的消息循环,并调用ExitInstance(),这是CWinApp的虚函数,可被CMyWinApp改写,调用过此函数后,回到AfxWinMain()中调用AfxWinTerm()结束程序。