MFC启动过程&单文档MFC程序的启动详细过程

来源:互联网 发布:软件测试行业前景 编辑:程序博客网 时间:2024/05/22 08:51

MFC启动过程

1. 首先说一下MFC程序的启动过程.

每个MFC程序都有一个全局的应用程序类的对象, 在面向对象程度非常好的MFC程序中, 应该只有这一个全局的对象.

MFC应用程序启动时, 首先创建这个应用程序对象, 比如对象名为theApp, 这时将调用这个对象的构造函数来初始化theApp.然后由应用程序框架调用MFC提供的AfxWinMain主函数. 在这个主函数中, 首先获得应用程序对象theApp的指针, 然后通过这个指针调用程序程序对象的有关函数, 来完成程序的初始化和启动工作, 最后调用Run函数, 进入消息循环. 主要代码如下:

CTheApp theApp;BOOL CTheApp::InitInstance(){         ....         m_pMainWnd = new CTheWindow();//调用窗口类的构造函数来创建一个窗口         m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口         m_pMainWnd->UpdateWindow();//更新窗口上的元素         return TRUE;}//////////////int AFXAPI AfxWinMain(){         CWinThread *pThread = AfxGetThread();//获取主线程指针         CWinApp *pApp = AfxGetApp();         AfxWinInit();         ....         pApp->InitApplication();         ...         pThread->InitInstance();//初始化应用程序实例         ...         nReturnCode = pThread->Run();//开始消息循环}

与普通的WIN32 SDK应用程序相比, 入口函数换成AfxWinMain, 初始化和启动工作主要交给应用程序类处理. 另外, 创建管理窗口由窗口类负责, 创建创建一个窗口只需要几行便可实现, 完全不用像WIN32 SDK那样用大量代码去注册窗口类, 而且由于消息映像机制的引进, 不用再去写麻烦的回调函数和SWITCH-CASE语句. 当然, MFC的强大远远不只这些, 比如能够方便的实现文档/视图结构和序列化就是它最强大的一个功能之一.
另外, 在AfxWinMain中可以看到, 运行代码和消息循环果然是由主线程来完成的. 此外, 可以看到主线程完成了界面管理(如窗口创建)和消息循环(Run), 这与线程的使用中所给的指导原则完全一致. 


2. 再看一个最简单的MFC程序的代码. 这个程序包含一个.H文件和一个.CPP文件, 代码如下:


//HELLO.H:class CTheApp : public CwinApp//应用程序类定义{public:    virtual BOOL InitInstance ();};class CMainWindow : public CframeWnd//主窗口类定义{public:    CMainWindow ();protected:    afx_msg void OnPaint ();    DECLARE_MESSAGE_MAP ()};//HELLO.CPP#include <afxwin.h>#include "Hello.h"CTheApp theApp;//全局应用程序对象/////////////////////////////////////////////////////////////////////////// CMyApp member functionsBOOL CTheApp::InitInstance ()//初始化应用应用程序实例: 创建并显示一个窗口{    m_pMainWnd = new CMainWindow;    m_pMainWnd->ShowWindow (SW_SHOW);    m_pMainWnd->UpdateWindow ();    return TRUE;}/////////////////////////////////////////////////////////////////////////// CMainWindow message map and member functionsBEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)//消息映射    ON_WM_PAINT ()END_MESSAGE_MAP ()CMainWindow::CMainWindow ()//构造一个窗口{    Create (NULL, _T ("Hello MFC!"));}void CMainWindow::OnPaint ()//重绘窗口内容{    CPaintDC dc (this);    CRect rect;    GetClientRect (&rect);    dc.DrawText (_T ("Hello, MFC"), -1, &rect,        DT_SINGLELINE | DT_CENTER | DT_VCENTER);}

在上面的代码中可以看到一个最简单的MFC应用程序的组成和结构:
首先, 应该声明一个应用程序类来负责程序的初始化和启动, 并且需要一个全局的应用程序类的对象, 它将被AfxWinMain使用来初始化应用程序. 此外, 应用程序类的InitInstance虚函数是必须要重载的函数, 要在其中放置实际的初始化代码, 比如载入一些程序设置选项, 创建一个窗口等.
第二, 应该声明一个主窗口类负责创建和管理主窗口及主窗口中的元素. 实际创建主窗口的代码应该在主窗口的构造函数中完成. 至于创建主窗口中的子窗口如菜单, 按钮等, MFC专门编写了一个虚函数OnCreate来负责.
第三, 要有消息映射机制来实现消息循环. 这部分代码的样子如上所示. 当然, 要响应一个消息, 必须有对应的消息函数. 比如上面所演示的, 用OnPaint来响应WM_PAINT消息, 当WM_PAINT消息发出时, 就调用OnPaint. 跟WIN32 SDK程序中每个窗口都有自己独立的消息循环一样, 每个窗口都有自己单独的消息映射.
在WIN32 SDK中要创建类似的程序, 所需要写的代码量比上面所演示的要多的多, 而且结构没有上面的代码那样清晰. 从第一个小程序开始, MFC的强大功能已经有所体现. 由于采用了面向对象的思想, 即使在一些大规模的程序中, MFC程序仍然能够保持清晰的组织结构, 并且代码重用将体现的更加强大. “MFC应该是开发WINDOWS平台桌面应用程序的首选工具”------有位大牛曾经这么说过. 

我做了格式上的修改,原址:http://blog.csdn.net/haonan9122/article/details/5747495


单文档MFC程序的启动详细过程


1、定义和构造全局应用程序对象,CSDITestApp theApp; 

//这个对象用来唯一标示应用程序,相当于SDK中的应用程序句柄hInstance


1.1 定义theApp,为其分配内存空间,由系统自动完成


1.2 调用构造函数:CSDITestApp:: CSDITestApp();


1.2.1 调用基类构造函数:CWinApp:: CWinApp(NULL);


pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this; //AfxGetApp()返回的就是这个值,代表了theApp
m_pDocManager = NULL;
//其他都设为NULL或0,因为此时WinMain还没有启动


1.2.2 子类构造函数CSDITestApp:: CSDITestApp();什么都没做


2、调用WinMain函数:这是编译链接的时候从外部链接进来的,确切的函数为tWinMain(AfxWinMain)


2.1 调用AfxWinMain函数


2.1.0 首先获取CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); //这是在1.2.1中设置的指针


2.1.1 调用AfxWinInit(); //设置四个入口参数


2.1.2 调用CWinApp::InitApplication(); //虚函数,子类可以重载,但一般不重载

//设置成员对象CWinApp::m_pDocManager的静态变量CDocManager::bStaticInit为FALSE


2.1.3 调用CSDITestApp::InitInstance(); //虚函数,子类已重载,一定要重载


2.1.3.1 定义构造CSingleDocTemplate对象


2.1.3.1.1 调用基类构造函数CDocTemplate::CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)
//设置 m_nIDResource = nIDResource;
// m_pDocClass = pDocClass;
// m_pFrameClass = pFrameClass;
// m_pViewClass = pViewClass;
//调用 CDocTemplate::LoadTemplate(); 用m_nIDResource设置m_strDocStrings


2.1.3.1.2 m_pOnlyDoc = NULL;


2.1.3.2 调用CWinApp::AddDocTemplate(pDocTemplate)


2.1.3.2.1 调用CDocManager::AddDocTemplate(pDocTemplate)//将pDocTemplate加入到CDocManager::m_templateList中


2.1.3.3 定义构造CCommandLineInfo cmdInfo


2.1.3.3.1 调用CCommandLineInfo:: CCommandLineInfo();//设置m_nShellCommand = FileNew


2.1.3.4 调用CWinApp::ParseCommandLine(); //分析命令行,详细过程忽略


2.1.3.5 调用CWinApp::ProcessShellCommand(cmdInfo)
//由于m_nShellCommand = FileNew,所以这里将调用CWinApp::OnFileNew,后面细讲
//如果在MDI程序下,可设置m_nShellCommand = FileNothing,则什么都不做。因为在MDI程序下,之前已经通过LoadFrame函数创建了主窗口(MainFrame)。本文仅针对SDI程序。


2.1.3.6 调用CWnd::ShowWindow(SW_SHOW) //非虚函数,不能重载
//将调用API函数::ShowWindow显示主窗口


2.1.3.7 调用CWnd::UpdateWindow() //非虚函数,不能重载
//将调用API函数::UpdateWindow更新主窗口


2.1.4 调用CWinApp::Run(); //建立消息循环。虚函数,可以重载,一般不重载


2.1.4.1 调用CWinThread::Run()


2.1.4.1.1 调用CWinThread::PumpMessage()


2.1.4.1.1.1 调用::GetMessage(), ::TranslateMessage(), ::DispatchMessage()


2.1.4.1.1.2 如果收到WM_QUIT消息,则调用ExitInstance(),返回wParam,退出程序。


2.1.4.1.2 调用::PeekMessage(…, PM_NOREMOVE)作循环


2.1.5 调用AfxWinTerm函数,完成注销等操作


2.1.6 退出程序


3、调用CWinApp::OnFileNew()的过程 //虚函数,可重载


//该过程要构造Document, FrameWnd和View类对象,并创建MainFrame和View窗口。实际上OnFileNew函数并不是直接调用的,而是通过CCmdTarget::OnCmdMsg发送一个ID_FILE_NEW来调用的

3.1 调用CDocManager::OnFileNew()函数

3.1.1 获取CDocTemplate* pTemplate对象指针 //在2.1.3.2.1中加入的,如果是MDI程序,会调出对话框,询问使用哪一个DocTemplate

3.1.2 调用CSingleDocTemplate::OpenDocumentFile(NULL);//虚函数,CDocTemplate基类中为纯虚函数,所以调用子类的函数

3.1.2.1 调用CDocTemplate::CreateNewDocument();

3.1.2.1.1 调用CRuntimeClass::CreateObject()创建并构造一个CSDITestDoc的对象 //CRuntimeClass的含义请看《深入浅出MFC》

3.1.2.1.2 调用CSingleDocTemplate::AddDocument()

3.1.2.1.2.1 调用基类CDocTemplate::AddDocument(),将CSDITestDoc对象的成员m_pDocTemplate设为当前模板this指针。

3.1.2.1.2.2 设置CSingleDocTemplate::m_pOnlyDoc = pDocument; //单文档只能有一个

3.1.2.2 调用CDocTemplate::CreateNewFrame(pDocument, NULL)

3.1.2.2.1调用CRunTimeClass::CreateObject()创建并构造一个CMainFrame的对象

3.1.2.2.2 调用CFrameWnd::LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, …) //该函数将完成注册窗口类结构wndclass,创建主窗口,创建view窗口,创建工具条状态条等任务,后面细讲

3.1.2.3 调用CSDITestDoc::OnNewDocument() //虚函数,子类已重载,加入一些初始化代码

3.1.2.4 调用CDocTemplate::InitialUpdateFrame()

3.1.2.4.1 调用CFrameWnd::InitialUpdateFrame()

3.1.2.4.1.1 设置ActiveView //SetActiveView

3.1.2.4.1.2 SendMessageToDescendants(WM_INITIALUPDATE,…)

3.1.2.4.1.3 pView->OnActiveFrame(WA_INACTIVE, this)

3.1.2.4.1.4 ActivateFrame()

3.1.2.4.1.5 pDoc->UpdateFrameCounts

3.1.2.4.1.6 OnUpdateFrameTitle(TRUE)


4. 调用CFrameWnd::LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, …)的过程 //虚函数,可重载


4.1 调用AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG),即AfxEndDeferRegisterClass构造窗口类,并注册

4.1.1 设置wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;

4.1.2 调用_AfxRegisterWithIcon注册窗口类

4.1.2.1 调用AfxRegisterClass()

//如果窗口类没有注册,则调用API函数::RegisterClass()注册窗口类 //第一次注册

4.2 调用GetIconWndClass()函数

4.2.1 调用CMainFrame::PreCreateWindow(cs) //虚函数,子类已经重载

4.2.1.1 又会调用AfxEndDeferRegisterClass注册窗口类,但此时的窗口类已经在4.1.2.1注册了,所以PreCreateWindow函数的主要功能是更新cs结构。

4.2.2 调用AfxRegisterWndClass注册窗口类 //第二次注册

4.3 调用CFrameWnd::Create()

4.3.1 调用CWnd::CreateEx

4.3.1.1 构造cs

4.3.1.2 再次调用PreCreateWindow(cs) //同样也不会注册窗口类,只是修改cs

4.3.1.3 调用API函数::CreateWindowEx创建主窗口,同时发送WM_CREATE消息

4.3.1.3.1 调用CMainFrame::OnCreate()函数响应消息 //虚函数,子类已经重载。后面详细讲述

5. 调用CMainFrame::OnCreate()函数


5.1 调用基类CFrameWnd::OnCreate()函数

5.1.1 调用CFrameWnd::OnCreateHelper函数

5.1.1.1 调用CWnd::OnCreate()函数 //调用缺省的窗口过程函数,详细过程略

5.1.1.2 调用CFrameWnd::OnCreateClient函数

5.1.1.2.1 调用CFrameWnd::CreateView函数

5.1.1.2.1.1 调用CRunTimeClass::CreateObject()创建CSDITestView类对象

//调用CView::CView()构造函数 m_pDocument = NULL;

5.1.1.2.1.2 调用CView::Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext)

// AFX_WS_DEFAULT_VIEW =WS_CHILD | WS_VISIBLE | WS_BORDER

5.1.1.2.1.2.1 调用CWnd::Create() //此函数只能创建WS_CHILD的窗口,如果是POPUP,需要用CreateEx函数

5.1.1.2.1.2.1.1 调用CWnd::CreateEx() //与4.3.1类似

//在CView::OnCreate函数中,有一个CSDITestDoc::AddView(this),该函数一方面将pView加入到CDocument的成员m_viewList中,另一方面将pView的m_pDocument指向pDocument

5.1.1.3 调用RecalcLayout函数5.2 调用CToolBar和CStatusBar创建工具条和状态条 //省略

我做了格式更改,原址:http://blogguan.blog.sohu.com/67507744.html