系統(tǒng)理解Win32 API和MFC(下)
作者: 溫昱
作者主頁: lcspace.diy.163.com
系統(tǒng)理解Win32 API和MFC(上)
二、MFC的概念模型
前面我們研究了WIN32 API的“領(lǐng)域模型”,對它有較全面的認識。下面,對MFC概念模型的研究,我們把重點放在對app framework的研究上。
app framework中的message響應(yīng)/傳遞機制是最重要的。而Hook機制和Message響應(yīng)/傳遞機制是密切相關(guān)的,后者以前者為基礎(chǔ)。
1. Hook機制
也許有些程序員只知道hook機制可以編寫很“牛”的應(yīng)用,孰不知MFC本身也是依靠hook機制的。
從圖中看到,每個hook擁有一個指針隊列,每個指針指向一個稱為的HookProc函數(shù),HookProc將在合適的時機被OS調(diào)用執(zhí)行。hook是分不同種類的,其實正是hook的種類決定了它什么時機被OS調(diào)用執(zhí)行。提示,可以看一下“訂閱-發(fā)布”設(shè)計模式以助理解。
2 MFC中Message響應(yīng)函數(shù)的安裝
2.1 回憶API中Message響應(yīng)函數(shù)的安裝
API中Message響應(yīng)函數(shù)的安裝,是由CreateWindow()實現(xiàn)的,它將window與一個windowClass聯(lián)系起來,而后者中記錄了Message響應(yīng)函數(shù)的指針。
至于細節(jié),看一下如何用Win32 SDK或Win16 SDK寫程序就清楚了,其中 DefWindowProc()是API函數(shù),負責(zé)提供缺省的消息處理,所以,程序員只需要handle需要特殊處理的消息。
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{
WNDCLASS wndclass;
...
wndclass.lpfnWndProc =WndProc;
wndclass.lpszClassName = szWindowClass;
...
RegisterClass(&wndclass);
hWnd = CreateWindow( szWindowClass, ...);
...
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch(message)
{
...
return;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
2.2 MFC中Message響應(yīng)函數(shù)的安裝
MFC中Message響應(yīng)函數(shù)的安裝顯然更復(fù)雜,是在CWnd::CreateEx()被調(diào)用時完成的,其中還用到了Hook機制。
我們可以先猜一下MFC是怎么做的。MFC支持massage map,使得對消息的響應(yīng)份散到多個message handler函數(shù)中,而不是API開發(fā)是那種集中式的消息處理函數(shù);所以,想必會有專門的代碼來負責(zé)“檢索message map table然后調(diào)用message handle”。message map是為了支持程序員處理他關(guān)心的特殊message的,那么缺省的message處理邏輯在哪里呢?答案是MFC創(chuàng)建window obj時是用的“預(yù)定義的窗口類”,自然已經(jīng)有了缺省的message處理函數(shù)。
從圖中看到,CWnd有成員變量m_pfnSuper、成員變量m_hWnd、成員函數(shù)OnWndMsg()和成員函數(shù)DefWindowProc()。Wnd::OnWndMsg()負責(zé)“在message map中定義的message handle”能否處理到來的message,如果處理了要返回true;CWnd::DefWindowProc()負責(zé)對message缺省處理。
執(zhí)行過程是,首先CWnd::CreateEx()被調(diào)用,window obj和window class被相應(yīng)建立,此時window class的WindowProc字段存儲了預(yù)定義的缺省處理函數(shù)的地址;由于有hook在監(jiān)聽窗口創(chuàng)建消息,所以注冊的hookProc()會被調(diào)用執(zhí)行,它將classWindow數(shù)據(jù)結(jié)構(gòu)的WindowProc字段備份到CWnd::m_pfnSuper,再用SetWindowLong()改寫classWindow數(shù)據(jù)結(jié)構(gòu)的WindowProc字段為::AfxWndProc()的地址。當(dāng)任何一個message到達時,::AfxWndProc()被調(diào)用,至于它的邏輯,聰明的你一定猜到了,先調(diào)用Wnd::OnWndMsg(),如果返回值為false,還要調(diào)用CWnd::DefWindowProc(),CWnd::m_pfnSuper指向的缺省處理邏輯,也會在CWnd::DefWindowProc()中被調(diào)用。
提示,上面其實有多態(tài)情況發(fā)生。比如你可以在搜一下pWnd->WindowProc(nMsg, wParam, lParam); 另外,OnWndMsg和DefWindowProc都是CWnd類的虛擬函數(shù)。
要是覺得不太好理解,最好在VC++里創(chuàng)建一個project實際跟蹤一下,下面是我跟蹤時調(diào)用棧映象的截圖。

3. SubClass機制

從圖中看到,SubClass機制以CWnd自身的m_pfnSuper為基礎(chǔ),和“MFC中Message響應(yīng)函數(shù)的安裝”很象。
4.frame work中的主要相關(guān)類
frame work中的主要相關(guān)類 就是 message route的候選人,正是它們的OnCmdMsg()共同完成了message route,形成了chain of responsability模式。
5. frame work中的chain of responsability模式
下圖是一個對象樹,注意消息會在縱向和橫向兩個方向傳播。

消息在縱向方向上的傳遞,是在“上溯父類的massge map表”,MFC的message map完全是為了代替虛函數(shù)而采取的手段,而和message route無關(guān)。
消息在橫向方向上的傳遞,才是message route,才是chain of responsability模式,由多個相關(guān)類的OnCmdMsg()共同完成。
三、 總結(jié)
從上面的討論不難發(fā)現(xiàn),MFC中用到了不少設(shè)計模式,如上面提到的chain of responsability模式、composite模式和“訂閱-發(fā)布”模式。上面的討論不僅有助于程序員全面掌握Win32 API和MFC,對architect設(shè)計architecture也有很大幫助。