标题：消息反射
消息反射的基础知识
1、消息反射解释：父窗口将控制子窗口发给它的通知消息，首先反射回子窗口进行处理（即给控制子窗口一个机会，让控制子窗口处理此消息），这样通知消息就有机会能被子窗口自身进行处理。2、MFC中引入消息反射的原因：在Windows的消息处理中，控制子窗口的发给其父窗口的通知消息只能由其父窗口进行处理，这使得控制子窗口的自身能动性大大降低（你想，它连改变自己的背景色，处理一个自身滚动问题都要其父窗口来完成），为了解决这个问题，在MFC中引入了反射消息“Reflect Message”的概念，进行消息反射，可以使得控制子窗口能够自行处理与自身相关的一些消息，增强了封装性，从而提高了控制子窗口的可重用性。3、消息反射的处理流程(不考虑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), &not;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中对应的源代码
什么是消息反射？
在windows里面，子控件经常向父控件发送消息，例如很多子控件要绘制自己的背景，就可能向父窗口发送消息WM_CTLCOLOR。对于从子控件发来的消息，父控件有可能在处理之前，把消息返还给子控件处理，这样消息看起来就想是从父窗口反射回来一样，故此得名：消息反射。
消息反射的由来
在windows和MFC4.0版本一下，父窗口（通常是一个对话框）会对这些消息进行处理，换句话说，子控件的这些消息处理必须在父窗口类体内，每当我们添加子控件的时候，就要在父窗口类中复制这些代码，我们可以想象这是多么的复杂，代码是多么的臃肿！
我们可以想象，如果这些消息都让父窗口类去做，父窗口就成了一个万能的神，一个臃肿不堪的代码机，无论如何消息的处理都集中在父窗口类中，会使父窗口繁重无比，但是子控件却无事可做，并且代码也无法重用，这对于一个程序员来讲是多么痛苦的一件事？！
在老版本的MFC中，设计者也意识到了这个问题，他们对一些消息采用了虚拟机制，例如：WM_DRAWITEM，这样子控件就有机会控制自己的动作，代码的可重用性有了一定的提高，但是这还没有达到大部分人的要求，所以在高版本的MFC中，提出了一种更方便的机制：消息反射。
通过消息反射机制，子控件窗口便能够自行处理与自身相关的一些消息，增强了封装性，同时也提高了子控件窗口类的可重用性。不过需要注意的是：消息反射是MFC实现的，不是windows实现的；要让你的消息反射机制工作，你得类必须从CWnd类派生。
Message-Map中的处理
如果想要处理消息反射，必须了解相应的Message-Map宏和函数原型。一般来讲，Message-Map是有一定的规律的，通常她在消息的前面加上一个ON_ ，然后再消息的最后加上 _REFLECT。例如我们前面提到的WM_CTLCOLOR 经过处理后变成了ON_WM_CTLCOLOR_REFLECT；WM_MEASUREITEM则变成了ON_WM_MEASUREITEM_REFLECT。
凡事总会有例外，这里也是这样，这里面有3个例外：
(1) WM_COMMAND 转换成 ON_CONTROL_REFLECT；
(2) WM_NOTIFY 转换成 ON_NOTIFY_REFLECT；
(3) ON_UPDATE_COMMAND_UI 转换成 ON_UPDATE_COMMAND_UI_REFLECT；
对于函数原型，也必须是以 afx_msg 开头。
利用ClassWizard添加消息反射
(1)在ClassWizard中，打开选择项Message Maps；
(2)在下拉列表Class name中选择你要控制的类；
(3)在Object IDs中，选中相应的类名；
(4)在Messages一栏中找到前面带有=标记的消息，那就是反射消息；
(5)双击鼠标或者单击添加按钮，然后OK!
消息处理的过程
(1)子窗口向父窗口发送通知消息，激发父窗口去调用它的虚函数CWnd::OnNotify。大致的结构如下
BOOL CWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
if (ReflectLastMsg(hWndCtrl, pResult)) file://hWndCtrl,为发送窗口
return TRUE; file://如果子窗口已处理了此消息，返回
AFX_NOTIFY notify;
notify.pResult = pResult;
notify.pNMHDR = pNMHDR;
return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY)? notify:NULL);
}
(2)ReflectLastMsg声明如下：static BOOL PASCAL ReflectLastMsg(HWND hWndChild, LRESULT* pResult = NULL);
它的主要任务就是调用发送窗口的SendChildNotifyLastMsg。 
(3)SendChildNotifyLastMsg声明如下：BOOL SendChildNotifyLastMsg(LRESULT* pResult = NULL);
调用发送窗口的虚函数OnChildNotify函数，进行处理。 如果发送窗口没有进行重载处理，则调用ReflectChildNotify(...)函数进行标准的反射消息的消息映射处理。
使用的一个例子
这里面我们举一个简单的例子，希望大家能够更清晰的掌握消息反射机制。
(1)创建一个基于对话框的工程。
(2)利用向导创建一个新的类：CMyEdit，基类是CEdit。
(3)在CMyEdit头文件中加入3个成员变量：
COLORREF m_clrText ;
COLORREF m_clrBkgnd ;
CBrush m_brBkgnd;
(4)利用向导在其中加入WM_CTLCOLOR（看到了么，前面是不是有一个=？），并且将它的函数体改为：
HBRUSH CMyEdit::CtlColor(CDC* pDC, UINT nCtlColor) 
{
pDC->SetTextColor( m_clrText ); // text
pDC->SetBkColor( m_clrBkgnd ); // text bkgnd
return m_brBkgnd; // ctl bkgnd
}
同时我们在.cpp文件中会看到ON_WM_CTLCOLOR_REFLECT()，这就是我们所说的经过处理的宏，是不是很符合规则？
(5)在对话框中加入一个Edit，增加一个关联的变量，选择Control属性，类别为CMyEdit。
(6)在对话框.cpp文件中加入#include "MyEdit.h"，运行。