深入解析MFC -- 句柄與對象的關系
CWnd::FromHandlePermanent ——根據窗口句柄得到CWnd*指針
This function, unlike FromHandle, does not create temporary objects.
CWnd::FromHandle——根據窗口句柄得到CWnd*指針
CWnd* PASCAL CWnd::FromHandle(HWND hWnd)
{
CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist
ASSERT(pMap != NULL);
CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);
#ifndef _AFX_NO_OCC_SUPPORT
pWnd->AttachControlSite(pMap);
#endif
ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
return pWnd;
}
CWnd* PASCAL CWnd::FromHandlePermanent(HWND hWnd)
{
CHandleMap* pMap = afxMapHWND(); //即,afxMapHWND(FALSE);
CWnd* pWnd = NULL;
if (pMap != NULL)
{
// only look in the permanent map - does no allocations
pWnd = (CWnd*)pMap->LookupPermanent(hWnd);
ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
}
return pWnd;
}
在Windows體系中,很多對象都是以句柄的形式展示給開發人員的。比如窗口句柄(HWND),繪圖設備(HDC)等等。然后大部分的API函數則圍繞 這些句柄做文章。比如ShowWindow,SetWindowText,TextOut等等。這些API函數的第一個參數通常就是句柄了。但是在C++ 體系中,這種對于事物細節的訪問,往往是有違其封裝精神的。因此MFC做了很多的封裝類,來隱藏這些細節。應運而生就是CWnd,CDC等類。通過這些類 暴露的方法,可以直接對句柄做操作,而又可以不去關心他。
不過,我們今天的主題不在這里。以下才是真正的內容。
按照C++的理論,被封裝的句柄的創建和銷毀都應該由類本身來完成,外界不了解其中細節。但是在MFC中,真是這樣嗎?至少CWnd不是這樣,HWND并 不完全在CWnd的掌控之中。在前一章我提到過,MFC中有大量的全局變量,其中一個全局變量是一張HWND與CWnd的Map表。這個Map表的位置不 好找,上章提到過全局對象AFX_MODULE_STATE,他其中有一個成員的類型是AFX_MODULE_THREAD_STATE,在 AFX_MODULE_THREAD_STATE的內部,則有著一群Map表,m_pmapHWND正是其中一個:
CHandleMap* m_pmapHMENU;
CHandleMap* m_pmapHDC;
CHandleMap* m_pmapHGDIOBJ;
CHandleMap* m_pmapHIMAGELIST;
看到了嗎?找起來是比較麻煩,不過MFC提供了全局函數afxMapHWND,無論在哪個地方,調用這個函數都能輕松的獲得這個Map表。
那么,這個表到底有什么作用呢?其實非常簡單,雖然在MFC中,都是對象在與對象打交道。但是MFC也要與Windows系統打交道。Windows給你的只有句柄,那么如何通過這些句柄找到相對應的類呢?通過Map表就能輕松的解決這個問題。
比如在Windows的消息機制中,當WndProc接收到一個消息的時候,只會得到一個HWND hWnd的目標窗口,如何找到匹配的類?從m_pmapHWND中搜索就行了。通過調用CWnd的靜態成員函數FromHandlePermanent,我們就能輕松的從Map表中找到與hWnd相對應的CWnd類。
FromHandlePermanent的實現也非常簡單。首先通過afxMapHWND找到m_pmapHWND,然后通過m_pmapHWND的成員函數LookupPermanent查找與hWnd對應的CWnd指針,最后返回他。
m_pmapHWND是什么時候被創建的呢?在第一個窗口被創建出來的時候,會調用CWnd的Attach函數,下面是具體的函數實現:
BOOL CWnd::Attach(HWND hWndNew)
{
if (hWndNew == NULL) return FALSE;
CHandleMap* pMap = afxMapHWND(TRUE); // create map if not exist
pMap->SetPermanent(m_hWnd = hWndNew, this);
return TRUE;
}
注意調用afxMapHWND的傳入的參數為TRUE,這表示當Map不存在時,則創建他。再讓我們看看afxMapHWND的實現:
CHandleMap* PASCAL afxMapHWND(BOOL bCreate)
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
if (pState->m_pmapHWND == NULL && bCreate)
{
pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CWnd),
ConstructDestruct<CWnd>::Construct, ConstructDestruct<CWnd>::Destruct,
offsetof(CWnd, m_hWnd));
}
return pState->m_pmapHWND;
}
afxMapHWND首先從我們前面說的地方找到這個指針,如果指針為空,并且bCreate為TRUE,則創建一個新的CHandleMap。非常簡單,不是嗎?RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成