1. 程式人生 > >解讀ATL/WTL/MFC訊息對映的實現方式

解讀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++程式設計師來說,是值得學習的啊