首先在appmodule.cpp函数中有一个函数_tWinMain,这个函数的实现和SDK程序设计当中基本是一样的。没错这个函数就是整个MFC程序的起手式。在这个函数当中,直接调用我们定义的AfxWinMain函数,是不是,实际上这里加上Afx前缀主要是为了表明这个函数式MFC全局函数(在所有的MFC函数里面加上Afx函数表明这是MFC的全局函数)。
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); }
在AfxWinMain函数里面首先获取全局的线程和全局的应用程序。貌似这里刚刚起来,那么从哪里来的线程和CApp类别呢?因为在我们的实现当中,会包含一个全局的CApp,因为全局变量会在程序运行起来之前进行初始化,所以这里线程和全局的App已经在WinMain函数之前已经初始化了。
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { int nReturnCode = -1; CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) goto InitFailure; if (pApp != NULL && !pApp->InitApplication()) goto InitFailure; if (!pThread->InitInstance()) { if (pThread->m_pMainWnd != NULL) { TRACE0("Warning: Destroying non-NULL m_pMainWnd\n"); pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } nReturnCode = pThread->Run(); InitFailure: AfxWinTerm(); return nReturnCode; }
首先,这里跳到InitInstance()函数调用的地方,由于这个函数是一个虚拟函数,所以这里实际上是调用我们自己实现的函数。至于pThread和pApp指针指向同一个地方可以从上面的状态数据分析中可以看出来。
BOOL CGraphicApp::InitInstance() { AfxEnableControlContainer(); Enable3dControlsStatic(); SetRegistryKey(_T("Local AppWizard-Generated Applications")); LoadStdProfileSettings(); CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CGraphicDoc), RUNTIME_CLASS(CMainFrame), RUNTIME_CLASS(CGraphicView)); AddDocTemplate(pDocTemplate); CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo)) return FALSE; m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; }
整个InitInstance函数当中,最重要的部分是ProcessShellCommand函数的调用,在这里通过一系列的处理才真正实现生成DOC,VIEW,以及frame。这些类别的生成以及与相应的句柄关联都是在CWinApp类别当中的OnFileNew当中,而OnFileNew则通过调用m_pDocManager->OnFileNew(),其实之前在InitInstance函数当中已经向m_pDocManager传递了文件模板。而文件模板的构造函数包含三个用于生成DOC,VIEW,FRAME的函数,所以,这里通过RUNTIME_CLASS(CGraphicDoc)、RUNTIME_CLASS(CMainFrame)、RUNTIME_CLASS(CGraphicView)将类对象新建出来,然后通过相应的创建相应的句柄并和相应的类别关联。生成顺序依次是DOC、FRAME、VIEW。因为这个窗口依赖文档的形式,而View作为frame的子窗口依赖于frame的生成。
窗口的创建会有一个问题,我们希望整个消息通过我们的消息运行环境处理环境。然而,因为我们的消息处理是一个类成员函数,并且消息还没有循环还没有完整的建立起来。整个frame和view的消息循环还没有加到整个消息循环当中去。所以在创建的时候需要HOOK一下,让WNDCLASS的消息处理函数指针指向我们的全局消息处理函数。然后在消息处理函数当中调用我们的类成员函数进行实际的消息处理。
MFC 窗口消息流程:
MFC把Windows的窗口句柄HWND封装成一个CWnd类,每个MFC窗口类(对话框除外)的Create最终都是会调用基类的CWnd::CreateEx过程。在CWnd::CreateEx过程中:
1) 调用PreCreateWindow虚函数允许用户在窗口创建之前修改窗口的相关属性。
2) 调用AfxHookWindowCreate安装窗口创建钩子WH_CBT: _AfxCbtFilterHook (目的在于将窗口过程替换成AfxWndProc)。
3) 调用::AfxCtxCreateWindowEx系统创建创建窗口。窗口创建过程中的消息被转移_AfxCbtFilterHook过程。
4) 窗口创建完毕后。调用AfxUnhookWindowCreate将窗口创建钩子WH_CBT从钩子链中移除。之后的窗口消息就会被分派到MFC的窗口过程函数AfxWndProc中。
_AfxCbtFilterHook过程:
1) 将创建的C++类对象与窗口句柄关联。
2) 调到虚函数PreSubclassWindow允许用户在窗口关联后做一些额外的事情。例如:修改样式ModifyStyle。 这是窗口创建后修改数据的最佳之处。无论是Create,还是通过对话框DoDataExchange关联的窗口类。都会调用到这个函数。
创建方式 |
PreCreateWindow |
PreSubclassWindow |
OnCreate |
Create |
是 |
是 |
是 |
SubclassWindow |
否 |
是 |
否 |
对话框模板创建的控件跟随对话框一起创建。这期间并没有关联C++对象。控件句柄与C++对象的关联是在DoDataExchange中完成的。而此时窗口已经创建完成。固然子类化不可能再收到WM_CREATE这种创建消息。