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 functions
- BOOL CTheApp::InitInstance ()//初始化应用应用程序实例: 创建并显示一个窗口
- {
- m_pMainWnd = new CMainWindow;
- m_pMainWnd->ShowWindow (SW_SHOW);
- m_pMainWnd->UpdateWindow ();
- return TRUE;
- }
- /
- // CMainWindow message map and member functions
- BEGIN_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