MFC将大部分函数都进行了封装,程序员只要改写部分重要的virtual函数即可,这往往使初学者摸不着头脑,连个WinMain函数都看不到,程序从哪开始从哪结束?基本的条理搞不清,永远也不会有提高。下面简单讲下基运行过程. 1,CMyWinApp theApp 程序从这里开始 2,_tWinMain() 在APPMODUL.CPP 它实际上只调用AfxWinMain函数 3,AfxWinMain() WINAMIN.CPP,去掉一些次要信息,它作的事就是: int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { int nReturnCode = -1; CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp(); ->实际上就是取得CMyWinApp对象指针 AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
pApp->InitApplication() pThread->InitInstance() pThread->Run();
AfxWinTerm(); return nReturnCode; }
其中: AfxGetApp(); AFXWIN.CPP中 InitApplication() AFXWIN.CPP中 InitInstance() AFXWIN.CPP中
实际上AfxWinMainy就是调用: CWinApp::InitApplication 因为我们程序没有改写它 CMyWinApp::InitInstance 我们改写了它且必须改写它,为什么?看源码就能证明一切
BOOL CWinApp::InitInstance() APPCORE.CPP中 { return TRUE; } 看到了吧,它什么也没干,直接return TRUE;
CWinApp::Run();
4:AfxWinInit() AFX内部初始化操作,APPINIT.CPP中,贴下源码,如下: BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { ..,去掉部分 pModuleState->m_hCurrentInstanceHandle = hInstance; pModuleState->m_hCurrentResourceHandle = hInstance;
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(); } if (!afxContextIsDLL) AfxInitThread();
return TRUE; } 可以看到。它主要进行一些初始化工作.其中: AfxInitThread() 在THRDCORE.CPP中
5:CWinApp::InitApplication() APPCORE.CPP中, BOOL CWinApp::InitApplication() { if (CDocManager::pStaticDocManager != NULL) { if (m_pDocManager == NULL) m_pDocManager = CDocManager::pStaticDocManager; CDocManager::pStaticDocManager = NULL; } if (m_pDocManager != NULL) m_pDocManager->AddDocTemplate(NULL); else CDocManager::bStaticInit = FALSE; return TRUE; } 5:CMyWinApp::InitInstance() 继InitApplication后就是InitInstance调用了。在我们改写的InitStance函数中,将是我们的主窗口的生命,在这里我们会有这样的操作: m_pMainWnd=new CMyFrameWnd();开始我们的主窗口,创建主窗口,MDI程序采取的是LoadFrame,大家可以看CFrameWnd::LoadFrame()(WNDFRM.CPP中),实际还是做了一样的事,大家不防看下源码。
6:CFrameWnd::Create()
BOOL CFrameWnd::Create(LPCTSTR lpszClassName, 类名 LPCTSTR lpszWindowName, 窗口名 DWORD dwStyle, 样式 const RECT& rect, 区域,默认:rectDefault CWnd* pParentWnd, 父窗口 LPCTSTR lpszMenuName, 菜单 DWORD dwExStyle, 扩展样式 CCreateContext* pContext) { HMENU hMenu = NULL; if (lpszMenuName != NULL) { HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU); if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL) { TRACE0("Warning: failed to load menu for CFrameWnd.\n"); PostNcDestroy(); // perhaps delete the C++ object return FALSE; } } 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; }
7:CWnd::CreateEx() 可以看到CFrameWnd::Create,只是调用了CreateEx 函数,CFrameWnd没有改字CreateEx 函数,所以它调用的是CWnd 类的函数.
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; 用过SDK写程序的朋友一定知道这是想做什么 cs.dwExStyle = dwExStyle; cs.lpszClass = lpszClassName; cs.lpszName = lpszWindowName; cs.style = dwStyle; cs.x = x; cs.y = y; cs.cx = nWidth; cs.cy = nHeight; cs.hwndParent = hWndParent; cs.hMenu = nIDorHMenu; cs.hInstance = AfxGetInstanceHandle(); cs.lpCreateParams = lpParam;
if (!PreCreateWindow(cs)) { PostNcDestroy(); return FALSE; }
AfxHookWindowCreate(this); HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); ... } 此函数又调用了PreCreateWindow,CreateWindowEx,
8:CFrameWnd::PreCreateWindow(), PreCreateWindow是个virtual,由于this 指针的原故,这里是调用CFrameWnd的PreCreateWindow.一般我们的程序都会改写些函数,在此设定窗口的一些样式。
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) --->WINFRM.CPP { if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background } ... }
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
AfxEndDeferRegisterClass() --->WINCORE.CPP,在这个函数中,调用RegisterWithIcon(实际上还是要调用AfxRegisterClass),和AfxRegisterClass,会我们注五个窗口类.在non-Unicode下使用MFC动态链接版和谳试版,五个类为: "AfxWnd42d" "AfxControlBar42d" "AfxMDIFrame42d" "AfxFrameOrView42d" "AfxOleControl42d" 在Uncode中使用静态和调试版,是: "AfxWnd42sud" "AfxControlBar42sud" "AfxMDIFrame42sud" "AfxFrameOrView42sud" "AfxOleControl42sud"
大家可以看到了。PreCreateWindow是在窗口产生前被调用,用来注册窗口类,如果我们指定的窗口类为NULL,则会用系统默认的。可以来看下不同功能的窗口使用的窗口类: BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs) -->WINCORE.CPP { if (cs.lpszClass == NULL) { ... cs.lpszClass = _afxWnd; file://使用_afxWnd窗口类 } }
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) -->WINFRM.CPP { if (cs.lpszClass == NULL) { ... cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background } }
BOOL CMDIFrameWnd::PreCreateWindow(CREATESTRUCT& cs) -->WINMDI.CPP { if (cs.lpszClass == NULL) { ... cs.lpszClass = _afxWndMDIFrame; file://使用_afxWndMDIFrame 窗口类 } return TRUE; }
...
9:CWinApp::Run() 程序到这里,窗口类注册好了。并显示出来了。UpDateWindow函数将会调用,并产生一个WM_PAINTIITH,等待处理。便进入了RUN函数, file://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"); AfxPostQuitMessage(0); } return CWinThread::Run(); // 调用CWinThread::Run() }
file://THRDCORE.CPP int CWinThread::Run() { ASSERT_VALID(this);
// for tracking the idle time state BOOL bIdle = TRUE; LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received. for (;;) { // phase1: check to see if we can do idle work while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)) { // call OnIdle while in bIdle state if (!OnIdle(lIdleCount++)) bIdle = FALSE; // assume "no idle" state }
// phase2: pump messages while available do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance();
// reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; }
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); }
ASSERT(FALSE); // not reachable }
如果没有消息,则调用OnIdle(),空闲处理,否则PumpMessage(),看下它做了什么? BOOL CWinThread::PumpMessage() { ASSERT_VALID(this);
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) { #ifdef _DEBUG if (afxTraceFlags & traceAppMsg) TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n"); m_nDisablePumpCount++; // application must die // Note: prevents calling message loop things in 'ExitInstance' // will never be decremented #endif return FALSE; }
#ifdef _DEBUG if (m_nDisablePumpCount != 0) { TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n"); ASSERT(FALSE); } #endif
#ifdef _DEBUG if (afxTraceFlags & traceAppMsg) _AfxTraceMsg(_T("PumpMessage"), &m_msgCur); #endif
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { ::TranslateMessage(&m_msgCur); ::DispatchMessage(&m_msgCur); } return TRUE; }
呵,和SDK程序做法一样,GetMessage,TranslateMessage(),DisptchMessage() 好了。进入到Run ()这里,程序就活动起来了。它会对一系列的消息进行处理。并在收到WM_CLOSE 消息时退出.默认函数对WM_CLOSE的处理是是调用::DestroyWindow(),关因而发出WM_DESTROY消息,默认的的WM_DESTROY处理方式是调用::PostQuitMessage(),因此发出WM_QUIT,Run收到WM_QUIT时,就结束消息循环,调用ExitInstance,最后在AfxWinMain中执行AfxWinTerm结束程序.

|