1.全局对象theApp先于WinMain函数构造,而theApp是一个派生类的对象,故先调用基类CWinApp的构造函数,再调用派生类对象的构造函数。CWinApp的构造函数定义于APPCORE.CPP文件中
CWinApp::CWinApp(LPCTSTR lpszAppName) { if (lpszAppName != NULL) m_pszAppName = _tcsdup(lpszAppName); else m_pszAppName = NULL; // initialize CWinThread state AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; ASSERT(AfxGetThread() == NULL); ASSERT(AfxGetThread() == this); m_nThreadID = ::GetCurrentThreadId(); // initialize CWinApp state pModuleState->m_pCurrentWinApp = this; ASSERT(AfxGetApp() == this); // in non-running state until WinMain m_hInstance = NULL; m_pszProfileName = NULL; m_pszRegistryKey = NULL; m_pszExeName = NULL; m_pRecentFileList = NULL; m_pDocManager = NULL; m_atomApp = m_atomSystemTopic = NULL; m_lpCmdLine = NULL; m_pCmdInfo = NULL; // initialize wait cursor state m_hcurWaitCursorRestore = NULL; // initialize current printer state // initialize DAO state // other initialization |
CWinApp构造函数主要完成this指针的赋值,将全局对象theApp的this指针赋值给全局唯一实例。该构造函数有一参数,我们在默认调用的时候却没有指定参数,其实这是因为有默认值,
追踪AFXWIN.h头文件查看CWinApp类的定义可知。
class CWinApp : public CWinThread DECLARE_DYNAMIC(CWinApp) public: // Constructor CWinApp(LPCTSTR lpszAppName = NULL); // app name defaults to EXE name ...... |
接着调用我们自己派生类的构造函数,我们可以做一些数据成员的初始化操作。
CHelloMFCApp::CHelloMFCApp() |
2.全局对象之后即是WinMain函数的调用,在MFC中对WinMain进行了封装。所以在文件中查找WinMain是找不到的。可以在源文件的class CAboutDlg : public CDialog这一行加上断点,F5运行,程序停在了
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain } |
代码处,而同时可以看到该代码位于APPMODUL.cpp中。说明程序运行时加载了该函数,有点像我们的WinMain函数。go to definition可以看到
#define _tWinMain WinMain,_tWinMain正是我们的WinMain。
而为什么是_tWinMain而不是WinMain执行,这主要是MFC做了手脚,在MFC开始运行时就运行_tWinMain函数。其中又调用了AfxWinMain函数。在MFC源代码中查找一下AfxWinMain,在WINMAIN.cpp中可以找到AfxWinMain的实现:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, { ASSERT(hPrevInstance == NULL); int nReturnCode = -1; CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); // AFX internal initialization if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) // App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) goto InitFailure; // Perform specific initializations 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: #ifdef _DEBUG // Check for missing AfxLockTempMap calls if (AfxGetModuleThreadState()->m_nTempMapLock != 0) { TRACE1("Warning: Temp map lock count non-zero (%ld)./n", AfxGetModuleThreadState()->m_nTempMapLock); } AfxLockTempMaps(); AfxUnlockTempMaps(-1); #endif AfxWinTerm(); return nReturnCode; } |
稍加整理,可看到AfxWinMain主要做些什么事:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, { int nReturnCode = -1; CWinApp* pApp = AfxGetApp(); AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow); pApp->InitApplication(); pApp->InitInstance(); nReturnCode = pApp->Run(); AfxWinTerm(); return nReturnCode; } |
在APPINIT.cpp中查看AfxWinInit的实现如下:
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, { ASSERT(hPrevInstance == NULL); // handle critical errors and avoid Windows message boxes SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); // set resource handles pModuleState->m_hCurrentInstanceHandle = hInstance; pModuleState->m_hCurrentResourceHandle = hInstance; // fill in the initial state for the application CWinApp* pApp = AfxGetApp(); if (pApp != NULL) { // Windows specific initialization (not done if no CWinApp) pApp->m_hInstance = hInstance; pApp->m_hPrevInstance = hPrevInstance; pApp->m_lpCmdLine = lpCmdLine; pApp->m_nCmdShow = nCmdShow; pApp->SetCurrentHandles(); // initialize thread specific data (for main thread) if (!afxContextIsDLL) AfxInitThread(); return TRUE; } void AFXAPI AfxInitThread() { if (!afxContextIsDLL) { // set message filter proc _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); ASSERT(pThreadState->m_hHookOldMsgFilter == NULL); #ifndef _AFX_NO_CTL3D_SUPPORT // intialize CTL3D for this thread _AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState; if (pCtl3dState->m_pfnAutoSubclass != NULL) (*pCtl3dState->m_pfnAutoSubclass)(AfxGetInstanceHandle()); // allocate thread local _AFX_CTL3D_THREAD just for automatic termination pTemp; // avoid unused warning #endif } }
|
AfxInitThread函数利用钩子函数将消息映射机制引入MFC的Message Map中,然后再回到默认的DefWindowProc。而AfxWinInit也主要做些初始化工作,完成一些初始细节配置。之后是pApp->Application()调用了,由于派生类没有改写,故调用基类CWinApp::Application(),回到APPCORE.CPP文件:
BOOL CWinApp::InitApplication() if (CDocManager::pStaticDocManager != NULL) { if (m_pDocManager == NULL) m_pDocManager = CDocManager::pStaticDocManager; CDocManager::pStaticDocManager = NULL; } if (m_pDocManager != NULL) else CDocManager::bStaticInit = FALSE; return TRUE; } |
可以看到主要做些内部管理。然后是pApp->InitInstance()调用,派生类进行了改写,所以调用的是派生类的InitInstance(),如下:
BOOL CHelloMFCApp::InitInstance() { AfxEnableControlContainer(); // Standard initialization #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL Enable3dControlsStatic(); // Call this when linking to MFC statically // Change the registry key under which our settings are stored. LoadStdProfileSettings(); // Load standard INI file options (including MRU) // Register the application's document templates. Document templates CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_HELLOMTYPE, RUNTIME_CLASS(CHelloMFCDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CHelloMFCView)); AddDocTemplate(pDocTemplate); // create main MDI Frame window if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; // Parse command line for standard shell commands, DDE, file open // Dispatch commands specified on the command line // The main window has been initialized, so show and update it. return TRUE;
|
在派生类的InitInstance函数中产生了一个CMainFrame窗口对象,故先调用基类CMDIFrameWnd的构造函数,CMDIFrameWnd的基类是CFrameWnd,故调用CFrameWnd的构造函数,在WinFrm.cpp文件中:
CFrameWnd::CFrameWnd() { ASSERT(m_hWnd == NULL); m_nWindow = -1; // unknown window ID m_bAutoMenuEnable = TRUE; // auto enable on by default m_lpfnCloseProc = NULL; m_hMenuDefault = NULL; m_hAccelTable = NULL; m_nIDHelp = 0; m_nIDLastMessage = 0; m_pViewActive = NULL; m_cModalStack = 0; // initialize modality support m_phWndDisable = NULL; m_pNotifyHook = NULL; m_hMenuAlt = NULL; m_nIdleFlags = 0; // no idle work at start m_rectBorder.SetRectEmpty(); m_bHelpMode = HELP_INACTIVE; // not in Shift+F1 help mode m_pNextFrameWnd = NULL; // not in list yet m_bInRecalcLayout = FALSE; AddFrameWnd(); |
一直追踪到基类CWnd,CCmdTarget,CObject都没有发现产生窗口(Create)的操作,当然在产生窗口之前首先要注册窗口。其实Create的调用是在pMainFrame->LoadFrame(IDR_MAINFRAME)中完成的。即CFrameWnd::LoadFrame()
BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, // only do this once m_nIDHelp = nIDResource; // ID for help context (+HID_BASE_RESOURCE) CString strFullString; VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); // attempt to create the window // save the default menu handle // load accelerator resource if (pContext == NULL) // send initial update return TRUE;
|
可以看到在LoadFrame中主要做了2件事,第一,注册窗口。第二,创建窗口。先看AfxDeferRegisterClass(),它是一个全局函数,在文件AFXIMPL.H中可以看到
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
在WINCORE.CPP中可以追踪到AfxEndDeferRegisterClass(fClass)如下:
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister) // mask off all classes that are already registered LONG fRegisteredClasses = 0; // common initialization INITCOMMONCONTROLSEX init; // work to register classes as specified by fToRegister, populate fRegisteredClasses as //we go if (fToRegister & AFX_WND_REG) // Child windows - no brush, no icon, safest default class styles if (fToRegister & AFX_WNDOLECONTROL_REG) // OLE Control windows - use parent DC for speed wndcls.lpszClassName = _afxWndOleControl; fRegisteredClasses |= AFX_WNDOLECONTROL_REG; if (fToRegister & AFX_WNDCONTROLBAR_REG) // Control bar windows if (AfxRegisterClass(&wndcls)) fRegisteredClasses |= AFX_WNDCONTROLBAR_REG; if (fToRegister & AFX_WNDMDIFRAME_REG) // MDI Frame window (also used for splitter window) if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame,AFX_IDI_STD_MDIFRAME)) if (fToRegister & AFX_WNDFRAMEORVIEW_REG) { // SDI Frame or MDI Child windows or views - normal colors wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME)) fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG; } if (fToRegister & AFX_WNDCOMMCTLS_REG) // this flag is compatible with the old InitCommonControls() API init.dwICC = ICC_WIN95_CLASSES; fToRegister &= ~AFX_WIN95CTLS_MASK; if (fToRegister & AFX_WNDCOMMCTL_UPDOWN_REG) init.dwICC = ICC_UPDOWN_CLASS; if (fToRegister & AFX_WNDCOMMCTL_TREEVIEW_REG) init.dwICC = ICC_TREEVIEW_CLASSES; if (fToRegister & AFX_WNDCOMMCTL_TAB_REG) init.dwICC = ICC_TAB_CLASSES; if (fToRegister & AFX_WNDCOMMCTL_PROGRESS_REG) Init.dwICC = ICC_PROGRESS_CLASS; if (fToRegister & AFX_WNDCOMMCTL_LISTVIEW_REG) init.dwICC = ICC_LISTVIEW_CLASSES; init.dwICC = ICC_USEREX_CLASSES; if (fToRegister & AFX_WNDCOMMCTL_DATE_REG) init.dwICC = ICC_DATE_CLASSES; // save new state of registered controls // special case for all common controls registered, turn on AFX_WNDCOMMCTLS_REG pModuleState->m_fRegisteredClasses |= AFX_WNDCOMMCTLS_REG; // must have registered at least as mamy classes as requested
|
至此可以看到MFC默认的注册了很多窗口类,而其中调用了AfxRegisterClass函数。而AfxRegisterClass函数实现如下:
BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass) if (!::RegisterClass(lpWndClass)) if (afxContextIsDLL) { // class registered successfully, add to registered list CATCH_ALL(e) AfxUnlockGlobals(CRIT_REGCLASSLIST); THROW_LAST(); END_CATCH_ALL return TRUE;
|
可以看到最终调用Windows API函数实现窗口类的注册。再次回到CFrameWnd::LoadFrame()函数,接下来就是产生窗口的操作了。LoadFrame中的Create调用依据多态性,应调用派生类的Create函数,由于派生类没有改写,此处调用CFrameWnd的Create函数。如下(在WINFRM.CPP中):
BOOL CFrameWnd::Create(LPCTSTR lpszClassName, { HMENU hMenu = NULL; // load in a menu that will get destroyed when window gets destroyed if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL) { TRACE0("Warning: failed to load menu for CFrameWnd./n"); } m_strTitle = lpszWindowName; // save title for later if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext)) { TRACE0("Warning: failed to create CFrameWnd./n"); if (hMenu != NULL) DestroyMenu(hMenu); return FALSE; } return TRUE; |
后者又调用了CreateEx函数,CWnd有CreateEx函数,而CFrameWnd函数并没有改写它,故调用CWnd类的CreateEx函数,在WINCORE.cpp中实现如下:
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle,int x, int y, int nWidth, int nHeight,HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam) { // allow modification of several common create parameters CREATESTRUCT cs; if (!PreCreateWindow(cs)) AfxHookWindowCreate(this); #ifdef _DEBUG TRACE1("Warning: Window creation failed: GetLastError returns0x%8.8X/n", GetLastError()); } #endif if (!AfxUnhookWindowCreate()) if (hWnd == NULL)
|
该函数先是调用PreCreateWindow函数,依据多态性,先调用派生类,接着是CMDIFrameWnd,然后是CFrameWnd的PreCreateWindow函数
在WINFRM.CPP中如下:
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) if (cs.lpszClass == NULL) VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4) if (afxData.bWin4) cs.dwExStyle |= WS_EX_CLIENTEDGE; return TRUE; } |
沿着AfxDeferRegisterClass追踪下去,和上面的相同,都为MFC注册窗口类。
追踪完毕,回到开始的InitInstance函数中,接下来就是ShowWindow、UpdateWindow了。UpdateWindow会发出WM_PAINT消息。再回到AfxWinMain()函数就是调用pApp->Run()函数了。因为派生类没有改写所以调用CWinApp::Run()函数,在APPCORE.CPP中实现如下:
int CWinApp::Run() if (m_pMainWnd == NULL && AfxOleGetUserCtrl()) // Not launched /Embedding or /Automation, but has no main window! TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application./n"); } return CWinThread::Run();
|
而CWinThread::Run实现如下,在文件THRDCORE.CPP中:
int CWinThread::Run() // for tracking the idle time state // acquire and dispatch messages until a WM_QUIT message is received. { // phase1: check to see if we can do idle work // call OnIdle while in bIdle state // phase2: pump messages while available { // pump message, but quit on WM_QUIT // reset "no idle" state after pumping "normal" message bIdle = TRUE; } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); ASSERT(FALSE); // not reachable
|
而CWinThread::PumpMessage实现如下:
BOOL CWinThread::PumpMessage() if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) return FALSE; #ifdef _DEBUG TRACE0("Error: CWinThread::PumpMessage called when not permitted./n"); ASSERT(FALSE); } #endif #ifdef _DEBUG _AfxTraceMsg(_T("PumpMessage"), &m_msgCur); #endif // process this message if (m_msgCur.message != WM_KICKIDLE&& !PreTranslateMessage(&m_msgCur)) { ::TranslateMessage(&m_msgCur); return TRUE; |
可以看到都是调用底层Windows API实现。程序退出时回到AfxWinInit最后调用AfxWinTerm终止程序。而消息映射的实现是利用钩子函数在AfxWndProc中实现的,最后没有处理的消息都用DefWindowProc函数处理