• 分析与理解消息反射机制


    前言:

      我曾写过一篇文章对通知消息WM_NOTIFY进行分析,消息反射是MFC中对通知消息的处理方式,两者之间关系十分紧密,因此,我写了这篇文章,希望能够描绘出通知消息的完整印象。

    消息反射的基础知识

    1、消息反射解释:
      父窗口将控制子窗口发给它的通知消息,首先反射回子窗口进行处理(即给控制子窗口一个机会,让控制子窗口处理此消息),这样通知消息就有机会能被子窗口自身进行处理。

    2、MFC中引入消息反射的原因:
      在Windows的消息处理中,控制子窗口的发给其父窗口的通知消息只能由其父窗口进行处理,这使得控制子窗口的自身能动性大大降低(你想,它连改变自己的背景色,处理一个自身滚动问题都要其父窗口来完成),为了解决这个问题,在MFC中引入了反射消息“Reflect Message”的概念,进行消息反射,可以使得控制子窗口能够自行处理与自身相关的一些消息,增强了封装性,从而提高了控制子窗口的可重用性。

    消息反射的处理流程(不考虑OLE控制)

    一、消息反射处理流程图:
      1、父窗口收到控制子窗口发来的通知消息后,调用它的虚函数CWnd::OnNotify.
    CWnd::OnNotify()主体部分:
    {
    if (ReflectLastMsg(hWndCtrl, pResult)) //此时,hWndCtrl,为发送窗口,即子窗口的窗口句柄
    return TRUE; // 子窗口已处理了此消息
    AFX_NOTIFY notify;
    notify.pResult = pResult;
    notify.pNMHDR = pNMHDR;
    return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), ?ify, NULL);
    }

      分析:首先,调用ReflectLastMsg(hCtrlChildWnd,...)给子窗口一个自身处理的机会,将消息反射给子窗口处理,函数返回TRUE,表明子窗口处理了此消息。反之,表示子窗口未处理此消息,此时,调用OnCmdMsg(...)由父窗口进行通常的处理。

      2、ReflectLastMsg中:
      主要是调用发送窗口的SendChildNotifyLastMsg(...)。

      3、SendChildNotifyLastMsg 中:
      调用发送窗口的虚函数OnChildNotify函数,进行处理。 如果没有处理,则调用ReflectChildNotify(...)函数进行标准的反射消息的消息映射处理。

      -------------------------------------------------------------------------

     我曾写过一篇文章对通知消息WM_NOTIFY进行分析,消息反射是MFC中对通知消息的处理方式,两者之间关系十分紧密,因此,我写了这篇文章,希望能够描绘出通知消息的完整印象。

    消息反射的基础知识

    1、消息反射解释:
      父窗口将控制子窗口发给它的通知消息,首先反射回子窗口进行处理(即给控制子窗口一个机会,让控制子窗口处理此消息),这样通知消息就有机会能被子窗口自身进行处理。

    2、MFC中引入消息反射的原因:
      在Windows的消息处理中,控制子窗口的发给其父窗口的通知消息只能由其父窗口进行处理,这使得控制子窗口的自身能动性大大降低(你想,它连改变自己的背景色,处理一个自身滚动问题都要其父窗口来完成),为了解决这个问题,在MFC中引入了反射消息“Reflect Message”的概念,进行消息反射,可以使得控制子窗口能够自行处理与自身相关的一些消息,增强了封装性,从而提高了控制子窗口的可重用性。

    消息反射的处理流程(不考虑OLE控制)

    一、消息反射处理流程图:
      1、父窗口收到控制子窗口发来的通知消息后,调用它的虚函数CWnd::OnNotify.
    CWnd::OnNotify()主体部分:
    {
    if (ReflectLastMsg(hWndCtrl, pResult)) //此时,hWndCtrl,为发送窗口,即子窗口的窗口句柄
    return TRUE; // 子窗口已处理了此消息
    AFX_NOTIFY notify;
    notify.pResult = pResult;
    notify.pNMHDR = pNMHDR;
    return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL);
    }

      分析:首先,调用ReflectLastMsg(hCtrlChildWnd,...)给子窗口一个自身处理的机会,将消息反射给子窗口处理,函数返回TRUE,表明子窗口处理了此消息。反之,表示子窗口未处理此消息,此时,调用OnCmdMsg(...)由父窗口进行通常的处理。

      2、ReflectLastMsg中:
      主要是调用发送窗口的SendChildNotifyLastMsg(...)。

      3、SendChildNotifyLastMsg 中:
      调用发送窗口的虚函数OnChildNotify函数,进行处理。 如果没有处理,则调用ReflectChildNotify(...)函数进行标准的反射消息的消息映射处理。


    二、消息处理

    方式1:
      由上述处理流程可以看出来,子窗口要想自身处理此消息,重载子控件窗口的OnChildNotify虚拟函数应该是很容易想到的方式。

      注意:MFC中对各个子控件窗口一般都已经重载了OnChildNotify函数,它对应调用类的虚函数进行处理,所以,你重载对应的虚函数即可,如下例:
    BOOL CStatusBarCtrl::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam,LRESULT* pResult)
    {
    if (message != WM_DRAWITEM) //对应不同的控制,会有不同的有特殊处理要求的消息。
    return CWnd::OnChildNotify(message, wParam, lParam, pResult);
    ...
    ...
    DrawItem((LPDRAWITEMSTRUCT)lParam);
    return TRUE;
    }
    virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
    void CStatusBarCtrl::DrawItem(LPDRAWITEMSTRUCT)
    {
    ASSERT(FALSE); // must override for self draw status bars
    }

      你重载CSTatusBarCtrl类的DrawItem虚拟函数,即可实现对反射消息WM_DRAWITEM的处理。

    方式2:
      从方式1可以看出,如果你不在被重载的OnChildNotify中对消息进行处理,函数会调用CWnd::OnChildNotify,它调用ReflectChildNotify函数进行标准的处理。
    1、增加反射消息的映射入口。
    2、增加对应的消息处理函数。
    注意:可以使用MFC的ClassWizard作上述动作,在ClassWizard中,可处理的反射消息以一个"="号以示区别。返回值为TRUE,表示控件窗口已处理此反射消息,为FALSE,表示控件子窗口未处理此反射消息。

    结语:

      消息反射不是很难的概念。它仅出现在MFC中;它的用意是方便控制子窗口的重用;对某些通知消息你可以重载对应的虚函数(WM_DRAWITEM...)进行处理;对其它你可以使用标准的消息反射映射进行处理。限于篇幅,一些细节问题,请阅读MFC中对应的源代码。

  • 相关阅读:
    深入了解ZooKeeper(一)
    ZooKeeper初探之安装和配置
    Java网络编程(TCP协议-服务端和客户端交互)
    Java网络编程(TCP服务端)
    Java网络编程(TCP客户端)
    Java网络编程(UDP协议-聊天程序)
    Java网络编程(UDP协议:接收端)
    Java网络编程(UDP协议:发送端)
    声明了包的类Java命令找不到或无法加载主类
    Java中的IP对象以及本地域名解析
  • 原文地址:https://www.cnblogs.com/dayouluo/p/201651.html
Copyright © 2020-2023  润新知