MFC消息映射与命令传递
MFC消息映射与命令传递 张杰 2011-09-17 声明 本文由张杰原创,参考了侯俊杰先生的《深入浅出MFC》,源码摘自 Microsoft Visual Studio 9.0\VC。 个人能力有限,文章必定存在很多错误。我的邮箱是:chinajiezhang@gmail.com chinajiezhang@163.com 欢迎您来邮件斧正。当然您也可以加我 msn: chinazhangjie@hotmail.com 交流。 本文可供传播、交流、学习使用,但请不要用于商业用途。转载请标明此声明,谢谢您的合作。 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 普通用户而言,了解到这里就已经够了。 |
|