DECLARE_MESSAGE_MAP宏
在 MFC 幾乎每個頭文件下(類的最後一行聲明),都會有這麼幾行代碼:
// 生成的消息映射函數
protected:
DECLARE_MESSAGE_MAP()
我們看一看 DECLARE_MESSAGE_MAP到底爲何物,查看 DECLARE_MESSAGE_MAP源碼(c:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\include\afxwin.h):
#define DECLARE_MESSAGE_MAP() \
protected: \
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
virtual const AFX_MSGMAP* GetMessageMap() const; \
我們看到了一個陌生的類型 AFX_MSGMAP ,查看其定義:
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
這個結構體第一個成員是一個函數指針,第二個成員類型爲 AFX_MSGMAP_ENTRY* ,查看AFX_MSGMAP_ENTRY 定義:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
AFX_MSGMAP_ENTRY 定義一些消息的相關信息,AFX_PSG 定義爲:
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
是一個函數指針。是不是將每個消息與其處理方法綁定起來呢?真有可能。
BEGIN_MESSAGE_MAP/ON.../END_MESSAGEBOX 宏
在MFC 幾乎每個(類定義的)源文件下都會出現下面幾行代碼(或者類似):
BEGIN_MESSAGE_MAP(CXXX, C***)
ON_……
END_MESSAGE_MAP()
我們繼續一探究竟,查看這幾個宏的源碼:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
PTM_WARNING_DISABLE \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return GetThisMessageMap(); } \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedef theClass ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
static const AFX_MSGMAP messageMap = \
{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
return &messageMap; \
} \
PTM_WARNING_RESTORE
關於 ON_...類型的宏就多了,下面我摘了(C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\include\afxmsg_.h)下面一些我們很熟悉的代碼:
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
static_cast<AFX_PMSG> (memberFxn) },
// ON_COMMAND(id, OnBar) is the same as
// ON_CONTROL(0, id, OnBar) or ON_BN_CLICKED(0, id, OnBar)
#define ON_NOTIFY(wNotifyCode, id, memberFxn) \
{ WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id, (WORD)id, AfxSigNotify_v, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)(NMHDR*, LRESULT*) > \
(memberFxn)) },
// for general controls
#define ON_CONTROL(wNotifyCode, id, memberFxn) \
{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)id, AfxSigCmd_v, \
(static_cast< AFX_PMSG > (memberFxn)) },
#define ON_WM_DESTROY() \
{ WM_DESTROY, 0, 0, 0, AfxSig_vv, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnDestroy)) },
#define ON_WM_SIZE() \
{ WM_SIZE, 0, 0, 0, AfxSig_vwii, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, int, int) > ( &ThisClass :: OnSize)) },
又加了一些宏,一步一步的看吧,PTM_WARNING_DISABLE與PTM_WARNING_RESTORE:
#define PTM_WARNING_DISABLE \
__pragma(warning( push )) \
__pragma(warning( disable : 4867 ))
#define PTM_WARNING_RESTORE \
__pragma(warning( pop ))
是處理警告的,貌似和我們的話題不符,就先不看了。
查看 AfxSig_end 定義:
enum AfxSig
{
AfxSig_end = 0, // [marks end of message map]
AfxSig_b_D_v, // BOOL (CDC*)
AfxSig_b_b_v, // BOOL (BOOL)
AfxSig_b_u_v, // BOOL (UINT)
AfxSig_b_h_v, // BOOL (HANDLE)
AfxSig_b_W_uu, // BOOL (CWnd*, UINT, UINT)
AfxSig_b_W_COPYDATASTRUCT, // BOOL (CWnd*, COPYDATASTRUCT*)
AfxSig_b_v_HELPINFO, // BOOL (LPHELPINFO);
AfxSig_CTLCOLOR, // HBRUSH (CDC*, CWnd*, UINT)
AfxSig_CTLCOLOR_REFLECT, // HBRUSH (CDC*, UINT)
AfxSig_i_u_W_u, // int (UINT, CWnd*, UINT) // ?TOITEM
AfxSig_i_uu_v, // int (UINT, UINT)
AfxSig_i_W_uu, // int (CWnd*, UINT, UINT)
AfxSig_i_v_s, // int (LPTSTR)
AfxSig_l_w_l, // LRESULT (WPARAM, LPARAM)
AfxSig_l_uu_M, // LRESULT (UINT, UINT, CMenu*)
AfxSig_v_b_h, // void (BOOL, HANDLE)
AfxSig_v_h_v, // void (HANDLE)
AfxSig_v_h_h, // void (HANDLE, HANDLE)
AfxSig_v_v_v, // void ()
AfxSig_v_u_v, // void (UINT)
AfxSig_v_u_u, // void (UINT, UINT)
AfxSig_v_uu_v, // void (UINT, UINT)
AfxSig_v_v_ii, // void (int, int)
AfxSig_v_u_uu, // void (UINT, UINT, UINT)
AfxSig_v_u_ii, // void (UINT, int, int)
AfxSig_v_u_W, // void (UINT, CWnd*)
AfxSig_i_u_v, // int (UINT)
AfxSig_u_u_v, // UINT (UINT)
AfxSig_b_v_v, // BOOL ()
AfxSig_v_w_l, // void (WPARAM, LPARAM)
AfxSig_MDIACTIVATE, // void (BOOL, CWnd*, CWnd*)
AfxSig_v_D_v, // void (CDC*)
AfxSig_v_M_v, // void (CMenu*)
AfxSig_v_M_ub, // void (CMenu*, UINT, BOOL)
AfxSig_v_W_v, // void (CWnd*)
AfxSig_v_v_W, // void (CWnd*)
AfxSig_v_W_uu, // void (CWnd*, UINT, UINT)
AfxSig_v_W_p, // void (CWnd*, CPoint)
AfxSig_v_W_h, // void (CWnd*, HANDLE)
AfxSig_C_v_v, // HCURSOR ()
AfxSig_ACTIVATE, // void (UINT, CWnd*, BOOL)
AfxSig_SCROLL, // void (UINT, UINT, CWnd*)
AfxSig_SCROLL_REFLECT, // void (UINT, UINT)
AfxSig_v_v_s, // void (LPTSTR)
AfxSig_v_u_cs, // void (UINT, LPCTSTR)
AfxSig_OWNERDRAW, // void (int, LPTSTR) force return TRUE
AfxSig_i_i_s, // int (int, LPTSTR)
AfxSig_u_v_p, // UINT (CPoint)
AfxSig_u_v_v, // UINT ()
AfxSig_v_b_NCCALCSIZEPARAMS, // void (BOOL, NCCALCSIZE_PARAMS*)
AfxSig_v_v_WINDOWPOS, // void (WINDOWPOS*)
AfxSig_v_uu_M, // void (UINT, UINT, HMENU)
AfxSig_v_u_p, // void (UINT, CPoint)
AfxSig_SIZING, // void (UINT, LPRECT)
AfxSig_MOUSEWHEEL, // BOOL (UINT, short, CPoint)
AfxSig_MOUSEHWHEEL, // void (UINT, short, CPoint)
AfxSigCmd_v, // void ()
AfxSigCmd_b, // BOOL ()
AfxSigCmd_RANGE, // void (UINT)
AfxSigCmd_EX, // BOOL (UINT)
AfxSigNotify_v, // void (NMHDR*, LRESULT*)
AfxSigNotify_b, // BOOL (NMHDR*, LRESULT*)
AfxSigNotify_RANGE, // void (UINT, NMHDR*, LRESULT*)
AfxSigNotify_EX, // BOOL (UINT, NMHDR*, LRESULT*)
AfxSigCmdUI, // void (CCmdUI*)
AfxSigCmdUI_RANGE, // void (CCmdUI*, UINT)
AfxSigCmd_v_pv, // void (void*)
AfxSigCmd_b_pv, // BOOL (void*)
AfxSig_l, // LRESULT ()
AfxSig_l_p, // LRESULT (CPOINT)
AfxSig_u_W_u, // UINT (CWnd*, UINT)
AfxSig_v_u_M, // void (UINT, CMenu* )
AfxSig_u_u_M, // UINT (UINT, CMenu* )
AfxSig_u_v_MENUGETOBJECTINFO, // UINT (MENUGETOBJECTINFO*)
AfxSig_v_M_u, // void (CMenu*, UINT)
AfxSig_v_u_LPMDINEXTMENU, // void (UINT, LPMDINEXTMENU)
AfxSig_APPCOMMAND, // void (CWnd*, UINT, UINT, UINT)
AfxSig_RAWINPUT, // void (UINT, HRAWINPUT)
AfxSig_u_u_u, // UINT (UINT, UINT)
AfxSig_MOUSE_XBUTTON, // void (UINT, UINT, CPoint)
AfxSig_MOUSE_NCXBUTTON, // void (short, UINT, CPoint)
AfxSig_INPUTLANGCHANGE, // void (BYTE, UINT)
AfxSig_v_u_hkl, // void (UINT, HKL)
AfxSig_INPUTDEVICECHANGE, // void (unsigned short)
// Old
AfxSig_bD = AfxSig_b_D_v, // BOOL (CDC*)
AfxSig_bb = AfxSig_b_b_v, // BOOL (BOOL)
AfxSig_bWww = AfxSig_b_W_uu, // BOOL (CWnd*, UINT, UINT)
AfxSig_hDWw = AfxSig_CTLCOLOR, // HBRUSH (CDC*, CWnd*, UINT)
AfxSig_hDw = AfxSig_CTLCOLOR_REFLECT, // HBRUSH (CDC*, UINT)
AfxSig_iwWw = AfxSig_i_u_W_u, // int (UINT, CWnd*, UINT)
AfxSig_iww = AfxSig_i_uu_v, // int (UINT, UINT)
AfxSig_iWww = AfxSig_i_W_uu, // int (CWnd*, UINT, UINT)
AfxSig_is = AfxSig_i_v_s, // int (LPTSTR)
AfxSig_lwl = AfxSig_l_w_l, // LRESULT (WPARAM, LPARAM)
AfxSig_lwwM = AfxSig_l_uu_M, // LRESULT (UINT, UINT, CMenu*)
AfxSig_vv = AfxSig_v_v_v, // void (void)
AfxSig_vw = AfxSig_v_u_v, // void (UINT)
AfxSig_vww = AfxSig_v_u_u, // void (UINT, UINT)
AfxSig_vww2 = AfxSig_v_uu_v, // void (UINT, UINT) // both come from wParam
AfxSig_vvii = AfxSig_v_v_ii, // void (int, int) // wParam is ignored
AfxSig_vwww = AfxSig_v_u_uu, // void (UINT, UINT, UINT)
AfxSig_vwii = AfxSig_v_u_ii, // void (UINT, int, int)
AfxSig_vwl = AfxSig_v_w_l, // void (UINT, LPARAM)
AfxSig_vbWW = AfxSig_MDIACTIVATE, // void (BOOL, CWnd*, CWnd*)
AfxSig_vD = AfxSig_v_D_v, // void (CDC*)
AfxSig_vM = AfxSig_v_M_v, // void (CMenu*)
AfxSig_vMwb = AfxSig_v_M_ub, // void (CMenu*, UINT, BOOL)
AfxSig_vW = AfxSig_v_W_v, // void (CWnd*)
AfxSig_vWww = AfxSig_v_W_uu, // void (CWnd*, UINT, UINT)
AfxSig_vWp = AfxSig_v_W_p, // void (CWnd*, CPoint)
AfxSig_vWh = AfxSig_v_W_h, // void (CWnd*, HANDLE)
AfxSig_vwW = AfxSig_v_u_W, // void (UINT, CWnd*)
AfxSig_vwWb = AfxSig_ACTIVATE, // void (UINT, CWnd*, BOOL)
AfxSig_vwwW = AfxSig_SCROLL, // void (UINT, UINT, CWnd*)
AfxSig_vwwx = AfxSig_SCROLL_REFLECT, // void (UINT, UINT)
AfxSig_vs = AfxSig_v_v_s, // void (LPTSTR)
AfxSig_vOWNER = AfxSig_OWNERDRAW, // void (int, LPTSTR), force return TRUE
AfxSig_iis = AfxSig_i_i_s, // int (int, LPTSTR)
AfxSig_wp = AfxSig_u_v_p, // UINT (CPoint)
AfxSig_wv = AfxSig_u_v_v, // UINT (void)
AfxSig_vPOS = AfxSig_v_v_WINDOWPOS, // void (WINDOWPOS*)
AfxSig_vCALC = AfxSig_v_b_NCCALCSIZEPARAMS, // void (BOOL, NCCALCSIZE_PARAMS*)
AfxSig_vNMHDRpl = AfxSigNotify_v, // void (NMHDR*, LRESULT*)
AfxSig_bNMHDRpl = AfxSigNotify_b, // BOOL (NMHDR*, LRESULT*)
AfxSig_vwNMHDRpl = AfxSigNotify_RANGE, // void (UINT, NMHDR*, LRESULT*)
AfxSig_bwNMHDRpl = AfxSigNotify_EX, // BOOL (UINT, NMHDR*, LRESULT*)
AfxSig_bHELPINFO = AfxSig_b_v_HELPINFO, // BOOL (HELPINFO*)
AfxSig_vwSIZING = AfxSig_SIZING, // void (UINT, LPRECT) -- return TRUE
// signatures specific to CCmdTarget
AfxSig_cmdui = AfxSigCmdUI, // void (CCmdUI*)
AfxSig_cmduiw = AfxSigCmdUI_RANGE, // void (CCmdUI*, UINT)
AfxSig_vpv = AfxSigCmd_v_pv, // void (void*)
AfxSig_bpv = AfxSigCmd_b_pv, // BOOL (void*)
// Other aliases (based on implementation)
AfxSig_vwwh = AfxSig_v_uu_M, // void (UINT, UINT, HMENU)
AfxSig_vwp = AfxSig_v_u_p, // void (UINT, CPoint)
AfxSig_bw = AfxSig_b_u_v, // BOOL (UINT)
AfxSig_bh = AfxSig_b_h_v, // BOOL (HANDLE)
AfxSig_iw = AfxSig_i_u_v, // int (UINT)
AfxSig_ww = AfxSig_u_u_v, // UINT (UINT)
AfxSig_bv = AfxSig_b_v_v, // BOOL (void)
AfxSig_hv = AfxSig_C_v_v, // HANDLE (void)
AfxSig_vb = AfxSig_vw, // void (BOOL)
AfxSig_vbh = AfxSig_v_b_h, // void (BOOL, HANDLE)
AfxSig_vbw = AfxSig_vww, // void (BOOL, UINT)
AfxSig_vhh = AfxSig_v_h_h, // void (HANDLE, HANDLE)
AfxSig_vh = AfxSig_v_h_v, // void (HANDLE)
AfxSig_viSS = AfxSig_vwl, // void (int, STYLESTRUCT*)
AfxSig_bwl = AfxSig_lwl,
AfxSig_vwMOVING = AfxSig_vwSIZING, // void (UINT, LPRECT) -- return TRUE
AfxSig_vW2 = AfxSig_v_v_W, // void (CWnd*) (CWnd* comes from lParam)
AfxSig_bWCDS = AfxSig_b_W_COPYDATASTRUCT, // BOOL (CWnd*, COPYDATASTRUCT*)
AfxSig_bwsp = AfxSig_MOUSEWHEEL, // BOOL (UINT, short, CPoint)
AfxSig_vws = AfxSig_v_u_cs,
};
AfxSig 的含義暫時不講,稍後再說。
把幾個宏分散開來,沒有整體效果。還是舉個例子吧:
DECLARE_MESSAGE_MAP()
EGIN_MESSAGE_MAP(CFirstMFCDemoApp, CWinApp)
ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
END_MESSAGE_MAP()
展開以後爲:
protected:
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
const AFX_MSGMAP* CFirstMFCDemoApp::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* PASCAL
CFirstMFCDemoApp::GetThisMessageMap()
{
typedef CFirstMFCDemoApp ThisClass;
typedef CWinApp TheBaseClass;
static const AFX_MSGMAP_ENTRY _messageEntries[] =
{
{
WM_COMMAND, CN_COMMAND, (WORD)ID_FILE_NEW,
(WORD)ID_FILE_NEW, AfxSigCmd_v,
static_cast<AFX_PMSG> (&CWinApp::OnFileNew)
},
{
WM_COMMAND, CN_COMMAND, (WORD)ID_FILE_OPEN,
(WORD)ID_FILE_OPEN, AfxSigCmd_v,
static_cast<AFX_PMSG> (&CWinApp::OnFileOpen)
},
{
0, 0, 0, 0,
AfxSig_end, (AFX_PMSG)0
}
};
static const AFX_MSGMAP messageMap =
{ &TheBaseClass::GetThisMessageMap,
&_messageEntries[0] };
return &messageMap;
}
仔細看上述代碼,會發現這和 RTTI 一樣,還是一個鏈表。將子類的消息映射表和父類的消息映射表聯繫起來。
下圖爲 MFC 消息映射表:
消息泵的開始
這裏用到 window高級編程中的 hook 技術,而我對 hook 技術很不瞭解,所以無法講述(與其講錯,不如不講)。想了解更多,自己查資料去吧,O(∩_∩)O~。
消息傳遞方式
(1)直線上溯(一般 windows 消息)
CWnd::WindowProc 調用 OnWndMsg 用來分辨並處理消息;如果是命令消息,交給 OnCommand 處理,如果是通知消息(Notification),交給 OnNotify 處理。而一般的 Windows 消息,就直接在消息映射表中上溯,尋找其歸宿(消息處理程序)。
下圖是WM_PAINT 消息發生於 View 時,消息傳遞路徑:
(2)拐彎上溯(WM_COMMAND命令消息)
如果消息是 WM_COMMAND ,CWnd::OnWndMsg 另闢蹊徑,交流 OnCommand 來處理。這並不一定是 CWnd::OnCommand ,得視 this 指針指向哪一種對象而定。在 MFC 之中,以下類都改寫了 OnCommand 函數;
Class CWnd : public CCmdTarget
Class CFrameWnd : public CFrameWnd
Class CMDIFrameWnd : public CWnd
Class CSplitterWnd : public CWnd
Class CPropertySheet : public CWnd
Class COlePropertyPage : public CDialog
下圖示出了 FrameWnd 窗口收到命令消息後的四個嘗試路徑:
消息映射和命令傳遞,大體上應該有了一個瞭解。一些細節如“AfxSig_XX 的奧祕”以及具體到程序的代碼,這是一個極其複雜的過程,需要深厚的 Windows 編程功底。而我覺得作爲 MFC 普通用戶而言,瞭解到這裏就已經夠了。
http://www.cnblogs.com/chinazhangjie/archive/2011/09/20/2181967.html