當前位置:編程學習大全網 - 編程語言 - MFC有哪些機制

MFC有哪些機制

MFC程序運行機制

學MFC,竟然還不知道MFC的MAIN函數在什麽地方?怎麽運行的?實在不高明。

看過候捷(JJHOU)老師的《深入淺出MFC》的,對它壹定很熟悉。呵呵,本文是獻給沒有看過那本書,但是又很希望學習MFC程序設計的朋友的。(沒有看過那本書的朋友還不趕快去買?)其實本文,主要是對《深入淺出MFC》第六章的壹個總結和補充罷了!(本文有該書不同的地方,也有壹些筆者自己的見解!)

言歸正傳。

假如妳用AppWizard壹步壹步NEXT下來,然後在CLASSVIEW中去找尋WINMAIN函數,那麽妳只有失望。MFC最大的特點是什麽?封裝!MFC的確封裝的太好了,以至於很多想學習MFC的人都望而卻步。閑話少說,還是繼續我們今天的話題,MAIN函數!實話告訴妳吧,即使妳搜索所有的MFC生成的文件,都無法發現WINMAIN的字眼,那麽它就近在什麽地方呢?

我相信妳已經想到,MAIN函數應該在主要的應用程序文件中。難道是“您定義的程序名.cpp”這個文件?不錯就是它。再Crtl+F壹下,看有沒有我們要找的WINMAIN函數?看來妳又要失望了,但是妳註意有這樣壹句:

/////////////////////////////////////////////////////////////////////////////

// The one and only CMyApp object

CMyApp theApp; //本人建立的工程名為My。

是不是很特別,再註意壹下那句註釋“The one and only CMyApp object”,每個應用程序有且只用壹個CMyApp對象。我想妳應該想到了,WinMain函數每個程序也只能有壹個,那麽這個全局對象跟WinMain函數肯定有莫大的關系?沒錯,相信妳的直覺。

特別註意:深曉C++細節的人壹定知道,全局對象優先於MAIN函數執行的道理。如果妳不知道也沒關系,那麽我在這裏告訴妳:“全局對象優先於MIAN函數執行,且構建於棧中,切記,切記!”

現在,我們該深入WinMain運行機制了,確切的說,應該是MFC的機制!

首先,看看MFC的庫文件把,它能給我們帶來許多驚喜。(vc6的相應的目錄是\Microsoft Visual Studio\VC98\MFC\SRC;VC7相應的目錄是\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc)

現在我們就從這個全局下手,開始今天的旅途。

CMyApp theApp;

此時,系統會執行CMyApp的父類(CWinApp)構造函數,再執行CMyApp的構造函數。(先有老爹,再有兒子!),此時就會調用CWinApp的構造函數。

CWinApp的構造函數(在VC提供的MFC代碼中以“文中的壹個字或詞組”的方式查詢關鍵字,此時打開APPCORE.CPP,以下使用相同搜索方式,不再復述。)找到以下內容:

CWinApp::CWinApp(LPCTSTR lpszAppName)

{

if (lpszAppName != NULL)

m_pszAppName = _tcsdup(lpszAppName);

else

m_pszAppName = NULL;

// initialize CWinThread state

AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();

AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;

ASSERT(AfxGetThread() == NULL);

pThreadState->m_pCurrentWinThread = this;

ASSERT(AfxGetThread() == this);

m_hThread = ::GetCurrentThread();

m_nThreadID = ::GetCurrentThreadId();

// initialize CWinApp state

ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please

pModuleState->m_pCurrentWinApp = this;

ASSERT(AfxGetApp() == this);

... ...

}

OK,就到這裏就可以了,仔細看上面代碼,它已經完成了應用程序線程額的啟動,它給予了我們程序的生命。現在請註意:

pThreadState->m_pCurrentWinThread = this;

pModuleState->m_pCurrentWinApp = this;

這兩行代碼其實都是做的壹件事兒。

這段代碼的意思是,獲得了CMyApp的全局對象的this指針。(此時妳肯定要疑問,為什麽是CMyApp的指針?this目前是在CWinApp中啊? 對此我的答案是,可是妳是由CMyApp的對象引發的CWinApp的構造啊!!)這個指針可非壹般的人物,稍後我們的很多工作都要靠它完成。

CWinApp之中的成員變量將因為theApp這個全局對象的誕生而獲得配置和初始值。

構造完父類,現在構造子類。可是我們看到,AppWizard給我們的子類裏它什麽也沒做?是的,這壹切都聽從妳的安排!

CMyApp::CMyApp()

{

// TODO: add construction code here,

// Place all significant initialization in InitInstance

}

接下來就是今天的主角兒了,搜索關鍵字“WinMain”,出現很多文件。別急,因為現在我們應該先看看WinMain的聲明。打開appmodul.cpp:

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

// call shared/exported WinMain

return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

}

這裏_tWinMain是為了支持UNICODE而命名的壹個宏,真正起作用的是AfxWinMain,註意看看它的參數,是不是和SDK的WinMain函數壹樣?

現在再搜索下AfxWinMain,其實在winmain.cpp中:

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

ASSERT(hPrevInstance == NULL);

int nReturnCode = -1;

CWinThread* pThread = AfxGetThread();

CWinApp* pApp = AfxGetApp();

// AFX internal initialization

if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))

goto InitFailure;

// App global initializations (rare)

if (pApp != NULL && !pApp->InitApplication())

goto InitFailure;

// Perform specific initializations

if (!pThread->InitInstance())

{

if (pThread->m_pMainWnd != NULL)

{

TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");

pThread->m_pMainWnd->DestroyWindow();

}

nReturnCode = pThread->ExitInstance();

goto InitFailure;

}

nReturnCode = pThread->Run();

... ...

}

此段代碼註意五個細節:

CWinApp* pApp = AfxGetApp();

意為獲得對象指針,其實就是剛才那個THIS。不記得了?指向CMyApp的那個!還值得註意的是,Afx意是全局的,隨時妳都可以調用它。(AFX就是MFC開發小組的開發代號,意為Application Framework 傳說X只是為了好看,沒實在意思?!)

if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))

AfxWinInit完成了線程的初始化和窗框類的註冊。具體參看appinit.cpp中的定義。

if (pApp != NULL && !pApp->InitApplication())

其實pApp和pThread是同壹個指針,都是指向CMyApp的指針,這裏因為CMyApp中沒有定義InitApplication,實際上就調用的CWinApp::InitApplication(),完成了MFC的內容管理。

if (!pThread->InitInstance())

因為CMyApp中改寫了它,所以調用CMyApp中的,其實它也是初始化工作。此時也完成了默認窗口類的定義。假如妳熟悉SDK編程的話,壹定不會忘記窗口類的設計、註冊、創建、現實及更新的步驟,此時MFC以為妳設計好了默認的窗口類。

現在妳不禁要疑問,InitApplication()和InitInstance()有何不同?

答案是,假如妳執行壹個程序,於是兩個函數都會被調用;當妳在不關閉前壹個程序的前提下,再執行壹個程序,那麽就只執行後壹個函數。

nReturnCode = pThread->Run();

這個壹步驟在《深入淺出MFC》中被成為程序的活水源頭,在我看來它就是妳開車踩油門的步驟。待會我們會具體闡述!

在設計窗口類以後,就應該是註冊,MFC自動調用(跳轉到)AfxEndDeferRegisterClass(WINCORE.CPP中),為妳註冊了五個窗口類,分別是:AfxWnd,AfxCreateBar,AfxMDIFrame,AfxFrameOrView,AfxOleControl以上窗口類MFC將自動轉化成獨立無二的類名,供其調用。

在窗口的註冊以後,就應該是窗口的創建工作,此時會調用CFrameWnd::Create(),該代碼位於WINFRM.Cpp中

BOOL CFrameWnd::Create(LPCTSTR lpszClassName,

LPCTSTR lpszWindowName,

DWORD dwStyle,

const RECT& rect,

CWnd* pParentWnd,

LPCTSTR lpszMenuName,

DWORD dwExStyle,

CCreateContext* pContext)

{

HMENU hMenu = NULL;

if (lpszMenuName != NULL)

{

// load in a menu that will get destroyed when window gets destroyed

HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);

if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)

{

TRACE(traceAppMsg, 0, "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))

{

TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");

if (hMenu != NULL)

DestroyMenu(hMenu);

return FALSE;

}

return TRUE;

}

其中完成了窗口的創建工作,裏面還涉及擴展風格的調用CreateEx,具體細節請參看MSDN。

此時妳不禁要問,我們的事兒都讓MFC做完了?工業化生產出來的窗口都是千篇壹律啊,我要有我自己的風格!

別急,MFC給用戶提供了壹個修改窗口設計的機會那就是:PreCreateWindow(CREATESTRUCT& cs) 妳在MSDN中查詢壹下CREATESTRUCT這個結構體,妳會發現它和我們的CreateWindow幾乎是壹模壹樣,這個就是MFC留給妳修改窗口的壹個機會。在PreCreateWindow時,會跳到CWnd::PreCreateWindow,裏面有壹個宏:AfxDeferRegisterClass,它的作用是:如果該窗口類沒有被註冊,那麽就註冊它;如果註冊了,就什麽也不管!

窗口類的設計、註冊、創建都已經完成,現在只剩下更新和顯示了。這些工作都交由 CMyApp::InitInstance()完成:

m_pMainWnd->ShowWindow(SW_SHOW);

m_pMainWnd->UpdateWindow();

現在if (!pThread->InitInstance())的工作已經完成,按照MAIN函數的內容,接下來該:nReturnCode = pThread->Run()了

此時應該調用CMyApp的Run()函數,但是在CMyApp類中,根本沒有聲明或定義這樣壹個函數,根據多態性的原來,指針遷升,指向CWinApp::Run(),其代碼位於APPCORE.CPP中:

int CWinApp::Run()

{

if (m_pMainWnd == NULL && AfxOleGetUserCtrl())

{

// Not launched /Embedding or /Automation, but has no main window!

TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");

AfxPostQuitMessage(0);

}

return CWinThread::Run();

}

最後妳會發現,它由調用了壹個CWinThread::Run(),此時妳就看不到CWinThread::Run()的代碼了(至少筆者沒有找到,因為微軟只提供了部分MFC代碼。)但是妳可以在MSDN中找到CWinThread::Run()的描述:

Run 控制線程的函數。包含消息泵。壹般不重寫。

再具體點就是:

Run acquires and dispatches Windows messages until the application receives a WM_QUIT message. If the thread's message queue currently contains no messages, Run calls OnIdle to perform idle-time processing. Incoming messages go to the PreTranslateMessage member function for special processing and then to the Windows function TranslateMessage for standard keyboard translation. Finally, the DispatchMessage Windows function is called.

Run is rarely overridden, but you can override it to implement special behavior.

This member function is used only in user-interface threads.

原來它把消息循環包裝了壹下,在MFC中稱為消息映射(message map)的東西!至於消息映射的具體細節本人會另寫文章說明!

OK,MFC不再神秘,掌握了它的來龍去脈,再看其他的MFC書籍的時候,就知道我該怎麽做?為什麽我要這樣做?起到了知其然又知其所以然的效果,這就是我所追求的技術境界

  • 上一篇:急!河南大學的計算機多媒體專業(專科)是什麽方向的?
  • 下一篇:機械制造與自動化的畢業設計怎麽寫?
  • copyright 2024編程學習大全網