MFC Object和Windows Object的關系
MFC中最重要的封裝是對Win32 API的封裝,因此,理解Windows Object和MFC Object (C++對象,一個C++類的實例)之間的關系是理解MFC的關鍵之一。所謂Windows Object(Windows對象)是Win32下用句柄表示的Windows操作系統對象;所謂MFC Object (MFC對象)是C++對象,是一個C++類的實例,這里(本書范圍內)MFC Object是有特定含義的,指封裝Windows Object的C++ Object,并非指任意的C++ Object。
MFC Object 和Windows Object是不一樣的,但兩者緊密聯系。以窗口對象為例:
一個MFC窗口對象是一個C++ CWnd類(或派生類)的實例,是程序直接創建的。在程序執行中它隨著窗口類構造函數的調用而生成,隨著析構函數的調用而消失。而Windows窗口則是Windows系統的一個內部數據結構的實例,由一個“窗口句柄”標識,Windows系統創建它并給它分配系統資源。Windows窗口在MFC窗口對象創建之后,由CWnd類的Create成員函數創建,“窗口句柄”保存在窗口對象的m_hWnd成員變量中。Windows窗口可以被一個程序銷毀,也可以被用戶的動作銷毀。MFC窗口對象和Windows窗口對象的關系如圖2-1所示。其他的Windows Object和對應的MFC Object也有類似的關系。
下面,對MFC Object和Windows Object作一個比較。有些論斷對設備描述表(MFC類是CDC,句柄是HDC)可能不適用,但具體涉及到時會指出。
從數據結構上比較
MFC Object是相應C++類的實例,這些類是MFC或者程序員定義的;
Windows Object是Windows系統的內部結構,通過一個句柄來引用;
MFC給這些類定義了一個成員變量來保存MFC Object對應的Windows Object的句柄。對于設備描述表CDC類,將保存兩個HDC句柄。
從層次上講比較
MFC Object是高層的,Windows Object是低層的;
MFC Object封裝了Windows Object的大部分或全部功能,MFC Object的使用者不需要直接應用Windows Object的HANDLE(句柄)使用Win32 API,代替它的是引用相應的MFC Object的成員函數。
從創建上比較
MFC Object通過構造函數由程序直接創建;Windows Object由相應的SDK函數創建。
MFC中,使用這些MFC Object,一般分兩步:
首先,創建一個MFC Object,或者在STACK中創建,或者在HEAP中創建,這時,MFC Object的句柄實例變量為空,或者說不是一個有效的句柄。
然后,調用MFC Object的成員函數創建相應的Windows Object,MFC的句柄變量存儲一個有效句柄。
CDC(設備描述表類)的創建有所不同,在后面的2.3節會具體說明CDC及其派生類的創建和使用。
當然,可以在MFC Object的構造函數中創建相應的Windows對象,MFC的GDI類就是如此實現的,但從實質上講,MFC Object的創建和Windows Object的創建是兩回事。
從轉換上比較
可以從一個MFC Object得到對應的Windows Object的句柄;一般使用MFC Object的成員函數GetSafeHandle得到對應的句柄。
可以從一個已存在的Windows Object創建一個對應的MFC Object; 一般使用MFC Object的成員函數Attach或者FromHandle來創建,前者得到一個永久性對象,后者得到的可能是一個臨時對象。
從使用范圍上比較
MFC Object對系統的其他進程來說是不可見、不可用的;而Windows Object一旦創建,其句柄是整個Windows系統全局的。一些句柄可以被其他進程使用。典型地,一個進程可以獲得另一進程的窗口句柄,并給該窗口發送消息。
對同一個進程的線程來說,只可以使用本線程創建的MFC Object,不能使用其他線程的MFC Object。
從銷毀上比較
MFC Object隨著析構函數的調用而消失;但Windows Object必須由相應的Windows系統函數銷毀。
設備描述表CDC類的對象有所不同,它對應的HDC句柄對象可能不是被銷毀,而是被釋放。
當然,可以在MFC Object的析構函數中完成Windows Object的銷毀,MFC Object的GDI類等就是如此實現的,但是,應該看到:兩者的銷毀是不同的。
每類Windows Object都有對應的MFC Object,下面用表格的形式列出它們之間的對應關系,如表2-1所示:
表2-1 MFC Object和Windows Object的對應關系
表2-1中的OBJECT分以下幾類:
描述Windows句柄MFC Object
窗口HWNDCWnd and CWnd-derived classes
設備上下文HDCCDC and CDC-derived classes
菜單HMENUCMenu
筆HPENCGdiObject類,CPen和CPen-derived classes
刷子HBRUSHCGdiObject類,CBrush和CBrush-derived classes
字體HFONTCGdiObject類,CFont和CFont-derived classes
位圖HBITMAPCGdiObject類,CBitmap和CBitmap-derived classes
調色板HPALETTECGdiObject類,CPalette和CPalette-derived classes
區域HRGNCGdiObject類,CRgn和CRgn-derived classes
圖像列表HimageLISTCimageList和CimageList-derived classes
套接字SOCKETCSocket,CAsynSocket及其派生類
Windows對象,
設備上下文對象,
GDI對象(BITMAP,BRUSH,FONT,PALETTE,PEN,RGN),
菜單,
圖像列表,
從廣義上來看,文檔對象和文件可以看作一對MFC Object和Windows Object,分別用CDocument類和文件句柄描述。
后續幾節分別對前四類作一個簡明扼要的論述。
Windows Object
用SDK的Win32 API編寫各種Windows應用程序,有其共同的規律:首先是編寫WinMain函數,編寫處理消息和事件的窗口過程WndProc,在WinMain里頭注冊窗口(Register Window),創建窗口,然后開始應用程序的消息循環。
MFC應用程序也不例外,因為MFC是一個建立在SDK API基礎上的編程框架。對程序員來說所不同的是:一般情況下,MFC框架自動完成了Windows登記、創建等工作。
下面,簡要介紹MFC Window對Windows Window的封裝。
Windows的注冊
一個應用程序在創建某個類型的窗口前,必須首先注冊該“窗口類”(Windows Class)。注意,這里不是C++類的類。Register Window把窗口過程、窗口類型以及其他類型信息和要登記的窗口類關聯起來。
“窗口類”的數據結構
“窗口類”是Windows系統的數據結構,可以把它理解為Windows系統的類型定義,而Windows窗口則是相應“窗口類”的實例。Windows使用一個結構來描述“窗口類”,其定義如下:
typedef struct _WNDCLASSEX {
UINT cbSize; //該結構的字節數
UINT style; //窗口類的風格
WNDPROC lpfnWndProc; //窗口過程
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance; //該窗口類的窗口過程所屬的應用實例
HICON hIcon; //該窗口類所用的像標
HBRUSH hbrBackground; //該窗口類所用的背景刷
LPCTSTR lpszMenuName; //該窗口類所用的菜單資源
LPCTSTR lpszClassName; //該窗口類的名稱
HICON hIconSm; //該窗口類所用的小像標
} WNDCLASSEX;
從“窗口類”的定義可以看出,它包含了一個窗口的重要信息,如窗口風格、窗口過程、顯示和繪制窗口所需要的信息,等等。關于窗口過程,將在后面消息映射等有關章節作詳細論述。
Windows系統在初始化時,會注冊(Register)一些全局的“窗口類”,例如通用控制窗口類。應用程序在創建自己的窗口時,首先必須注冊自己的窗口類。在MFC環境下,有幾種方法可以用來注冊“窗口類”,下面分別予以討論。
調用AfxRegisterClass注冊
AfxRegisterClass函數是MFC全局函數。AfxRegisterClass的函數原型:
BOOL AFXAPI AfxRegisterClass(WNDCLASS *lpWndClass);
參數lpWndClass是指向WNDCLASS結構的指針,表示一個“窗口類”。
首先,AfxRegisterClass檢查希望注冊的“窗口類”是否已經注冊,如果是則表示已注冊,返回TRUE,否則,繼續處理。
接著,調用::RegisterClass(lpWndClass)注冊窗口類;
然后,如果當前模塊是DLL模塊,則把注冊“窗口類”的名字加入到模塊狀態的域m_szUnregisterList中。該域是一個固定長度的緩沖區,依次存放模塊注冊的“窗口類”的名字(每個名字是以“\n\0”結尾的字符串)。之所以這樣做,是為了DLL退出時能自動取消(Unregister)它注冊的窗口類。至于模塊狀態將在后面第9章詳細的討論。
最后,返回TRUE表示成功注冊。
調用AfxRegisterWndClass注冊
AfxRegisterWndClass函數也是MFC全局函數。AfxRegisterWndClass的函數原型:
LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle,
HCURSOR hCursor, HBRUSH hbrBackground, HICON hIcon)
參數1指定窗口類風格;
參數2、3、4分別指定該窗口類使用的光標、背景刷、像標的句柄,缺省值是0。
此函數根據窗口類屬性動態地產生窗口類的名字,然后,判斷是否該類已經注冊,是則返回窗口類名;否則用指定窗口類的屬性(窗口過程指定為缺省窗口過程),調用AfxRegisterCalss注冊窗口類,返回類名。
動態產生的窗口類名字由以下幾部分組成(包括冒號分隔符):
如果參數2、3、4全部為NULL,則由三部分組成。
“Afx”+“:”+模塊實例句柄”+“:”+“窗口類風格”
否則,由六部分組成:
“Afx”+“:”+模塊實例句柄+“:”+“窗口類風格”+“:”+光標句柄+“:”+背景刷句柄+“:”+像標句柄。比如:“Afx:400000:b:13de:6:32cf”。
該函數在MFC注冊主邊框或者文檔邊框“窗口類”時被調用。具體怎樣用在5.3.3.3節會指出。
隱含的使用MFC預定義的的窗口類
MFC4.0以前的版本提供了一些預定義的窗口類,4.0以后不再預定義這些窗口類。但是,MFC仍然沿用了這些窗口類,例如:
用于子窗口的“AfxWnd”;
用于邊框窗口(SDI主窗口或MDI子窗口)或視的“AfxFrameOrView”;
用于MDI主窗口的“AfxMDIFrame”;
用于標準控制條的“AfxControlBar”。
這些類的名字就 是“AfxWnd”、“AfxFrameOrView”、“AfxMdiFrame”、 “AfxControlBar”加上前綴和后綴(用來標識版本號或是否調試版等)。它們使用標準應用程序像標、標準文檔像標、標準光標等標準資源。為了使用這些“窗口類”,MFC會在適當的時候注冊這些類:或者要創建該類的窗口時,或者創建應用程序的主窗口時,等等。
評論
查看更多