MFC复习(五)MFC消息映射与消息传递

本文深入解析MFC消息机制,涵盖消息分类、消息映射表结构、宏展开细节、消息映射网连接方式及命令传递机制等内容。

                        ///////////////////////////////////////////////
                       /*     1.Windows消息概览      */
                       //////////////////////////////////////////////
对于消息,程序员应该不陌生。WM_CREATE,WM_PAINT等等都是Windows程序设计中必不可缺少的组成部分。大多有关MFC Win32编程的书籍都将Windows消息分为三大类即:
* 标准消息:   任何以WM_开头的消息(WM_COMMAND除外);如:WM_QUIT,WM_CREATE;
* 命令消息:   WM_COMMAND;
* 子窗口通知: 由子窗口(大多为控件)产生并发送到该控件所属的父窗口的消息。(注意:此类消息也以WM_COMMAND形式出现)
                       //////////////////////////////////////////////////////
                       /*  2.MFC消息映射网的组成元素 */
                       ////////////////////////////////////////////////////// 

  1. //in xx.h
  2. class theClass
  3. {
  4.     ...//
  5.    DECLARE_MESSAGE_MAP()
  6. };
  7. //in xx.cpp
  8. BEGIN_MESSAGE_MAP(theClass, baseClass)
  9.     ON_COMMAND( ID_MYCMD, OnMyCommand )
  10.     ON_WM_CREATE()
  11. END_MESSAGE_MAP()
  12. ...//
  13.   
  14. //这些宏的定义如下:
  15. //in Afxwin.h
  16. #define DECLARE_MESSAGE_MAP() /
  17. private: /
  18.     static const AFX_MSGMAP_ENTRY _messageEntries[]; /
  19. protected: /
  20.     static const AFX_MSGMAP messageMap; /
  21.     static const AFX_MSGMAP* PASCAL GetThisMessageMap(); /
  22.     virtual const AFX_MSGMAP* GetMessageMap() const; /
  23. #define BEGIN_MESSAGE_MAP(theClass, baseClass) /
  24.     const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() /
  25.      { return &theClass::messageMap; } /
  26.     const AFX_MSGMAP* theClass::GetMessageMap() const /
  27.      { return &theClass::messageMap; } /
  28.     AFX_COMDAT const AFX_MSGMAP theClass::messageMap = /
  29.     { &baseClass::GetThisMessageMap, &theClass::_messageEntries[0] }; /
  30.     AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = /
  31.     { /
  32. #define END_MESSAGE_MAP() /
  33.     {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } /
  34.     }; /

DECLARE_MESSAGE_MAP()宏为每个类添加了四个东东,
消息映射表messageMap、消息入口结构数组_messageEntries[] 以及两个获取消息映射表的函数,注意其中一个为virtual
BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏则初始化了它们
                       ///////////////////////////////////////////////
                       /*      3.MFC消息映射表       */
                       //////////////////////////////// //////////////
下面我们看看消息映射表messageMap和消息入口结构AFX_MSGMAP_ENTRY的定义:

  1. //in Afxwin.h
  2. struct AFX_MSGMAP_ENTRY
  3. {
  4.     UINT nMessage;   // windows message
  5.     UINT nCode;      // control code or WM_NOTIFY code
  6.     UINT nID;        // control ID (or 0 for windows messages)
  7.     UINT nLastID;    // used for entries specifying a range of control id's
  8.     UINT_PTR nSig;   // signature type (action) or pointer to message #
  9.     AFX_PMSG pfn;    // routine to call (or special value)
  10. };
  11. struct AFX_MSGMAP
  12. {
  13.     #ifdef _AFXDLL
  14.     const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();//基类的映射表指针,本程序将使用
  15.     #else
  16.     const AFX_MSGMAP* pBaseMap;
  17.     #endif
  18.     const AFX_MSGMAP_ENTRY* lpEntries;
  19. };

其中AFX_MSGMAP结构中包含一个基类的映射表指针和一个指向消息入口结构AFX_MSGMAP_ENTRY的指针。
                       /////////////////////////////////////////////////
                       /*    4.MFC消息映射宏展开     */
                       /////////////////////////////////////////////////
上面的宏展开后代码如下:(以CMaimFrame为例)

  1. //in MaimFrm.h
  2. class CMaimFrame : public CFrameWnd
  3.   ...//
  4. private:
  5.      static const AFX_MSGMAP_ENTRY _messageEntries[];
  6. protected
  7.     static const AFX_MSGMAP messageMap; 
  8.     static const AFX_MSGMAP* PASCAL GetThisMessageMap(); 
  9.     virtual const AFX_MSGMAP* GetMessageMap() const
  10. };
  11. //in MaimFrm.cpp
  12. const AFX_MSGMAP* PASCAL CMaimFrame::GetThisMessageMap() 
  13. {
  14.     return &CMaimFrame::messageMap;
  15. const AFX_MSGMAP* CMaimFrame::GetMessageMap() const 
  16. {
  17.     return &CMaimFrame::messageMap;
  18. }
  19. AFX_COMDAT const AFX_MSGMAP theClass::messageMap = 
  20. {
  21.     &CFrameWnd::GetThisMessageMap,
  22.     &CMaimFrame::_messageEntries[0]
  23. }; 
  24. AFX_COMDAT const AFX_MSGMAP_ENTRY CMaimFrame::_messageEntries[] = 
  25.     {         ...//                      }
  26.               ...
  27.     {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } 
  28. }; 

夹在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()之间的宏,这些宏可分为两类
一类是Windows预定义消息宏(比如:ON_WM_CREATE(),ON_WM_DESTROY()等定义在afxmsg_.h中的Message map tables for Windows messages)
一类是自定义的ON_COMMAND宏以及类似的如ON_UPDATE_COMMAND_UI等宏

  1. //in afxmsg_.h
  2. // Message map tables for Windows messages
  3. #define ON_WM_CREATE() /
  4. { WM_CREATE, 0, 0, 0, AfxSig_is, /
  5.  (AFX_PMSG) (AFX_PMSGW) /
  6.  (static_castint (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > (OnCreate)) },
  7. #define ON_COMMAND(id, memberFxn) /
  8. { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, /
  9.  static_cast<AFX_PMSG> (memberFxn) },
  10. //从而==>
  11. //AFX_MSGMAP_ENTRY结构初始化过程:
  12. AFX_COMDAT const AFX_MSGMAP_ENTRY CMaimFrame::_messageEntries[] = 
  13.     { WM_CREATE,  0,          0,               0,             AfxSig_is,   (AFX_PMSG) (AFX_PMSGW)(static_castint (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > (OnCreate)) },
  14.     { WM_COMMAND, CN_COMMAND, (WORD)ID_MYCMD, (WORD)ID_MYCMD, AfxSigCmd_v, static_cast<AFX_PMSG> ( OnMyCommand) },        
  15.     {0,           0,          0,               0,             AfxSig_end,  (AFX_PMSG)0 } 
  16. };

                       //////////////////////////////////////////////////
                       /*    5.MFC消息映射网的连接   */
                       //////////////////////////////////////////////////
MFC消息映射网的连接也是在初始化过程中完成的,其建立过程很简单。主要有关成员有:
private:
    static const AFX_MSGMAP_ENTRY _messageEntries[];
protected:
    static const AFX_MSGMAP messageMap;
和BEGIN_MESSAGE_MAP()宏开后的
AFX_COMDAT const AFX_MSGMAP theClass::messageMap =
 { &baseClass::GetThisMessageMap, &theClass::_messageEntries[0] };
该宏将pfnGetBaseMap赋值为其基类的messageMap地址;将AFX_MSGMAP_ENTRY* lpEntries赋值为该类的_messageEntries[0]
这样一个类不仅拥有本类的messageMap,而且还拥有其基类的messageMap
依此类推MFC消息映射网的连接就建立了,最终的基类都是CCmdTarget
                       //////////////////////////////////////////////////
                       /*    6.MFC命令传递机制概述   */
                       //////////////////////////////////////////////////
是否还记得MFC在注册窗口类时作了什么?

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)//部分源代码
{
    ...//
    // common initialization
    WNDCLASS wndcls;
    memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults
    wndcls.lpfnWndProc = DefWindowProc;
    ...//
}
可以看到MFC注册时将wndcls.lpfnWndProc赋值为DefWindowProc函数,那么实际上是否消息都是由它处理的呢?显然不可能,MFC又不知道我们要处理什么消息。
《MFC文档视图结构》中曾提到“CWnd::CreateEx函数调用了AfxHookWindowCreate(this);后者是干什么的呢?其实它与消息映射和命令传递有关。”
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
    ...//
    if (pThreadState->m_hHookOldCbtFilter == NULL)
    {
        //注册消息钩子,其钩子处理函数为_AfxCbtFilterHook
        pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT, _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
        ...//
    }
    ...//
}
钩子就是能给你一个在某个消息到达其默认的处理函数之前处理该消息机会的工具。
钩子函数的类型(hook type)--WH_CBT;
有关WH_CBT,MFC文档时如是说的:
Installs a hook procedure that receives notifications useful to a computer-based training (CBT) application. The system calls this function(这里指的是_AfxCbtFilterHook) before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the keyboard focus; or before synchronizing with the system message queue. A computer-based training (CBT) application uses this hook procedure to receive useful notifications from the system.
                       /////////////////////////////////////////////
                       /*    7.偷换“窗口函数”      */
                       /////////////////////////////////////////////

当发生窗口(包括子窗口)发生被激活,创建,撤销,最小化等时候,应用程序将调用_AfxCbtFilterHook函数
_AfxCbtFilterHook==>SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);
_AfxCbtFilterHook函数偷换了窗口函数,将原来的DefWndProc换成AfxWndProc函数.
                       ////////////////////////////////////////////////////
                       /*   8.MFC的“WndProc”函数   */
                       ////////////////////////////////////////////////////
AfxWndProc函数就可以说是MFC的“WndProc”函数,它也是MFC中消息传递的开始
AfxWndProc==>AfxCallWndProc==>pWnd->WindowProc(nMsg, wParam, lParam)==>OnWndMsg
函数OnWndMsg主要用于分辨消息并将消息交于其处理函数。
                       /////////////////////////////////////////////////////
                       /* 9.MFC各类消息的"行走路径 " */
                       /////////////////////////////////////////////////////
无论什么消息都由AfxWndProc进入,到达CWnd::OnWndMsg函数分检消息
对于标准消息:
       标准消息一般都沿其消息映射表从本类到父类逐层查找其处理函数,若没查到着交给::DefWindowProc处理。
对于命令消息:
       命令消息除了能像标准消息一样从本类到父类逐层查找其处理函数外,有时他们可能还要拐弯交给了CWnd::OnCommand(wParam, lParam)
SDI程序中命令消息在Frame,View,Document以及CWinApp对象之间的传递路线:
    Frame的COMMAND传递顺序是View--->Frame本身-->CWinApp对象。
    View的COMMAND传递顺序是View本身--->Document
    Document的COMMAND传递顺序是Document本身--->Document Template

对于子窗口通知:由于子窗口通知通常以WM_COMMAND形式出现,所以它的传递路径也大致与WM_COMMAND相同

 

 

 


摘自
http://www.chinaitpower.com/A/2001-10-06/881.html

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值