MFC应用程序框架入门
摘要: 本文主要對(duì)VC++ 6.0的MFC編程方法及MFC應(yīng)用程序框架進(jìn)行簡(jiǎn)要介紹。
關(guān)鍵詞: VC++6.0;MFC;程序框架
1 MFC概述
顧名思意,MFC應(yīng)用程序框架是以MFC作為框架基礎(chǔ)的,以此程序框架模式搭建起來(lái)的應(yīng)用程序在程序結(jié)構(gòu)組織上是完全不同于以前的Win32 SDK編程方式的。自20世紀(jì)90年代初問(wèn)世以來(lái),MFC一直試圖把Windows API函數(shù)封裝到類(lèi)庫(kù)中個(gè)各個(gè)邏輯類(lèi)中。MFC的這種封裝并非簡(jiǎn)單地對(duì)API函數(shù)進(jìn)行分組與打包,而是更多地通過(guò)類(lèi)來(lái)試圖實(shí)現(xiàn)全部的系統(tǒng)策略。隨著越來(lái)越多系統(tǒng)功能的加入,MFC的規(guī)模也在不斷拓展,目前已包括有200多個(gè)類(lèi),涵蓋了通用Windows 類(lèi)、文檔/視框架、OLE、數(shù)據(jù)庫(kù)、Internet以及分布式功能等多方面的基本內(nèi)容。這樣一個(gè)堅(jiān)實(shí)的程序開(kāi)發(fā)基礎(chǔ)無(wú)疑從很大程度上方便了程序設(shè)計(jì)人員對(duì)Windows 程序的開(kāi)發(fā)。
MFC提供了相當(dāng)多不同功能的類(lèi)以適合盡可能廣泛的需求。這里絕大多數(shù)的MFC類(lèi)都是直接或間接從CObject類(lèi)派生出來(lái)的,CObject類(lèi)為其派生類(lèi)提供了三個(gè)重要的特性支持:持久性(Serialization)支持、運(yùn)行時(shí)(Run-time)類(lèi)信息支持和診斷(Diagnostic)調(diào)試支持等。其中持久性是以流的方式將某個(gè)類(lèi)對(duì)象中的持久性數(shù)據(jù)輸出或輸入到外部存儲(chǔ)介質(zhì)如磁盤(pán)文件等的過(guò)程;運(yùn)行時(shí)類(lèi)信息(Run-time Class Information,RTCI)則可以重新獲取一個(gè)對(duì)象的類(lèi)名及其他一些有關(guān)對(duì)象在運(yùn)行時(shí)的信息。RTCI也是C++中除運(yùn)行時(shí)類(lèi)型信息(Run-time Type Information,RTTI)機(jī)制外的另一個(gè)重要工具;診斷和調(diào)試支持作為CObject類(lèi)的一個(gè)組成部分,可以在實(shí)現(xiàn)CObject派生類(lèi)時(shí)執(zhí)行有效性檢查并可向調(diào)試窗口輸出狀態(tài)信息。
并非MFC提供的所有函數(shù)都是類(lèi)成員函數(shù),MFC也提供了一系列以Afx為前綴的全局函數(shù)。類(lèi)成員函數(shù)只能在其所屬類(lèi)對(duì)象所在的上下文中使用,但是這些AFX函數(shù)卻可以在任何時(shí)候的任何地方直接使用。下表列出的是幾個(gè)比較重要AFX函數(shù):
?
| 函數(shù)名 | 函數(shù)說(shuō)明 |
| AfxAbout | 無(wú)條件終止一個(gè)應(yīng)用程序;通常在發(fā)生無(wú)法回復(fù)的錯(cuò)誤時(shí)使用 |
| AfxBeginThread | 創(chuàng)建一個(gè)新的線程并開(kāi)始執(zhí)行 |
| AfxEndThread | 終止當(dāng)前正在執(zhí)行的線程 |
| AfxMessageBox | 顯示一個(gè)Windows 消息窗口 |
| AfxGetApp | 返回一個(gè)指向應(yīng)用程序?qū)ο蟮闹羔?/td> |
| AfxGetAppName | 返回應(yīng)用程序名 |
| AfxGetMainWnd | 返回一個(gè)指向應(yīng)用程序主窗口的指針 |
| AfxGetInstanceHandle | 返回一個(gè)標(biāo)識(shí)當(dāng)前應(yīng)用程序?qū)嵗木浔?/td> |
| AfxRegisterWndClass | 為一個(gè)MFC應(yīng)用程序注冊(cè)一個(gè)用戶(hù)自定義的窗口類(lèi) |
?
?
2 MFC對(duì)API函數(shù)的封裝
如果讀者曾經(jīng)有過(guò)SDK的開(kāi)發(fā)經(jīng)歷,一定會(huì)對(duì)其煩瑣的編程方式和大量的Win32 API函數(shù)調(diào)用深有感觸。所有不同功能的API函數(shù)均是以全局函數(shù)的形式放在一起的,由于API函數(shù)數(shù)目比較龐大,因此無(wú)論是學(xué)習(xí)還是使用都是有一定難度的。相比而言,建立在API函數(shù)基礎(chǔ)之上的MFC類(lèi)庫(kù)則通過(guò)把相關(guān)API函數(shù)的分類(lèi)封裝而可以大大簡(jiǎn)化編程的難度,用MFC類(lèi)編寫(xiě)的Windows 應(yīng)用程序完成相同的任務(wù)只需要進(jìn)行少量的工作。
眾多的API函數(shù)根據(jù)功能的不同而被MFC封裝到200多個(gè)類(lèi)中,這些類(lèi)基本涵蓋了進(jìn)行Windows 編程大部分可能用到的功能。由于封裝后的MFC類(lèi)太多,這里不能一一介紹,下面就以其中比較重要的CObject類(lèi)和CWnd類(lèi)為例對(duì)API函數(shù)的封裝情況做一簡(jiǎn)要介紹。
CObject類(lèi)是MFC中最主要也是最基本的類(lèi)之一,該類(lèi)不支持多重繼承,派生的類(lèi)只能有一個(gè)CObject基類(lèi)。CObject類(lèi)是位于類(lèi)層次結(jié)構(gòu)最頂層的,絕大多數(shù)MFC類(lèi)都是從CObject類(lèi)派生出來(lái)的。CObject類(lèi)包含了所有MFC類(lèi)必須具備的幾個(gè)基本功能:持久性支持、運(yùn)行時(shí)類(lèi)信息支持和診斷調(diào)試支持。其中持久性支持功能由成員函數(shù)IsSerializable()和Serialize()提供。前者用于檢測(cè)對(duì)象是否支持序列化。如果一個(gè)類(lèi)能夠被序列化,就必須在聲明時(shí)包含DECLARE_SERIAL宏、在實(shí)現(xiàn)時(shí)包含IMPLEMENT_SERIAL宏。Serialize()函數(shù)則可以將對(duì)象寫(xiě)入檔案文件(Archive)或從檔案文件讀出對(duì)象。成員函數(shù)GetRuntimeClass()可以獲取到一個(gè)指向CruntimeClass類(lèi)對(duì)象的指針,通過(guò)該指針可以得到對(duì)象的運(yùn)行時(shí)類(lèi)信息。CObject類(lèi)在診斷調(diào)試支持方面提供了成員函數(shù)AssertValid()和Dump(),前者可對(duì)對(duì)象內(nèi)存狀態(tài)的有效性進(jìn)行檢查,后者負(fù)責(zé)將對(duì)象的內(nèi)容轉(zhuǎn)儲(chǔ)到一個(gè)CdumpContext對(duì)象中,并可以提供診斷服務(wù)及一些有用的調(diào)試信息。
在MFC中,CWnd類(lèi)提供了所有窗口類(lèi)的基本功能,是一個(gè)非常重要的類(lèi),大約三分之一的MFC類(lèi)都是以此為基類(lèi)。該類(lèi)主要對(duì)創(chuàng)建、操縱窗口類(lèi)的API函數(shù)進(jìn)行了封裝,而且通過(guò)消息映射機(jī)制隱藏了SDK編程中使用相當(dāng)不便的窗口處理函數(shù),是消息的分發(fā)處理更加方便。
CWnd類(lèi)最重要的一個(gè)封裝是對(duì)API函數(shù)CreateWindow()的封裝,該函數(shù)被封裝為CWnd類(lèi)成員函數(shù)Create()。從VC提供的MFC源文件WinCore.cpp中可以清楚看出CWnd類(lèi)對(duì)CreateWindow()函數(shù)的封裝過(guò)程,下面給出相關(guān)部分的實(shí)現(xiàn)清單:
?
| BOOL CWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) { // can't use for desktop or pop-up windows (use CreateEx instead) ASSERT(pParentWnd != NULL); ASSERT((dwStyle & WS_POPUP) == 0); return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext); } |
可以看出,主要工作是在CreateEx()成員函數(shù)中完成的,而該函數(shù)又是對(duì)API函數(shù)CreateWindowEx()的封裝。封裝后的代碼在調(diào)用CreateWindowEx()前構(gòu)造并填充了一個(gè)非常類(lèi)似于WNDCLASS結(jié)構(gòu)的CREATESTRUCT結(jié)構(gòu),并調(diào)用了PreCreateWindow()。
?
| 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; 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); #ifdef _DEBUG if (hWnd == NULL) { TRACE1("Warning: Window creation failed: GetLastError returns 0x%8.8X\n", GetLastError()); } #endif if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if CreateWindowEx fails too soon if (hWnd == NULL) return FALSE; ASSERT(hWnd == m_hWnd); // should have been set in send msg hook return TRUE; } |
看上去經(jīng)過(guò)封裝的窗口創(chuàng)建函數(shù)要比原API函數(shù)復(fù)雜許多,但這并不說(shuō)明MFC的封裝將導(dǎo)致編程的效率低下,恰恰相反,由于CWnd在絕大多數(shù)場(chǎng)合中是以基類(lèi)的形式出現(xiàn)的,因此可在派生類(lèi)中添加代碼完成對(duì)CWnd::Create()的調(diào)用而比較方便的實(shí)現(xiàn)對(duì)派生類(lèi)窗口的創(chuàng)建。
?
?
3 MFC應(yīng)用程序框架?
MFC應(yīng)用程序框架可以看作是MFC基本類(lèi)庫(kù)的一個(gè)超集(Superset),類(lèi)庫(kù)是眾多可在任何程序中使用的類(lèi)的集合,而應(yīng)用程序框架則定義了程序自身的結(jié)構(gòu)。下面給出一個(gè)使用了MFC應(yīng)用程序框架的簡(jiǎn)單例子,通過(guò)這段例程可以比較清楚地了解MFC應(yīng)用程序框架的一般結(jié)構(gòu)。
?
| // Sample01.h文件 // 應(yīng)用程序類(lèi) class CSample01App : public CWinApp { public: virtual BOOL InitInstance(); }; // 框架窗口類(lèi) class CSample01Frame : public CFrameWnd { public: CSample01Frame(); protected: afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() }; // Sample01.cpp文件 #include <afxwin.h> #include "Sample01.h" // 應(yīng)用程序?qū)ο?br /> CSample01App theApp;? // 初始化應(yīng)用程序?qū)嵗?br /> BOOL CSample01App::InitInstance() { m_pMainWnd = new CSample01Frame(); m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE; } // 消息映射? BEGIN_MESSAGE_MAP(CSample01Frame, CFrameWnd) ON_WM_PAINT() END_MESSAGE_MAP() // 構(gòu)造函數(shù) CSample01Frame::CSample01Frame() { Create(NULL, "MFC應(yīng)用程序框架程序"); } // WM_PAINT消息響應(yīng)函數(shù) void CSample01Frame::OnPaint() { CPaintDC dc(this); dc.TextOut(100, 100, "Hello World!"); } |
仍象編寫(xiě)Sample00程序一樣建立一個(gè)Win32應(yīng)用程序工程Sample01(配套程序見(jiàn)"光盤(pán)\配套程序\Sample01\"),然后分別向工程添加頭文件Sample01.h和源文件Sample01.cpp,并將上述代碼寫(xiě)入相應(yīng)的文件。為了能順利編譯,還需要修改一下編譯命令,通過(guò)"Alt+F7"快捷鍵呼出【Project Settings】對(duì)話(huà)框,在【Preprocessor definitions】欄的最后添加選項(xiàng)"_AFXDLL",前面用逗號(hào)分隔。接下來(lái)還需要在【Project Options】欄的最后添加命令行"/MD",用空格同其他命令行參數(shù)進(jìn)行分隔。編譯運(yùn)行,可以看出效果同SDK方式編寫(xiě)的Sample00程序是一樣的,但在代碼實(shí)現(xiàn)上更加結(jié)構(gòu)化,編寫(xiě)過(guò)程也更加簡(jiǎn)單。
接下來(lái),對(duì)上述應(yīng)用程序框架代碼進(jìn)行分析。首先從MFC應(yīng)用程序的核心--CWinApp類(lèi)的派生類(lèi)CSample01App談起。CWinApp類(lèi)提供了可以獲取消息并將獲取到的消息分發(fā)到應(yīng)用程序窗口的消息循環(huán)和一些關(guān)鍵的虛函數(shù),通過(guò)對(duì)這些虛函數(shù)的重載可使開(kāi)發(fā)人員對(duì)應(yīng)用程序的一些固有行為進(jìn)行擴(kuò)展。當(dāng)把頭文件Afxwin.h包含進(jìn)來(lái)后,就可以在程序中使用包括CWinApp在內(nèi)的一些MFC類(lèi)了。一個(gè)MFC應(yīng)用程序有且只能有一個(gè)應(yīng)用程序?qū)ο蠖冶仨毐灰匀址绞竭M(jìn)行聲明,所以該對(duì)象自程序開(kāi)始運(yùn)行起就一直駐留在內(nèi)存。
由于使用了MFC應(yīng)用程序框架的程序在本質(zhì)上仍是Windows 應(yīng)用程序,因此必然需要在程序中存在作為Windows 應(yīng)用程序入口的WinMain()函數(shù)。在前面的示例代碼中之所以沒(méi)有看到WinMain()函數(shù)是由于該函數(shù)已經(jīng)通過(guò)封裝的手段隱藏到應(yīng)用程序框架中了。除WinMain()外,CWinApp類(lèi)成員函數(shù)Run()也是隱含執(zhí)行的,這個(gè)函數(shù)也是非常重要的,它負(fù)責(zé)把消息放進(jìn)應(yīng)用程序窗口的消息循環(huán)中,由WinMain()函數(shù)完成對(duì)Run()的調(diào)用 。當(dāng)WinMain()函數(shù)尋找到應(yīng)用程序?qū)ο蠛髮⒘⒓凑{(diào)用CWinApp類(lèi)的虛函數(shù)InitInstance()。由于CWinApp基類(lèi)是不知道究竟需要何種主框架窗口的,因此在使用時(shí)必須在CWinApp的派生類(lèi)中對(duì)InitInstance()函數(shù)進(jìn)行重載。InitInstance()函數(shù)是在應(yīng)用程序已經(jīng)開(kāi)始運(yùn)行但窗口尚未創(chuàng)建時(shí)被調(diào)用的,若非由InitInstance()創(chuàng)建了窗口,應(yīng)用程序是無(wú)法擁有窗口的,這也就意味著缺少了InitInstance()函數(shù)的應(yīng)用程序?qū)o(wú)法接收、處理消息,對(duì)Windows程序而言這也就失去了存在的意義。由此可見(jiàn),從CWinApp類(lèi)中進(jìn)行派生,并且對(duì)InitInstance()函數(shù)進(jìn)行重載是編寫(xiě)MFC應(yīng)用程序框架程序的必要條件。
除應(yīng)用程序類(lèi)外,從CFrameWnd派生的CSample01Frame類(lèi)還對(duì)應(yīng)用程序的主框架窗口做了描述。在構(gòu)造函數(shù)中調(diào)用基類(lèi)的CFrameWnd成員函數(shù)Create(),由Windows 負(fù)責(zé)創(chuàng)建出實(shí)際的窗口結(jié)構(gòu),并由應(yīng)用程序框架將其鏈接到C++對(duì)象。
本示例程序的大部分功能實(shí)際是在MFC的CWinApp和CFrameWnd等基類(lèi)中完成的,在編程時(shí),只需在派生類(lèi)中編寫(xiě)少量功能代碼,C++允許以這樣的方式借用基類(lèi)中的大量代碼而無(wú)須復(fù)制代碼。應(yīng)用程序框架負(fù)責(zé)提供程序的結(jié)構(gòu)框架,開(kāi)發(fā)人員在此基礎(chǔ)上為其添加相應(yīng)的實(shí)現(xiàn)代碼,從而可以非常方便地完成一個(gè)完整的應(yīng)用程序。應(yīng)用程序框架不僅定義了應(yīng)用程序的結(jié)構(gòu)安排,實(shí)際上還包含了更多的C++基類(lèi)。
4 小結(jié)
SDK的API編程方法、MFC的編程方法以及本系列講座后面將要介紹的ATL編程方法是VC++程序設(shè)計(jì)中比較常用的幾種編程方法,其中MFC以其強(qiáng)大的功能和靈活的編程方式而成為大多數(shù)程序開(kāi)發(fā)人員最經(jīng)常使用的一種編程方式。本文從基礎(chǔ)問(wèn)題入手對(duì)MFC及其框架程序做了較為詳細(xì)的論述,使讀者能夠?qū)FC編程有一個(gè)基本的認(rèn)識(shí)。
總結(jié)
以上是生活随笔為你收集整理的MFC应用程序框架入门的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 四种类型转换 cast
- 下一篇: H.264的NALU,RTP封包说明(转