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

来源:互联网 发布:淘宝直通车关键词技巧 编辑:程序博客网 时间:2024/05/09 09:36

MFC启动过程

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

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

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

[cpp] view plain copy
  1. CTheApp theApp;  
  2.   
  3. BOOL CTheApp::InitInstance()  
  4.   
  5. {  
  6.   
  7.          ....  
  8.   
  9.          m_pMainWnd = new CTheWindow();//调用窗口类的构造函数来创建一个窗口  
  10.   
  11.          m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口  
  12.   
  13.          m_pMainWnd->UpdateWindow();//更新窗口上的元素  
  14.   
  15.          return TRUE;  
  16.   
  17. }  
  18.   
  19. //////////////  
  20.   
  21. int AFXAPI AfxWinMain()  
  22.   
  23. {  
  24.   
  25.          CWinThread *pThread = AfxGetThread();//获取主线程指针  
  26.   
  27.          CWinApp *pApp = AfxGetApp();  
  28.   
  29.          AfxWinInit();  
  30.   
  31.          ....  
  32.   
  33.          pApp->InitApplication();  
  34.   
  35.          ...  
  36.   
  37.          pThread->InitInstance();//初始化应用程序实例  
  38.   
  39.          ...  
  40.   
  41.          nReturnCode = pThread->Run();//开始消息循环  
  42.   
  43. }  

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


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


[cpp] view plain copy
  1. //HELLO.H:  
  2.   
  3. class CTheApp : public CwinApp//应用程序类定义  
  4.   
  5. {  
  6.   
  7. public:  
  8.   
  9.     virtual BOOL InitInstance ();  
  10.   
  11. };  
  12.   
  13. class CMainWindow : public CframeWnd//主窗口类定义  
  14.   
  15. {  
  16.   
  17. public:  
  18.   
  19.     CMainWindow ();  
  20.   
  21. protected:  
  22.   
  23.     afx_msg void OnPaint ();  
  24.   
  25.     DECLARE_MESSAGE_MAP ()  
  26.   
  27. };  
  28.   
  29. //HELLO.CPP  
  30.   
  31. #include <afxwin.h>  
  32.   
  33. #include "Hello.h"  
  34.   
  35. CTheApp theApp;//全局应用程序对象  
  36.   
  37. /////////////////////////////////////////////////////////////////////////  
  38.   
  39. // CMyApp member functions  
  40.   
  41. BOOL CTheApp::InitInstance ()//初始化应用应用程序实例: 创建并显示一个窗口  
  42.   
  43. {  
  44.   
  45.     m_pMainWnd = new CMainWindow;  
  46.   
  47.     m_pMainWnd->ShowWindow (SW_SHOW);  
  48.   
  49.     m_pMainWnd->UpdateWindow ();  
  50.   
  51.     return TRUE;  
  52.   
  53. }  
  54.   
  55. /////////////////////////////////////////////////////////////////////////  
  56.   
  57. // CMainWindow message map and member functions  
  58.   
  59. BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)//消息映射  
  60.   
  61.     ON_WM_PAINT ()  
  62.   
  63. END_MESSAGE_MAP ()  
  64.   
  65. CMainWindow::CMainWindow ()//构造一个窗口  
  66.   
  67. {  
  68.   
  69.     Create (NULL, _T ("Hello MFC!"));  
  70.   
  71. }  
  72.   
  73. void CMainWindow::OnPaint ()//重绘窗口内容  
  74.   
  75. {  
  76.   
  77.     CPaintDC dc (this);  
  78.   
  79.     CRect rect;  
  80.   
  81.     GetClientRect (&rect);  
  82.   
  83.     dc.DrawText (_T ("Hello, MFC"), -1, &rect,  
  84.   
  85.         DT_SINGLELINE | DT_CENTER | DT_VCENTER);  
  86.   
  87. }  

在上面的代码中可以看到一个最简单的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

0 0
原创粉丝点击