映射窗口句柄對象
話題引入:在C語言時代,當人們使用純粹的C開發windows應用程序時,只需要通過窗口句柄HWND【HWND是WND的內存句柄,而WND是Windows是一個內部私有數據結構,存儲著窗口的相關信息:尺寸信息、標題信息等。一般來說開發人員無法訪問這個數據結構,不過可以以HWND為參數調用windows的API函數如:GetWindowRect、GetWindowText等獲得對其的訪問】即可完成有關窗口的操作。MFC類是C++類機制,MFC的類通過C++的對象方法將窗口的句柄封裝起來。存在著窗口句柄(Windows對象)到MFC中C++窗口對象(MFC對象)之間的映射機制,該機制保證了HWND與C++對象之間的雙向溝通,從而開辟了程序設計的一種全新模式。句柄映射機制主要包括兩個類句柄映射輔助類、模塊——線程狀態類。參見書《MFC窗口程序設計 姚領田,高守傳 中國水利水電出版社》第一章Windows窗口與Cwnd對象。
這個筆記描述了MFC的規則,這個規則支持窗口的對象句柄到C++對象的映射。
問題
窗口對象通常以HANDLEs的方式表示出來,MFC的類通過C++的對象方法將窗口的句柄封裝起來。MFC類庫的句柄封裝函數提供了一個通過特殊的句柄封裝窗口對象成C++對象的方法,很多時候窗口的對象并不具有一個C++的封裝對象,然而,很多時候一個臨時的對象會被建立起來來充當C++封裝對象的作用。
The Windows objects that use handle maps are:
使用句柄映射的窗口對象如下:
HWND (CWnd and CWnd-derived classes) /CWnd類封裝了HWND,存在窗口句柄HWND到MFC類對象CWnd之間的映射/
HDC (CDC and CDC-derived classes) /同理CDC封裝了HDC,存在/
HMENU (CMenu)
HPEN (CGdiObject)
HBRUSH (CGdiObject)
HFONT (CGdiObject)
HBITMAP (CGdiObject)
HPALETTE (CGdiObject)
HRGN (CGdiObject)
HIMAGELIST (CImageList)
SOCKET (CSocket)
HWND是WND的內存句柄,窗體句柄,而WND是Windows是一個內部私有數據結構,存儲著窗口的相關信息:尺寸信息、標題信息等。一般來說開發人員無法訪問這個數據結構,不過可以以HWND為參數調用windows的API函數如:GetWindowRect、GetWindowText等獲得對其的訪問
HDC是設備描述表(Device Context)的句柄。設備描述表中記錄和此設備相關的各種信息,比如對于顯示器來說,記錄了顯示器的尺寸、分辨率,還有當前選擇的畫筆、畫刷、字體等GDI對象的信息。 可以將HDC理解做一個設備的表面,比如顯示器的表明,打印機的表面等等,我們可以使用這個HDC在這些表明上繪制圖形——很多GDI繪圖函數,都需要使用這個HDC作為參數的。
HMENU是菜單句柄。HPEN是畫筆句柄。HBRUSH 畫刷句柄。HFONT 字體句柄。HBITMAP位圖句柄。HPALETTE 調色板句柄。HRGN 區域 (region)句柄。
通過給以上這些對象賦予句柄,你可以發現MFC的對象的封裝操作是通過調用一個靜態的成員函數 FromHandle來實現的。舉例,已知一個值為hWnd的HWND CWnd::FromHandle(hWnd)
將會返回一個指向CWnd的指針,這個指針封裝了hWnd。如果hWnd并沒有產生一個明確的對象,那么一個臨時的CWnd將被創建出來封裝hWnd。這使通過任何句柄獲得一個有效的C++對象變得可能。
一點你有了一個被封裝的對象,你就能通過一個共有成員變量獲得他的句柄。在CWnd這種情況下,m_hWnd包含這個對象的HWND句柄。
Attaching Handles to MFC Objects
已知一個新創建的已封裝的句柄對象和一個指向窗口對象的句柄,你可以將這兩者通過調用Attach聯系起來。例如:
CWnd myWnd;
myWnd.Attach(hWnd);
這會建立起一個項目,這個項目是永久性的關聯myWnd 和hWnd的一個映射。調用CWnd::FromHandle(hWnd) 將會返回一個指向myWnd的指針。當myWnd 被刪除后,析構函數會自動的通過窗口函數DestroyWindow 銷毀hWnd。如果你并不愿意這么做,那么hWnd 必須在myWnd 的對象被銷毀之前同myWnd 相分離。(通常離開myWnd 定義的作用域中)
成員函數Detach 做這些工作。
myWnd.Detach();
臨時對象
當FromHandle 被付給一個句柄的時候的時候并沒有一個已經封裝的C++對象,那么就會產生一個臨時的對象。這些臨時的對象是同他們的句柄相分離的,并且通過調用DeleteTempMap 函數進行刪除。默認狀況下OnIdle 時刻CWinThread 自動調用DeleteTempMap 來使每一個類都支持臨時的句柄映射。這就意味著你不能假定一個指向臨時對象的指針可以在獲得該指針的函數退出時依然保持有效,在Windows消息循環空閑時間這個臨時的對象就會被刪除。
封裝對象和多線程
臨時對象和永久對象都是每一個線程的基礎,那就是說,一個線程不能夠訪問其他線程的C++對象,無論是臨時對象還是永久的對象,正如以上所述,當線程進入OnIdle.狀態的時候臨時的對象就會被刪除。
在線程間傳遞這些對象通常使用他們自身的HANDLE 類型,在線程間傳遞C++封裝的對象經常會造成異常。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/sunnymov/archive/2010/05/24/5619699.aspx