CWnd類與Windows視窗的關係-3、CWnd類如何封裝Windows視窗

來源:互聯網
上載者:User

現在,可以比較深入地對CWnd類的封裝機制進行剖析了。

在建立視窗控制代碼映射方面,CWnd使用了一個未公開的類CHandleMap進行管理。使用CWnd及衍生類別建立視窗時,建立了控制代碼映射,在視窗銷毀時刪除映射。一個在MFC內部建立的CHandleMap對象管理所有CWnd執行個體與視窗控制代碼的映射,該對象通過一個內部使用的全域函數afxMapHWND()建立並取得。

6.3.1  使用操作映射的函數
CHandleMap主要包括3個成員函數:SetPermanent()(建立映射)、RemoveHandle()(刪除映射)、LookupPermanent()(在映射中尋找與指定控制代碼對應的對象指標)。下面代碼是操作控制代碼映射的3個CWnd成員函數:

//根據指定的視窗控制代碼,在映射中尋找對應的CWnd對象指標

CWnd* PASCAL CWnd::FromHandlePermanent(HWND hWnd)

{                 //取得映射管理對象

         CHandleMap* pMap = afxMapHWND();

         CWnd* pWnd = NULL;

         if (pMap != NULL)

         {        //在映射中尋找視窗對象

                   pWnd = (CWnd*)pMap->LookupPermanent(hWnd);

                   ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);

         }

         return pWnd;

}

/*根據指定的視窗控制代碼,首先在映射中尋找對應的CWnd對象指標,如果在映射中尋找失敗,則建立一個臨時的CWnd對象,與控制代碼關聯,再將對象指標返回。如果不鎖定臨時對象,在空閑時將自動刪除之。*/

CWnd* PASCAL CWnd::FromHandle(HWND hWnd)

{

//參數為TRUE,如果映射對象沒有建立,則自動建立

         CHandleMap* pMap = afxMapHWND(TRUE);

         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對象與指定控制代碼的映射

BOOL CWnd::Attach(HWND hWndNew)

{

//如果建立映射,保證二者是一一對應的

         ASSERT(m_hWnd == NULL);  // 對象是否已建立映射

         ASSERT(FromHandlePermanent(hWndNew) == NULL);//控制代碼是否已建立映射

         if (hWndNew == NULL)

                   return FALSE;

//參數為TRUE,如果映射對象沒有建立,則自動建立

         CHandleMap* pMap = afxMapHWND(TRUE);

         ASSERT(pMap != NULL);

//建立映射關係

         pMap->SetPermanent(m_hWnd = hWndNew, this);

#ifndef _AFX_NO_OCC_SUPPORT

         AttachControlSite(pMap);

#endif

         return TRUE;

}

 

CWnd myWnd;myWnd.Attach(hWnd);

這會建立起一個項目,這個項目是永久性的關聯myWnd hWnd的一個映射。調用CWnd::FromHandle(hWnd)
將會返回一個指向myWnd的指標。當myWnd
被刪除後,解構函式會自動的通過視窗函數DestroyWindow 銷毀
hWnd。如果你並不願意這麼做,那麼hWnd
必須在myWnd 的對象被銷毀之前同myWnd
相分離。(通常離開
myWnd 定義的範圍中)
成員函數Detach 做這些工作。

myWnd.Detach();

 

//刪除當前CWnd對象已建立的控制代碼映射

HWND CWnd::Detach()

{

         HWND hWnd = m_hWnd;

         if (hWnd != NULL)

         {                 CHandleMap* pMap = afxMapHWND();

                   if (pMap != NULL)

                   //刪除映射

                            pMap->RemoveHandle(m_hWnd);

         m_hWnd = NULL;

         }

#ifndef _AFX_NO_OCC_SUPPORT

         m_pCtrlSite = NULL;

#endif

         return hWnd;

}

6.3.2  CWnd如何處理視窗訊息
在視窗訊息處理方面,CWnd使用了視窗子類化和訊息映射機制,關於訊息映射的知識將在第9章詳述,下面著重闡述CWnd是如何應用子類化處理視窗訊息的。其實,在6.2節的樣本中,CBaseWnd已經使用了與CWnd類似的子類化方法處理視窗訊息。成員函數CBaseWnd::SubWindowClass()將視窗過程子類化為CBaseWnd::MyBaseWndProc(),在這個視窗過程中調用CBaseWnd::WindowProc()處理視窗訊息。原始的視窗過程存入成員m_Super WndProc中,在預設處理中調用。

CWnd在視窗建立之初,使用AfxWndProc()子類化視窗,在AfxWndProc()中同樣調用CWnd::WindowProc()處理視窗訊息。不同的是,對於具體的訊息處理,CWnd::WindowProc()使用訊息映射機制,而不是調用固定的虛擬函數。原始的視窗過程儲存在CWnd::m_pfnSuper中,在預設的處理過程CWnd::DefWindowProc()中調用。

下面是與訊息處理相關的幾個CWnd成員函數:

//用於子類化的視窗過程

LRESULT CALLBACK

AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

{

         // special message which identifies the window as using AfxWndProc

         if (nMsg == WM_QUERYAFXWNDPROC)

                   return 1;

         // all other messages route through message map

         //通過控制代碼取得CWnd對象指標

         CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

         ASSERT(pWnd != NULL);

         ASSERT(pWnd->m_hWnd == hWnd);

         return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);

}

LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,

         WPARAM wParam = 0, LPARAM lParam = 0)

{  ……

//調用CWnd的虛擬成員函數處理訊息

         lResult = pWnd->WindowProc(nMsg, wParam, lParam);

         ……

return lResult;

}

//可在類嚮導中重載的虛擬函數

LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

         // OnWndMsg函數處理訊息映射,如果在映射中沒有發現當前訊息的處理函數,則返回false

         LRESULT lResult = 0;

         if (!OnWndMsg(message, wParam, lParam, &lResult))

         //如果當前訊息沒被映射處理,調用預設處理函數

         lResult = DefWindowProc(message, wParam, lParam);

         return lResult;

}

//預設的訊息處理函數,同Default()

LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)

{

         if (m_pfnSuper != NULL)

         //調用原始的視窗過程

                   return::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);

         WNDPROC pfnWndProc;

         if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)

                   //調用預設視窗處理過程

                   return::DefWindowProc(m_hWnd, nMsg, wParam, lParam);

         else

                   return::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);

}

除以上幾個相關成員函數外,CWnd類還定義了兩個公用成員,SubclassWindow()和UnsubclassWindow(),前者用於動態地關聯並使用AfxWndProc()子類化Windows視窗,後者執行相反過程。這意味著,可以隨時將一個使用WIN32 API建立的視窗,通過調用SubclassWindow()關聯到CWnd執行個體上。因為同時執行了子類化操作,所以此時就如同使用CWnd的Create()函數建立了這個Windows視窗一樣。函數的代碼如下:

BOOL CWnd::SubclassWindow(HWND hWnd)

{

         //hWnd應該是一個沒有與任何CWnd執行個體關聯的Windows視窗

         if (!Attach(hWnd)) //先建立關聯,再子類化

                   return FALSE;

         //在子類化前調用這個虛函數,給使用者提供編程介面

         PreSubclassWindow();

WNDPROC* lplpfn = GetSuperWndProcAddr();/*取得原始視窗函數。如果當前類尚未子類化視窗,返回NULL*/

         //使用AfxWndProc()子類化該視窗

         WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,

                   (DWORD)AfxGetAfxWndProc());//取得AfxWndProc()子類視窗函數

         ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());

         if (*lplpfn == NULL)

                   *lplpfn = oldWndProc; //儲存原始視窗函數,在預設處理時調用

         return TRUE;

}

HWND CWnd::UnsubclassWindow()

{

         ASSERT(::IsWindow(m_hWnd));

//得到原始視窗過程

         WNDPROC* lplpfn = GetSuperWndProcAddr();

         //恢複視窗過程

SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)*lplpfn);

         *lplpfn = NULL;

         //分離視窗對象和視窗控制代碼

         return Detach();

}

virtual void CWnd::PreSubclassWindow()

{//該虛擬函數在建立視窗時,子類化前被調用,在CWnd::SubclassWindow中也被調用

//它不執行任何操作,可以重載它,根據需要執行子類化。那樣,子類化過程會成為原始的視窗過程,在訊息預設處理時被調用

}

對視窗操作的封裝是很好理解的,成員CWnd::m_hWnd儲存映射的視窗控制代碼,不同的視窗操作成員函數一般封裝同名的WIN32 API,函數通過m_hWnd調用同名API即可。下面列舉幾個相關的成員函數。

void CWnd::SetWindowText(LPCTSTR lpszString)

         { ASSERT(::IsWindow(m_hWnd)); ::SetWindowText(m_hWnd, lpszString); }

BOOL CWnd::IsIconic() const

         { ASSERT(::IsWindow(m_hWnd)); return ::IsIconic(m_hWnd); }

void CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint)

         { ASSERT(::IsWindow(m_hWnd)); ::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint); }

BOOL CWnd::SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags)

         { ASSERT(::IsWindow(m_hWnd));

 return ::SetWindowPos(m_hWnd, pWndInsertAfter->GetSafeHwnd(),     x, y, cx, cy, nFlags); }

CDC* CWnd::GetWindowDC()

         { ASSERT(::IsWindow(m_hWnd)); return CDC::FromHandle(::GetWindowDC(m_hWnd)); }

int CWnd::ReleaseDC(CDC* pDC)

         { ASSERT(::IsWindow(m_hWnd)); return ::ReleaseDC(m_hWnd, pDC->m_hDC); }

void CWnd::UpdateWindow()

         { ASSERT(::IsWindow(m_hWnd)); ::UpdateWindow(m_hWnd); }

關於CWnd的子視窗管理部分,將在第7章闡述

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.