解讀ATL/WTL/MFC訊息對映的實現方式
在我們編寫WTL程式的時候,如下的訊息對映我們很熟悉:
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(ID_CATALOG,OnSetCatalog)
END_MSG_MAP()
在寫MFC程式的時候,是如下的訊息對映:
BEGIN_MESSAGE_MAP(CUpdateDlg, CDialog)
ON_WM_PAINT()
ON_WM_TIMER()
END_MESSAGE_MAP()
然後我們編寫相應的訊息函式,程式執行的時候就會被呼叫。
對我們初級程式設計師來說,我們有可能很少注意這些巨集的實現方式,而僅僅關注在了會使用這些巨集,並知道這些巨集能做什麼。
暫且不說它們的實現,看到這種實現方式足以很讓我們這些初級程式設計師感到驚奇。
請閉上眼睛自問:如果讓我實現一個有同樣功能的程式框架,我會怎麼做?
大部分人會首先想到一個結構switch(){case ...},不錯,就像我們的哥哥姐姐們寫WIN 32程式的時候,有一個大家都很熟悉的函式:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
PAINT_FUN(...);
break;
case WM_MOUSEMOVE:
MOUSEMOVE_FUN(...);
break;
case WM_MOUSELEAVE:
MOUSELEAVE_FUN(...);
break;
....
default:
break;
}
如果再涉及到派生和繼承,為了支援多型,就聯想到虛擬函式,也許你會自然而然想到一個這樣的類:
class COwnWnd
{
virtual LRESULT PAINT_FUN(...){...}
virtual LRESULT MOUSEMOVE_FUN(...){...}
....
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
PAINT_FUN(...);
break;
case WM_MOUSEMOVE:
MOUSEMOVE_FUN(...);
break;
case WM_MOUSELEAVE:
MOUSELEAVE_FUN(...);
break;
....
default:
break;
}
};
然後在派生類裡僅僅需要派生相應的虛擬函式就OK了。
恩,是的,可以達到要求;但在我們看WTL/MFC的程式的時候,我們發現他們用了巨集來很巧妙的實現了這種方式。
這可以通過檢視WTL的原始碼來檢視:
我們發現所有的視窗類都繼承了CMessageMap類,而這個類到底做了什麼呢?下面是這個類的原始碼:
class ATL_NO_VTABLE CMessageMap
{
public:
virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
LRESULT& lResult, DWORD dwMsgMapID) = 0;
};
哦,原來它僅僅提供了一個純虛擬函式,繼續看,我們會發現這個重要的巨集:
#define BEGIN_MSG_MAP(theClass) /
public: /
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) /
{ /
BOOL bHandled = TRUE; /
(hWnd); /
(uMsg); /
(wParam); /
(lParam); /
(lResult); /
(bHandled); /
switch(dwMsgMapID) /
{ /
case 0:
#define ALT_MSG_MAP(msgMapID) /
break; /
case msgMapID:
#define MESSAGE_HANDLER(msg, func) /
if(uMsg == msg) /
{ /
bHandled = TRUE; /
lResult = func(uMsg, wParam, lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define MESSAGE_RANGE_HANDLER(msgFirst, msgLast, func) /
if(uMsg >= msgFirst && uMsg <= msgLast) /
{ /
bHandled = TRUE; /
lResult = func(uMsg, wParam, lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define COMMAND_HANDLER(id, code, func) /
if(uMsg == WM_COMMAND && id == LOWORD(wParam) && code == HIWORD(wParam)) /
{ /
bHandled = TRUE; /
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define COMMAND_ID_HANDLER(id, func) /
if(uMsg == WM_COMMAND && id == LOWORD(wParam)) /
{ /
bHandled = TRUE; /
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define COMMAND_CODE_HANDLER(code, func) /
if(uMsg == WM_COMMAND && code == HIWORD(wParam)) /
{ /
bHandled = TRUE; /
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define COMMAND_RANGE_HANDLER(idFirst, idLast, func) /
if(uMsg == WM_COMMAND && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) /
{ /
bHandled = TRUE; /
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define COMMAND_RANGE_CODE_HANDLER(idFirst, idLast, code, func) /
if(uMsg == WM_COMMAND && code == HIWORD(wParam) && LOWORD(wParam) >= idFirst && LOWORD(wParam) <= idLast) /
{ /
bHandled = TRUE; /
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define NOTIFY_HANDLER(id, cd, func) /
if(uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom && cd == ((LPNMHDR)lParam)->code) /
{ /
bHandled = TRUE; /
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define NOTIFY_ID_HANDLER(id, func) /
if(uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) /
{ /
bHandled = TRUE; /
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define NOTIFY_CODE_HANDLER(cd, func) /
if(uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) /
{ /
bHandled = TRUE; /
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define NOTIFY_RANGE_HANDLER(idFirst, idLast, func) /
if(uMsg == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) /
{ /
bHandled = TRUE; /
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define NOTIFY_RANGE_CODE_HANDLER(idFirst, idLast, cd, func) /
if(uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) /
{ /
bHandled = TRUE; /
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); /
if(bHandled) /
return TRUE; /
}
#define CHAIN_MSG_MAP(theChainClass) /
{ /
if(theChainClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) /
return TRUE; /
}
#define CHAIN_MSG_MAP_MEMBER(theChainMember) /
{ /
if(theChainMember.ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) /
return TRUE; /
}
#define CHAIN_MSG_MAP_ALT(theChainClass, msgMapID) /
{ /
if(theChainClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, msgMapID)) /
return TRUE; /
}
#define CHAIN_MSG_MAP_ALT_MEMBER(theChainMember, msgMapID) /
{ /
if(theChainMember.ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, msgMapID)) /
return TRUE; /
}
#define CHAIN_MSG_MAP_DYNAMIC(dynaChainID) /
{ /
if(CDynamicChain::CallChain(dynaChainID, hWnd, uMsg, wParam, lParam, lResult)) /
return TRUE; /
}
#define END_MSG_MAP() /
break; /
default: /
ATLTRACE(ATL::atlTraceWindowing, 0, _T("Invalid message map ID (%i)/n"), dwMsgMapID); /
ATLASSERT(FALSE); /
break; /
} /
return FALSE; /
}
這個巨集,會提供一個具體的ProcessWindowMessage函式,來重寫那個純虛擬函式。
如果有一個這樣的類:
class test:public CMessageMap
{
//當你寫下這對巨集的時候,你已經提供了一個具體的ProcessWindowMessage函式
BEGIN_MSG_MAP(test)
//新增相應的訊息對映
END_MSG_MAP()
}
哦,原來如此,那就讓我們模擬一下這種實現方式吧!
//msg
#define OWN_MSG_1 0x1
#define OWN_MSG_2 0x2
#define OWN_MSG_3 0x3
//cmd
#define OWN_CMD_1 0x4
#define OWN_CMD_2 0x5
#define OWN_CMD_3 0x6
class COwnMessageMap//
{
public:
virtual int DefaultWinProc(unsigned int uMsg,char* wParam,char* lParam)=0;//關鍵的純虛擬函式
void _Run()
{
int i=0;
char str[3]={0};
while(1)//模擬GetMsg
{
_sleep(1000);
sprintf(str,"%d",i);
DefaultWinProc(i,str,str);
i++;
if(i>10)
break;
}
}
};
#define BEGIN_OWN_MAP(theClass) /
int DefaultWinProc(unsigned int uMsg,char* wParam,char* lParam) /
{/
#define MSG_HANDLER(msg,function) /
if(uMsg==msg) /
{/
return function(wParam,lParam); /
}/
#define CMD_HANDLER(msg,function) /
if(uMsg==msg) /
{/
return function(uMsg,wParam,lParam); /
}/
#define END_OWN_MAP()/
return 1;/
}
class COwnWnd:public COwnMessageMap
{
BEGIN_OWN_MAP(COwnWnd)
MSG_HANDLER(OWN_MSG_1,OnMsg1)
MSG_HANDLER(OWN_MSG_3,OnMsg3)
CMD_HANDLER(OWN_CMD_1,OnCmd1)
CMD_HANDLER(OWN_CMD_2,OnCmd)
CMD_HANDLER(OWN_CMD_3,OnCmd)
END_OWN_MAP()
int OnMsg1(char* wp,char* lp)
{
printf("Msg/t");
printf(wp);
printf("/n");
return 1;
}
int OnMsg3(char* wp,char* lp)
{
printf("MSG/t");
printf(wp);
printf("/n");
return 1;
}
int OnCmd1(unsigned int id,char* wp,char* lp)
{
printf("command id:%x/t",id);
printf("CMD/t");
printf(wp);
printf("/n");
return 1;
}
int OnCmd(unsigned int id,char* wp,char* lp)
{
printf("command id:%d/t",id);
printf("CMD/t");
printf(wp);
printf("/n");
return 1;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
COwnWnd Wnd;
Wnd._Run();
getchar();
return 0;
}
執行結果:
Msg 1
Msg 3
command id:4 CMD 4
command id:5 CMD 5
command id:6 CMD 6
巨集特別是對C/C++程式設計師來說,是值得學習的啊