青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

MFC和Win32

  1. MFC和Win32

     

    1. 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)可能不適用,但具體涉及到時會指出。

  1. 從數據結構上比較

     

    MFC Object是相應C++類的實例,這些類是MFC或者程序員定義的;

    Windows Object是Windows系統的內部結構,通過一個句柄來引用;

    MFC給這些類定義了一個成員變量來保存MFC Object對應的Windows Object的句柄。對于設備描述表CDC類,將保存兩個HDC句柄。

  2. 從層次上講比較

     

    MFC Object是高層的,Windows Object是低層的;

    MFC Object封裝了Windows Object的大部分或全部功能,MFC Object的使用者不需要直接應用Windows Object的HANDLE(句柄)使用Win32 API,代替它的是引用相應的MFC Object的成員函數。

  3. 從創建上比較

     

    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的創建是兩回事。

  4. 從轉換上比較

     

    可以從一個MFC Object得到對應的Windows Object的句柄;一般使用MFC Object的成員函數GetSafeHandle得到對應的句柄。

    可以從一個已存在的Windows Object創建一個對應的MFC Object; 一般使用MFC Object的成員函數Attach或者FromHandle來創建,前者得到一個永久性對象,后者得到的可能是一個臨時對象。

  5. 從使用范圍上比較

     

    MFC Object對系統的其他進程來說是不可見、不可用的;而Windows Object一旦創建,其句柄是整個Windows系統全局的。一些句柄可以被其他進程使用。典型地,一個進程可以獲得另一進程的窗口句柄,并給該窗口發送消息。

    對同一個進程的線程來說,只可以使用本線程創建的MFC Object,不能使用其他線程的MFC Object。

  6. 從銷毀上比較

     

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的對應關系

描述

Windows句柄

MFC Object

窗口

HWND

CWnd and CWnd-derived classes

設備上下文

HDC

CDC and CDC-derived classes

菜單

HMENU

CMenu

HPEN

CGdiObject類,CPen和CPen-derived classes

刷子

HBRUSH

CGdiObject類,CBrush和CBrush-derived classes

字體

HFONT

CGdiObject類,CFont和CFont-derived classes

位圖

HBITMAP

CGdiObject類,CBitmap和CBitmap-derived classes

調色板

HPALETTE

CGdiObject類,CPalette和CPalette-derived classes

區域

HRGN

CGdiObject類,CRgn和CRgn-derived classes

圖像列表

HimageLIST

CimageList和CimageList-derived classes

套接字

SOCKET

CSocket,CAsynSocket及其派生類

 

 


表2-1中的OBJECT分以下幾類:

 

Windows對象,

設備上下文對象,

GDI對象(BITMAP,BRUSH,FONT,PALETTE,PEN,RGN),

菜單,

圖像列表,

網絡套接字接口。

從廣義上來看,文檔對象和文件可以看作一對MFC Object和Windows Object,分別用CDocument類和文件句柄描述。

后續幾節分別對前四類作一個簡明扼要的論述。

    1. Windows Object

       

      用SDK的Win32 API編寫各種Windows應用程序,有其共同的規律:首先是編寫WinMain函數,編寫處理消息和事件的窗口過程WndProc,在WinMain里頭注冊窗口(Register Window),創建窗口,然后開始應用程序的消息循環。

      MFC應用程序也不例外,因為MFC是一個建立在SDK API基礎上的編程框架。對程序員來說所不同的是:一般情況下,MFC框架自動完成了Windows登記、創建等工作。

      下面,簡要介紹MFC Window對Windows Window的封裝。

      1. Windows的注冊

         

一個應用程序在創建某個類型的窗口前,必須首先注冊該“窗口類”(Windows Class)。注意,這里不是C++類的類。Register Window把窗口過程、窗口類型以及其他類型信息和要登記的窗口類關聯起來。

  1. “窗口類”的數據結構

     

    “窗口類”是Windows系統的數據結構,可以把它理解為Windows系統的類型定義,而Windows窗口則是相應“窗口類”的實例。Windows使用一個結構來描述“窗口類”,其定義如下:

    typedef struct _WNDCLASSEX {

    UINT cbSize; //該結構的字節數

    UINT style; //窗口類的風格

    WNDPROC lpfnWndProc; //窗口過程

    int cbClsExtra;

    int cbWndExtra;

    HANDLE hInstance; //該窗口類的窗口過程所屬的應用實例

    HICON hIcon; //該窗口類所用的像標

    HCURSOR hCursor; //該窗口類所用的光標

    HBRUSH hbrBackground; //該窗口類所用的背景刷

    LPCTSTR lpszMenuName; //該窗口類所用的菜單資源

    LPCTSTR lpszClassName; //該窗口類的名稱

    HICON hIconSm; //該窗口類所用的小像標

    } WNDCLASSEX;

    從“窗口類”的定義可以看出,它包含了一個窗口的重要信息,如窗口風格、窗口過程、顯示和繪制窗口所需要的信息,等等。關于窗口過程,將在后面消息映射等有關章節作詳細論述。

    Windows系統在初始化時,會注冊(Register)一些全局的“窗口類”,例如通用控制窗口類。應用程序在創建自己的窗口時,首先必須注冊自己的窗口類。在MFC環境下,有幾種方法可以用來注冊“窗口類”,下面分別予以討論。

  2. 調用AfxRegisterClass注冊

     

    AfxRegisterClass函數是MFC全局函數。AfxRegisterClass的函數原型:

    BOOL AFXAPI AfxRegisterClass(WNDCLASS *lpWndClass);

    參數lpWndClass是指向WNDCLASS結構的指針,表示一個“窗口類”。

    首先,AfxRegisterClass檢查希望注冊的“窗口類”是否已經注冊,如果是則表示已注冊,返回TRUE,否則,繼續處理。

    接著,調用::RegisterClass(lpWndClass)注冊窗口類;

    然后,如果當前模塊是DLL模塊,則把注冊“窗口類”的名字加入到模塊狀態的域m_szUnregisterList中。該域是一個固定長度的緩沖區,依次存放模塊注冊的“窗口類”的名字(每個名字是以“\n\0”結尾的字符串)。之所以這樣做,是為了DLL退出時能自動取消(Unregister)它注冊的窗口類。至于模塊狀態將在后面第9章詳細的討論。

    最后,返回TRUE表示成功注冊。

  3. 調用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節會指出。

  4. 隱含的使用MFC預定義的的窗口類

     

    MFC4.0以前的版本提供了一些預定義的窗口類,4.0以后不再預定義這些窗口類。但是,MFC仍然沿用了這些窗口類,例如:

    用于子窗口的“AfxWnd”;

    用于邊框窗口(SDI主窗口或MDI子窗口)或視的“AfxFrameOrView”;

    用于MDI主窗口的“AfxMDIFrame”;

    用于標準控制條的“AfxControlBar”。

    這些類的名字就 是“AfxWnd”、“AfxFrameOrView”、“AfxMdiFrame”、 “AfxControlBar”加上前綴和后綴(用來標識版本號或是否調試版等)。它們使用標準應用程序像標、標準文檔像標、標準光標等標準資源。為了使用這些“窗口類”,MFC會在適當的時候注冊這些類:或者要創建該類的窗口時,或者創建應用程序的主窗口時,等等。

    MFC內部使用了函數

    BOOL AFXAPI AfxEndDeferRegisterClass(short fClass)

    來幫助注冊上述原MFC版本的預定義“窗口類”。參數fClass區分了那些預定義窗口的類型。根據不同的類型,使用不同的窗口類風格、窗口類名字等填充WndClass的域,然后調用AfxRegisterClass注冊窗口類。并且注冊成功之后,通過模塊狀態的m_fRegisteredClasses記錄該窗口類已經注冊,這樣該模塊在再次需要注冊這些窗口類之前可以查一下m_fRegisteredClasses,如果已經注冊就不必浪費時間了。為此,MFC內部使用宏

    AfxDeferRegisterClass(short fClass)

    來注冊“窗口類”,如果m_fRegisteredClasses記錄了注冊的窗口類,返回TRUE,否則,調用AfxEndDeferRegisterClass注冊。

    注冊這些窗口類的例子:

    MFC在加載邊框窗口時,會自動地注冊“AfxFrameOrView”窗口類。在創建視時,就會使用該“窗口類”創建視窗口。當然,如果創建視窗口時,該“窗口類”還沒有注冊,MFC將先注冊它然后使用它創建視窗口。

    不過,MFC并不使用”AfxMDIFrame”來創建MDI主窗口,因為在加載主窗口時一般都指定了主窗口的資源,MFC使用指定的像標注冊新的MDI主窗口類(通過函數AfxRegisterWndClass完成,因此“窗口類”的名字是動態產生的)。

    MDI子窗口類似于上述MDI主窗口的處理。

    在MFC創建控制窗口時,如工具欄窗口,如果“AfxControlBar”類還沒有注冊,則注冊它。注冊過程很簡單,就是調用::InitCommonControl加載通用控制動態連接庫。

  5. 調用::RegisterWndClass。

     

    直接調用Win32的窗口注冊函數::RegisterWndClass注冊“窗口類”,這樣做有一個缺點:如果是DLL模塊,這樣注冊的“窗口類”在程序退出時不會自動的被取消注冊(Unregister)。所以必須記得在DLL模塊退出時取消它所注冊的窗口類。

  6. 子類化

     

子類化(Subclass)一個“窗口類”,可自動地得到它的“窗口類”屬性。

      1. MFC窗口類CWnd

         

在Windows系統里,一個窗口的屬性分兩個地方存放:一部分放在“窗口類”里頭,如上所述的在注冊窗口時指定;另一部分放在Windows Object本身,如:窗口的尺寸,窗口的位置(X,Y軸),窗口的Z軸順序,窗口的狀態(ACTIVE,MINIMIZED,MAXMIZED,RESTORED…),和其他窗口的關系(父窗口,子窗口…),窗口是否可以接收鍵盤或鼠標消息,等等。

為了表達所有這些窗口的共性,MFC設計了一個窗口基類CWnd。有一點非常重要,那就是CWnd提供了一個標準而通用的MFC窗口過程,MFC下所有的窗口都使用這個窗口過程。至于通用的窗口過程卻能為各個窗口實現不同的操作,那就是MFC消息映射機制的奧秘和作用了。這些,將在后面有關章節詳細論述。

CWnd提供了一系列成員函數,或者是對Win32相關函數的封裝,或者是CWnd新設計的一些函數。這些函數大致如下。

(1)窗口創建函數

這里主要討論函數Create和CreateEx。它們封裝了Win32窗口創建函數::CreateWindowEx。Create的原型如下:

BOOL CWnd::Create(LPCTSTR lpszClassName,

LPCTSTR lpszWindowName, DWORD dwStyle,

const RECT& rect,

CWnd* pParentWnd, UINT nID,

CCreateContext* pContext)

Create是一個虛擬函數,用來創建子窗口(不能創建桌面窗口和POP UP窗口)。CWnd的基類可以覆蓋該函數,例如邊框窗口類等覆蓋了該函數以實現邊框窗口的創建,視類則使用它來創建視窗口。

Create調用了成員函數CreateEx。CWnd::CreateEx的原型如下:

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)

CreateEx有11個參數,它將調用::CreateWindowEx完成窗口的創建,這11個參數對應地傳遞給::CreateWindowEx。參數指定了窗口擴展風格、“窗口類”、窗口名、窗口大小和位置、父窗口句柄、窗口菜單和窗口創建參數。

CreateEx的處理流程將在后面4.4.1節討論窗口過程時分析。

窗口創建時發送WM_CREATE消息,消息參數lParam指向一個CreateStruct結構的變量,該結構有11個域,其描述見后面4.4.1節對窗口過程的分析,Windows使用和CreateEx參數一樣的內容填充該變量。

(2)窗口銷毀函數

例如:

DestroyWindow函數 銷毀窗口

PostNcDestroy( ),銷毀窗口后調用,虛擬函數

(3)用于設定、獲取、改變窗口屬性的函數,例如:

SetWindowText(CString tiltle) 設置窗口標題

GetWindowText() 得到窗口標題

SetIcon(HICON hIcon, BOOL bBigIcon);設置窗口像標

GetIcon( BOOL bBigIcon ) ;得到窗口像標

GetDlgItem( int nID);得到窗口類指定ID的控制子窗口

GetDC(); 得到窗口的設備上下文

SetMenu(CMenu *pMenu); 設置窗口菜單

GetMenu();得到窗口菜單

(4)用于完成窗口動作的函數

用于更新窗口,滾動窗口,等等。一部分成員函數設計成或可重載(Overloaded)函數,或虛擬(Overridden)函數,或MFC消息處理函數。這些函數或者實現了一部分功能,或者僅僅是一個空函數。如:

  • 有關消息發送的函數:

     

SendMessage( UINT message,WPARAM wParam = 0, LPARAM lParam = 0 );

給窗口發送發送消息,立即調用方式

PostMessage(( UINT message,WPARAM wParam = 0, LPARAM lParam = 0 );

給窗口發送消息,放進消息隊列

  • 有關改變窗口狀態的函數

     

MoveWindow( LPCRECT lpRect, BOOL bRepaint = TRUE );

移動窗口到指定位置

ShowWindow(BOOL );顯示窗口,使之可見或不可見

….

  • 實現MFC消息處理機制的函數:

     

virtual LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam ); 窗口過程,虛擬函數

virtual BOOL OnCommand( WPARAM wParam, LPARAM lParam );處理命令消息

  • 消息處理函數:

     

OnCreate( LPCREATESTRUCT lpCreateStruct );MFC窗口消息處理函數,窗口創建時由MFC框架調用

OnClose();MFC窗口消息處理函數,窗口創建時由MFC框架調用

  • 其他功能的函數

     

CWnd的導出類是類型更具體、功能更完善的窗口類,它們繼承了CWnd的屬性和方法,并提供了新的成員函數(消息處理函數、虛擬函數、等等)。

常用的窗口類及其層次關系見圖1-1。

      1. 在MFC下創建一個窗口對象

         

MFC下創建一個窗口對象分兩步,首先創建MFC窗口對象,然后創建對應的Windows窗口。在內存使用上,MFC窗口對象可以在棧或者堆(使用new創建)中創建。具體表述如下:

  • 創建MFC窗口對象。通過定義一個CWnd或其派生類的實例變量或者動態創建一個MFC窗口的實例,前者在棧空間創建一個MFC窗口對象,后者在堆空間創建一個MFC窗口對象。

     

  • 調用相應的窗口創建函數,創建Windows窗口對象。

     

例如:在前面提到的AppWizard產生的源碼中,有CMainFrame(派生于CMDIFrame(SDI)或者CMDIFrameWnd(MDI))類。它有兩個成員變量定義如下:

CToolBar m_wndToolBar;

CStatusBar m_wndStatusBar;

當創建CMainFrame類對象時,上面兩個MFC Object也被構造。

CMainFrame還有一個成員函數

OnCreate(LPCREATESTRUCT lpCreateStruct),

它的實現包含如下一段代碼,調用CToolBar和CStatusBar的成員函數Create來創建上述兩個MFC對象對應的工具欄HWND窗口和狀態欄HWND窗口:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (!m_wndToolBar.Create(this) ||

!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

{

TRACE0("Failed to create toolbar\n");

return -1; // fail to create

}

if (!m_wndStatusBar.Create(this) ||

!m_wndStatusBar.SetIndicators(indicators,

sizeof(indicators)/sizeof(UINT)))

{

TRACE0("Failed to create status bar\n");

return -1; // fail to create

}

}

關于工具欄、狀態欄將在后續有關章節作詳細討論。

在MFC中,還提供了一種動態創建技術。動態創建的過程實際上也如上所述分兩步,只不過MFC使用這個技術是由框架自動地完成整個過程的。通常框架窗口、文檔框架窗口、視使用了動態創建。介于MFC的結構,CFrameWnd和CView及其派生類的實例即使不使用動態創建,也要用new在堆中分配。理由見窗口的銷毀(2.2.5節)。

至于動態創建技術,將在下一章具體討論。

在Windows窗口的創建過程中,將發送一些消息,如:

在創建了窗口的非客戶區(Nonclient area)之后,發送消息WM_NCCREATE;

在創建了窗口的客戶區(client area)之后,發送消息WM_CREATE;

窗口的窗口過程在窗口顯示之前收到這兩個消息。

如果是子窗口,在發送了上述兩個消息之后,還給父窗口發送WM_PARENATNOTIFY消息。其他類或風格的窗口可能發送更多的消息,具體參見SDK開發文檔。

      1. MFC窗口的使用

         

        MFC提供了大量的窗口類,其功能和用途各異。程序員應該選擇哪些類來使用,以及怎么使用他們呢?

        直接使用MFC提供的窗口類或者先從MFC窗口類派生一個新的C++類然后使用它,這些在通常情況下都不需要程序員提供窗口注冊的代碼。是否需要派生新的C++類,視MFC已有的窗口類是否能滿足使用要求而定。派生的C++類繼承了基類的特性并改變或擴展了它的功能,例如增加或者改變對消息、事件的特殊處理等。

        主要使用或繼承以下一些MFC窗口類(其層次關系圖見圖1-1):

        框架類CFrameWnd,CMdiFrameWnd;

        文檔框架CMdiChildWnd;

        視圖CView和CView派生的有特殊功能的視圖如:列表CListView,編輯CEditView,樹形列表CTreeView,支持RTF的CRichEditView,基于對話框的視CFormView等等。

        對話框CDialog。

        通常,都要從這些類派生應用程序的框架窗口和視窗口或者對話框。

        工具條CToolBar

        狀態條CStatusBar

        其他各類控制窗口,如列表框CList,編輯框CEdit,組合框CComboBox,按鈕Cbutton等。

        通常,直接使用這些類。

      2. 在MFC下窗口的銷毀

         

窗口對象使用完畢,應該銷毀。在MFC下,一個窗口對象的銷毀包括HWND窗口對象的銷毀和MFC窗口對象的銷毀。一般情況下,MFC編程框架自動地處理了這些。

(1)對CFrameWnd和CView的派生類

這些窗口的關閉導致銷毀窗口的函數DestroyWindow被調用。銷毀Windows窗口時,MFC框架調用的最后一個成員函數是OnNcDestroy函數,該函數負責Windows清理工作,并在最后調用虛擬成員函數PostNcDestroy。CFrameWnd和CView的PostNcDestroy調用delete this刪除自身這個MFC窗口對象。

所以,對這些窗口,如前所述,應在堆(Heap)中分配,而且,不要對這些對象使用delete操作。

(2)對Windows Control窗口

在它們的析構函數中,將調用DestroyWidnow來銷毀窗口。如果在棧中分配這樣的窗口對象,則在超出作用范圍的時候,隨著析構函數的調用,MFC窗口對象和它的Windows window對象都被銷毀。如果在堆(Heap)中分配,則顯式調用delete操作符,導致析構函數的調用和窗口的銷毀。

所以,這種類型的窗口應盡可能在棧中分配,避免用額外的代碼來銷毀窗口。如前所述的CMainFrame的成員變量m_wndStatusBar和m_wndToolBar就是這樣的例子。

(3)對于程序員直接從CWnd派生的窗口

程序員可以在派生類中實現上述兩種機制之一,然后,在相應的規范下使用。

后面章節將詳細的討論應用程序退出時關閉、清理窗口的過程。

    1. 設備描述表

       

      1. 設備描述表概述

         

當一個應用程序使用GDI函數時,必須先裝入特定的設備驅動程序,然后為繪制窗口準備設備描述表,比如指定線的寬度和顏色、刷子的樣式和顏色、字體、剪裁區域等等。不像其他Win32結構,設備描述表不能被直接訪問,只能通過系列Win32函數來間接地操作。

如同Windows“窗口類”一樣,設備描述表也是一種Windows數據結構,用來描述繪制窗口所需要的信息。它定義了一個坐標映射模式、一組GDI圖形對象及其屬性。這些GDI對象包括用于畫線的筆,繪圖、填圖的刷子,位圖,調色板,剪裁區域,及路徑(Path)。

表2-2列出了設備描述表的結構和各項缺省值,表2-3列出了設備描述表的類型,表2-4顯示設備描述表的類型。

表2-2 設備描述表的結構

屬性

缺省值

Background color

Background color setting from Windows Control Panel (typically, white)

Background mode

OPAQUE

Bitmap

None

Brush

WHITE_BRUSH

Brush origin

(0,0)

Clipping region

Entire window or client area with the update region clipped, as appropriate. Child and pop-up windows in the client area may also be clipped

Palette

DEFAULT_PALETTE

Current pen position

(0,0)

Device origin

Upper left corner of the window or the client area

Drawing mode

R2_COPYPEN

Font

SYSTEM_FONT (SYSTEM_FIXED_FONT for applications written to run with Windows versions 3.0 and earlier)

Intercharacter spacing

0

Mapping mode

MM_TEXT

Pen

BLACK_PEN

Polygon-fill mode

ALTERNATE

Stretch mode

BLACKONWHITE

Text color

Text color setting from Control Panel (typically, black)

Viewport extent

(1,1)

Viewport origin

(0,0)

Window extent

(1,1)

Window origin

(0,0)

 

表2-3 設備描述表的分類

Display

顯示設備描述表,提供對視頻顯示設備上的繪制操作的支持

Printer

打印設備描述表,提供對打印機、繪圖儀設備上的繪制操作的支持

Memory

內存設備描述表,提供對位圖操作的支持

Information

信息設備描述表,提供對操作設備信息獲取的支持

表2-3中的顯示設備描述表又分三種類型,如表2-4所示。

表2-4 顯示設備描述表的分類

名稱

特點

功能

Class Device

Contexts

提供對Win16的向后兼容

 

Common

Device

Contexts

在Windows系統的高速緩沖區,數量有限

Applicaion獲取設備描述表時,Windows用缺省值初始化該設備描述表,Application使用它完成繪制操作,然后釋放

Private

Device

Contexts

沒有數量限制,用完不需釋放一次獲取,多次使用

多次使用過程中,每次設備描述表屬性的任何修改或變化都會被保存,以支持快速繪制

 

(1)使用設備描述表的步驟

要使用設備描述表,一般有如下步驟:

  • 獲取或者創建設備描述表;

     

  • 必要的話,改變設備描述表的屬性;

     

  • 使用設備描述表完成繪制操作;

     

  • 釋放或刪除設備描述表。

     

Common設備描述表通過::GetDC,::GetDCEx,::BeginPaint來獲得一個設備描述表,用畢,用::ReleaseDC或::EndPaint釋放設備描述表;

Printer設備描述表通過::CreateDC創建設備描述表,用::DeleteDC刪除設備描述表。

Memory設備描述表通過::CreateCompatibleDC創建設備描述表,用::DeleteDC刪除。

Information設備描述表通過::CreateIC創建設備描述表,用::DeleteDC刪除。

(2)改變設備描述表屬性的途徑

要改變設備描述表的屬性,可通過以下途徑:

用::SelectObject選入新的除調色板以外的GDI Object到設備描述表中;

對于調色板,使用::SelectPalette函數選入邏輯調色板,并使用::RealizePalette把邏輯調色板的入口映射到物理調色板中。

用其他API函數改變其他屬性,如::SetMapMode改變映射模式。

      1. 設備描述表在MFC中的實現

         

MFC提供了CDC類作為設備描述表類的基類,它封裝了Windows的HDC設備描述表對象和相關函數。

  1. CDC類

     

    CDC類包含了各種類型的Windows設備描述表的全部功能,封裝了所有的Win32 GDI 函數和設備描述表相關的SDK函數。在MFC下,使用CDC的成員函數來完成所有的窗口繪制工作。

    CDC 類的結構示意圖2-2所示。

    CDC類有兩個成員變量:m_hDC,m_hAttribDC,它們都是Windows設備描述表句柄。CDC的成員函數作輸出操作時,使用m_Hdc;要獲取設備描述表的屬性時,使用m_hAttribDC。

    在創建一個CDC類實例時,缺省的m_hDC等于m_hAttribDC。如果需要的話,程序員可以分別指定它們。例如,MFC框架實現CMetaFileDC類時,就是如此:CMetaFileDC從物理設備上讀取設備信息,輸出則送到元文件(metafile)上,所以m_hDC和m_hAttribDC是不同的,各司其責。還有一個類似的例子:打印預覽的實現,一個代表打印機模擬輸出,一個代表屏幕顯示。

    CDC封裝::SelectObject(HDC hdc,HGDIOBJECT hgdiobject)函數時,采用了重載技術,即它針對不同的GDI對象,提供了名同而參數不同的成員函數:

    SelectObject(CPen *pen)用于選入筆;

    SelectObject(CBitmap* pBitmap)用于選入位圖;

    SelectObject(CRgn *pRgn)用于選入剪裁區域;

    SelectObject(CBrush *pBrush)用于選入刷子;

    SelectObject(CFont *pFont)用于選入字體;

    至于調色板,使用SelectPalette(CPalette *pPalette,BOOL bForceBackground )選入調色板到設備描述表,使用RealizePalletter()實現邏輯調色板到物理調色板的映射。

  2. 從CDC派生出功能更具體的設備描述表

     

從CDC 派生出四個功能更具體的設備描述表類。層次如圖2-3所示。

下面,分別討論派生出的四種設備描述表。

  • CCientDC

     

代表窗口客戶區的設備描述表。其構造函數CClientDC(CWnd *pWin)通過::GetDC獲取指定窗口的客戶區的設備描述表HDC,并且使用成員函數Attach把它和CClientDC對象捆綁在一起;其析構函數使用成員函數Detach把設備描述表句柄HDC分離出來,并調用::ReleaseDC釋放設備描述表HDC。

  • CPaintDC

     

僅僅用于響應WM_PAINT消息時繪制窗口,因為它的構造函數調用了::BeginPaint獲取設備描述表HDC,并且使用成員函數Attach把它和CPaintDC對象捆綁在一起;析構函數使用成員函數Detach把設備描述表句柄HDC分離出來,并調用::EndPaint釋放設備描述表HDC,而::BeginPaint和::EndPaint僅僅在響應WM_PAINT時使用。

  • CMetaFileDC

     

用于生成元文件。

  • CWindowDC

     

代表整個窗口區(包括非客戶區)的設備描述表。其構造函數CWindowDC(CWnd *pWin)通過::GetWindowDC獲取指定窗口的客戶區的設備描述表HDC,并使用Attach把它和CWindowDC對象捆綁在一起;其析構函數使用Detach把設備描述表HDC分離出來,調用::ReleaseDC釋放設備描述表HDC。

      1. MFC設備描述表類的使用

         

  1. 使用CPaintDC、CClientDC、CWindowDC的方法

     

    首先,定義一個這些類的實例變量,通常在棧中定義。然后,使用它。

    例如,MFC中CView對WM_PAINT消息的實現方法如下:

    void CView::OnPaint()

    {

    // standard paint routine

    CPaintDC dc(this);

    OnPrepareDC(&dc);

    OnDraw(&dc);

    }

    在棧中定義了CPaintDC類型的變量dc,隨著構造函數的調用獲取了設備描述表;設備描述表使用完畢,超出其有效范圍就被自動地清除,隨著析構函數的調用,其獲取的設備描述表被釋放。

    如果希望在堆中創建,例如

    CPaintDC *pDC;

    pDC = new CPaintDC(this)

    則在使用完畢時,用delete刪除pDC:

    delete pDC;

  2. 直接使用CDC

     

需要注意的是:在生成CDC對象的時候,并不像它的派生類那樣,在構造函數里獲取相應的Windows設備描述表。最好不要使用::GetDC等函數來獲取一個設備描述表,而是創建一個設備描述表。其構造函數如下:

CDC::CDC()

{

m_hDC = NULL;

m_hAttribDC = NULL;

m_bPrinting = FALSE;

}

其析構函數如下:

CDC::~CDC()

{

if (m_hDC != NULL)

::DeleteDC(Detach());

}

在CDC析構函數中,如果設備描述表句柄不空,則調用DeleteDC刪除它。這是直接使用CDC時最好創建Windows設備描述表的理由。如果設備描述表不是創建的,則應該在析構函數被調用前分離出設備描述表句柄并用::RealeaseDC釋放它,釋放后m_hDC為空,則在析構函數調用時不會執行::DeleteDC。當然,不用擔心CDC的派生類的析構函數調用CDC的析構函數,因為CDC::~CDC()不是虛擬析構函數。

直接使用CDC的例子是內存設備上下文,例如:

CDC dcMem; //聲明一個CDC對象

dcMem.CreateCompatibleDC(&dc); //創建設備描述表

pbmOld = dcMem.SelectObject(&m_bmBall);//更改設備描述表屬性

…//作一些繪制操作

dcMem.SelectObject(pbmOld);//恢復設備描述表的屬性

dcMem.DeleteDC(); //可以不調用,而讓析構函數去刪除設備描述表

    1. GDI對象

       

在討論設備描述表時,已經多次涉及到GDI對象。這里,需強調一下:GDI對象要選入Windows 設備描述表后才能使用;用畢,要恢復設備描述表的原GDI對象,并刪除該GDI對象。

一般按如下步驟使用GDI對象:

Create or get a GDI OBJECT hNewGdi;

hOldGdi = ::SelectObject(hdc, hNewGdi)

……

::SelectObject(hdc, hOldGdi)

::DeleteObject(hNewGdi)

先創建或得到一個GDI對象,然后把它選入設備描述表并保存它原來的GDI對象;用畢恢復設備描述表原來的GDI對象并刪除新創建的GDI對象。

需要指出的是,如果hNewGdi是一個Stock GDI對象,可以不刪除(刪除也可以)。通過

HGDIOBJ GetStockObject(

int fnObject // type of stock object

);

來獲取Stock GDI對象。

  1. MFC GDI對象

     

    MFC用一些類封裝了Windows GDI對象和相關函數,層次結構如圖2-4所示:

    CGdiObject封裝了Windows GDI Object共有的特性。其派生類在繼承的基礎上,主要封裝了各類GDI的創建函數以及和具體GDI對象相關的操作。

    CGdiObject的構造函數僅僅讓m_hObject為空。如果m_hObject不空,其析構函數將刪除對應的Windows GDI對象。MFC GDI對象和Windows GDI對象的關系如圖2-5所示。

  2. 使用MFC GDI類的使用

     

首先創建GDI對象,可分一步或兩步創建。一步創建就是構造MFC對象和Windows GDI對象一步完成;兩步創建則先構造MFC對象,接著創建Windows GDI對象。然后,把新創建的GDI對象選進設備描述表,取代原GDI對象并保存。最后,恢復原GDI對象。例如:

void CMyView::OnDraw(CDC *pDC)

{

CPen penBlack; //構造MFC CPen對象

if (penBlack.CreatePen(PS_SOLID, RGB(0, 0, 0)))

{

CPen *pOldPen = pDC->SelectObject(&penBlack)); //選進設備表,保存原筆

pDC->SelectObject(pOldPen); //恢復原筆

}else

{

}

}

和在SDK下有一點不同的是:這里沒有DeleteObject。因為執行完OnDraw后,棧中的penBlack被銷毀,它的析構函數被調用,導致DeleteObject的調用。

還有一點要說明:

pDC->SelectObject(&penBlack)返回了一個CPen *指針,也就是說,它根據原來PEN的句柄創建了一個MFC CPen對象。這個對象是否需要刪除呢?不必要,因為它是一個臨時對象,MFC框架會自動地刪除它。當然,在本函數執行完畢把控制權返回給主消息循環之前,該對象是有效的。

關于臨時對象及MFC處理它們的內部機制,將在后續章節詳細討論。

至此,Windows編程的核心概念:窗口、GDI界面(設備描述表、GDI對象等)已經陳述清楚,特別揭示了MFC對這些概念的封裝機制,并簡明講述了與這些Windows Object對應的MFC類的使用方法。還有其他Windows概念,可以參見SDK開發文檔。在MFC的實現上,基本上僅僅是對和這些概念相關的Win32函數的封裝。如果明白了MFC的窗口、GDI界面的封裝機制,其他就不難了。

posted on 2008-11-01 15:42 wrh 閱讀(1346) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


導航

<2008年5月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

統計

常用鏈接

留言簿(19)

隨筆檔案

文章檔案

收藏夾

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲视频一二区| 日韩亚洲在线| 免费在线欧美黄色| 欧美成人高清| 欧美性大战xxxxx久久久| 欧美三日本三级少妇三99| 国产精品一区二区久久| 国产综合色产| 91久久精品国产91性色| 亚洲专区一区| 欧美不卡三区| 亚洲视频大全| 久久久精品国产99久久精品芒果| 麻豆精品精华液| 欧美日韩中文| 伊人久久综合| 亚洲欧美日韩综合国产aⅴ | 午夜久久久久| 美女主播一区| 中日韩在线视频| 久久综合狠狠综合久久激情| 国产精品porn| 亚洲精品久久久久久久久久久| 亚洲一区二区三区午夜| 免费成人在线视频网站| 中国av一区| 免费美女久久99| 国产午夜亚洲精品羞羞网站| 99国产精品视频免费观看| 久久精品欧美日韩| 野花国产精品入口| 欧美成黄导航| 欲色影视综合吧| 久久久www成人免费毛片麻豆| 亚洲精品欧美激情| 麻豆精品91| 黑人巨大精品欧美一区二区 | 久久亚洲综合色| 99精品国产在热久久下载| 久久国产一区二区三区| 欧美视频一区二区三区四区| 一区二区三区在线观看欧美| 亚洲一区二区三区在线播放| 欧美成人一区二区三区在线观看 | 一本久道久久综合婷婷鲸鱼| 久久久久久久久久码影片| 欧美性片在线观看| 亚洲激情视频在线播放| 韩国精品在线观看| 一区二区三区精品久久久| 噜噜噜91成人网| 午夜伦理片一区| 欧美日韩综合一区| 亚洲人成网站在线观看播放| 久久久亚洲人| 午夜精品久久久久久久久久久久久| 欧美理论大片| 亚洲激情影院| 欧美不卡一区| 久久夜色精品国产噜噜av| 国产一区二区黄| 欧美中文字幕在线观看| 宅男噜噜噜66一区二区66| 欧美精品一区二区三区一线天视频| 国产一区二区三区网站| 性久久久久久久久久久久| 一本久道久久综合婷婷鲸鱼| 欧美日韩免费观看一区| 亚洲毛片av| 亚洲伦理精品| 欧美日本免费| 亚洲一区二区精品| 一区二区三区四区国产精品| 欧美午夜一区二区| 午夜精品999| 亚洲影院色无极综合| 国产亚洲综合性久久久影院| 久久久久一区二区三区| 久久久久久久一区| 黄色成人免费网站| 欧美~级网站不卡| 欧美肥婆在线| 亚洲欧美国产精品桃花| 亚洲欧美一区二区激情| 国产一区二区三区不卡在线观看| 久久久综合精品| 老司机亚洲精品| 一区二区久久久久久| 亚洲一区中文字幕在线观看| 国产亚洲欧美一级| 亚洲高清一区二区三区| 欧美a级大片| 亚洲影院色无极综合| 性欧美大战久久久久久久免费观看| 激情成人在线视频| 亚洲毛片一区二区| 国产喷白浆一区二区三区| 欧美电影在线免费观看网站| 欧美日韩亚洲一区二区| 久久精品国产清高在天天线| 免费视频亚洲| 性娇小13――14欧美| 美女久久网站| 午夜精品国产| 欧美国产第一页| 性视频1819p久久| 久久中文精品| 久久久久久网址| 欧美午夜剧场| 国内精品免费午夜毛片| 午夜伦理片一区| 免费在线视频一区| 久久国产精品久久久久久| 欧美裸体一区二区三区| 久久久久久一区二区三区| 欧美片在线观看| 久久激情网站| 国产精品高精视频免费| 牛牛精品成人免费视频| 国产美女精品| 99视频精品全国免费| 亚洲激情综合| 久久av最新网址| 亚洲资源在线观看| 久久综合久久综合这里只有精品| 久久国产日韩欧美| 国产精品嫩草影院av蜜臀| 亚洲激情视频网站| 亚洲欧洲日韩在线| 久久男女视频| 久久天堂成人| 国产偷久久久精品专区| 亚洲欧美日产图| 亚洲影院在线| 国产精品国产馆在线真实露脸| 亚洲激情一区二区| 亚洲精品国产视频| 嫩草伊人久久精品少妇av杨幂| 久久综合给合久久狠狠色| 国产一区二区三区丝袜 | 免费看亚洲片| 欧美成人dvd在线视频| 亚洲二区视频| 欧美成人综合网站| 亚洲国产精品久久久久| 亚洲区一区二区三区| 久久久久久久一区二区| 久久久综合网站| 亚洲国产经典视频| 女同性一区二区三区人了人一| 欧美阿v一级看视频| 亚洲精品一区二区三区不| 欧美激情偷拍| 夜夜嗨av一区二区三区四区| 亚洲永久免费av| 国产日韩成人精品| 久久五月婷婷丁香社区| 亚洲国产91精品在线观看| 欧美一区二区三区喷汁尤物| 久久婷婷av| 亚洲第一精品在线| 欧美福利电影在线观看| 99国产精品久久久久久久成人热| 亚洲精品一区二区三区婷婷月| 欧美成人首页| 在线天堂一区av电影| 久久精品理论片| 亚洲区中文字幕| 国产精品日韩欧美一区| 久久久久国内| 99视频有精品| 久久免费的精品国产v∧| 91久久精品国产91久久| 欧美日韩国产页| 亚欧成人在线| 欧美高清视频免费观看| 亚洲欧美中文另类| 亚洲第一精品久久忘忧草社区| 欧美一区二区三区视频免费播放 | 欧美一级片在线播放| 欧美r片在线| 一本到12不卡视频在线dvd| 国产精品一卡二| 免费在线观看精品| 亚洲图片你懂的| 欧美xx视频| 亚洲欧美日韩一区二区三区在线观看 | 亚洲伦理精品| 国产欧美日韩综合一区在线播放| 久热精品在线视频| 亚洲一区二区三区四区视频| 欧美成人精品在线观看| 在线一区亚洲| 在线看片一区| 国产欧美精品在线播放| 欧美片在线观看| 久久琪琪电影院| 亚洲欧美日本精品| 一本色道久久综合精品竹菊 | 久久精品国产精品亚洲精品|