一 FromHandle()
MFC 實際上是對內核對象HANDLE(如CDC的m_hDC,CWnd的m_hWnd)封裝了這個句柄有關的所有操作,一個類生成一個新對象的時候這個句柄是無效的,要獲得這個句柄,可以有兩個方法,一個是Create來創建,另一個就是用Attach來與一個已有的句柄建立關聯,實際上也就是給類的句柄
成員變量賦值。 而有些時候這個句柄不是由我們創建,但是我們要對它的
封裝類進行操作,(mfc
框架)必需創建對應的封裝類包裝它
MFC 中對各種包含
內核對象的封裝類都有FromHandle(HANDLE h)方法。FromHandle(HANDLE h) 先查找由用戶定義的
內核對象的封裝類, 如果找到直接返回,沒有找到構造一個臨時對象返回.
二 Fromhandle的內部機制
例如,你的程序中必然對你的主窗口Attach(這是由Framework完成的),這樣的話,假如你又得到了你程序的主窗口句柄hwndMain,你如果再調用FromHandle(hwndMain),它返回的將是你的App中的m_pMainWnd,原因就是FromHandle會維持一個內部的列表,紀錄每個hwnd與CWnd的關聯情況,如果一旦一個hwnd早已與某個CWnd對象相關連,它會返回該CWnd對象的指針。既然如此,FromHandle返回的便是m_pMainWnd,而此對象Framework會自動析構,因此你只是得到了該指針的一個副本,不能對其作析溝操作,否則會導致你的程序運行不正常。
考慮另外一種情況,就是一個hwnd與任何對象都沒有關聯(比如,你用API
CreateWindow新建了一個窗口),此時的hwnd尚未與任何CWnd對象關聯,如果你用FromHandle(hwnd),FromHandle便會臨時new一個CWnd對象,並Attatch到此hwnd,然后返回給你。我剛才說了,FromHandle會維持一個hwnd與CWnd關聯的列表,每當Framework OnIdle時,它便會檢查此列表,一旦發現某個CWnd是FromHandle臨時創建的對象,它便會首先Detach此對象,然后delete之。因此,你在程序中也不必delete從FromHandle得到的對象指針,但這種指針只在一次消息處理過程中有效。
考慮另外一種情況,就是一個hwnd與任何對象都沒有關聯(比如,你用API
CreateWindow新建了一個窗口),此時的hwnd尚未與任何CWnd對象關聯,如果你用FromHandle(hwnd),FromHandle便會臨時new一個CWnd對象,並Attatch到此hwnd,然后返回給你。我剛才說了,FromHandle會維持一個hwnd與CWnd關聯的列表,每當Framework OnIdle時,它便會檢查此列表,一旦發現某個CWnd是FromHandle臨時創建的對象,它便會首先Detach此對象,然后delete之。因此,你在程序中也不必delete從FromHandle得到的對象指針,但這種指針只在一次消息處理過程中有效。
三 與FromHandlePermanent()的區別
FromHandlePermanent函數,它當且僅當hwnd已與某個CWnd對象關聯時才返回此對象的指針,否則返回NULL。這也是它為什么叫Permanent——區別於FromHandle會new一個臨時的CWnd對象。 這兩個函數都是在公共的 CMapHandle 中查找句柄對應的 CWnd 對象(通過一個CBT鈎子,CWnd 對象將創建時得到的句柄和自己的指針紀錄到 CMapHandle),區別是如果找不到相關的對象,FromHandle 在CMapHandle 的 temporarylist 中創建並返回一個臨時對象的指針 ,而 FromHandlePermanent 返回 NULL(此外 FromHandlePermanent 不使用 temporarylist ,所以不查找 temporarylist 下的句柄)。四 使用注意
大部分情況下,對任意句柄使用 FromHandle 是不錯的,因為大多情況下只利用返回的 CWnd 指針調用的非虛函數,返回的即使是一個臨時對象,調用也是正確的(MFC 中較少用 FromHandlePermanent,除非確定句柄是由本線程創建的或不需要創建臨時對象)。
但是在某些情況下,比如從使用 MFC 的非 Extension DLL 中創建的窗口,這個機制會有問題,因為窗口創建在其他的 MFC 模塊之下,在 EXE 中調用 FromHandle,由於該模塊的 CMapHandle 對象某有相關的紀錄,所以只能得到臨時對象,如果使用返回的指針調用 CWnd 的虛函數如 PreTranslateMessage,得到調用的是 CWnd::PreTranslateMessage,而不是 DLL 中的 CWnd 派生類重載過的 CWnd::PreTranslateMessage。
但是在某些情況下,比如從使用 MFC 的非 Extension DLL 中創建的窗口,這個機制會有問題,因為窗口創建在其他的 MFC 模塊之下,在 EXE 中調用 FromHandle,由於該模塊的 CMapHandle 對象某有相關的紀錄,所以只能得到臨時對象,如果使用返回的指針調用 CWnd 的虛函數如 PreTranslateMessage,得到調用的是 CWnd::PreTranslateMessage,而不是 DLL 中的 CWnd 派生類重載過的 CWnd::PreTranslateMessage。
對於GDI對象,以上的分析也是適用的。
