使用的一个例子
这里面我们举一个简单的例子,希望大家能够更清晰的掌握消息反射机制。
(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文件中加入#
那么,消息反射是怎样实现的呢?源代码说明一切!父窗口在接收到子控件的通知消息时调用虚的消息响应函数CWnd::OnNotify(),代码如下:
BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult)
{
ASSERT(pResult != NULL);
NMHDR* pNMHDR = (NMHDR*)lParam;
HWND hWndCtrl = pNMHDR->hwndFrom;
// get the child ID from the window itself
UINT nID = _AfxGetDlgCtrlID(hWndCtrl);
int nCode = pNMHDR->code;
ASSERT(hWndCtrl != NULL);
ASSERT(::IsWindow(hWndCtrl));
if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd)
return TRUE; // locked out - ignore control notification
// reflect notification to child window control
if (ReflectLastMsg(hWndCtrl, pResult))
return TRUE; // eaten by child
AFX_NOTIFY notify;
notify.pResult = pResult;
notify.pNMHDR = pNMHDR;
return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL);
}
接着看ReflectLastMsg()函数:
BOOL PASCAL CWnd::ReflectLastMsg(HWND hWndChild, LRESULT* pResult)
{
// get the map, and if no map, then this message does not need reflection
CHandleMap* pMap = afxMapHWND();
if (pMap == NULL)
return FALSE;
// check if in permanent map, if it is reflect it (could be OLE control)
CWnd* pWnd = (CWnd*)pMap->LookupPermanent(hWndChild);
ASSERT(pWnd == NULL || pWnd->m_hWnd == hWndChild);
if (pWnd == NULL)
{
#ifndef _AFX_NO_OCC_SUPPORT
// check if the window is an OLE control
CWnd* pWndParent = (CWnd*)pMap->LookupPermanent(::GetParent(hWndChild));
if (pWndParent != NULL && pWndParent->m_pCtrlCont != NULL)
{
// If a matching control site exists, it's an OLE control
COleControlSite* pSite = (COleControlSite*)pWndParent->
m_pCtrlCont->m_siteMap.GetValueAt(hWndChild);
if (pSite != NULL)
{
CWnd wndTemp(hWndChild);
wndTemp.m_pCtrlSite = pSite;
LRESULT lResult = wndTemp.SendChildNotifyLastMsg(pResult);
wndTemp.m_hWnd = NULL;
return lResult;
}
}
#endif //!_AFX_NO_OCC_SUPPORT
return FALSE;
}
// only OLE controls and permanent windows will get reflected msgs
ASSERT(pWnd != NULL);
return pWnd->SendChildNotifyLastMsg(pResult);
}
注意红色代码!此时调用的是子控件的SendChildNotifyLastMsg() 。继续看SendChildNotifyLastMsg():
BOOL CWnd::SendChildNotifyLastMsg(LRESULT* pResult)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
return OnChildNotify(pThreadState->m_lastSentMsg.message,
pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam, pResult);
}
调用子控件的虚函数OnChildNotify函数,进行处理。 如果没有处理,则调用ReflectChildNotify(...)函数进行标准的反射消息的消息映射处理。 如果在ReflectChildNotify(...)中此消息还没被处理,就返回到CWnd::OnNotify(...)中调用OnCmdMsg(...)处理,这样,父窗口就可以响应此消息了。
文章出处:http://www.diybl.com/course/3_program/c++/cppsl/2008520/117221.html
下面是我抄下来的源码,作补充
BOOL CWnd::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
#ifndef _AFX_NO_OCC_SUPPORT
if (m_pCtrlSite != NULL) //这里表示没有作MAP表映射
{
// first forward raw OCM_ messages to OLE control sources
LRESULT lResult = SendMessage(OCM__BASE+uMsg, wParam, lParam);
if (uMsg >= WM_CTLCOLORMSGBOX && uMsg <= WM_CTLCOLORSTATIC &&
(HBRUSH)lResult == NULL)
{
// for WM_CTLCOLOR msgs, returning NULL implies continue routing
return FALSE;
}
if (pResult != NULL)
*pResult = lResult;
return TRUE;
}
#endif
return ReflectChildNotify(uMsg, wParam, lParam, pResult); //使用默认的反射处理
}
BOOL CWnd::ReflectChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// Note: reflected messages are send directly to CWnd::OnWndMsg
// and CWnd::OnCmdMsg for speed and because these messages are not
// routed by normal OnCmdMsg routing (they are only dispatched)
switch (uMsg)
{
// normal messages (just wParam, lParam through OnWndMsg)
case WM_HSCROLL:
case WM_VSCROLL:
case WM_PARENTNOTIFY:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_DELETEITEM:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
case WM_COMPAREITEM:
// reflect the message through the message map as WM_REFLECT_BASE+uMsg
return CWnd::OnWndMsg(WM_REFLECT_BASE+uMsg, wParam, lParam, pResult);
// special case for WM_COMMAND
case WM_COMMAND:
{
// reflect the message through the message map as OCM_COMMAND
int nCode = HIWORD(wParam);
if (CWnd::OnCmdMsg(0, MAKELONG(nCode, WM_REFLECT_BASE+WM_COMMAND), NULL, NULL))
{
if (pResult != NULL)
*pResult = 1;
return TRUE;
}
}
break;
// special case for WM_NOTIFY
case WM_NOTIFY:
{
// reflect the message through the message map as OCM_NOTIFY
NMHDR* pNMHDR = (NMHDR*)lParam;
int nCode = pNMHDR->code;
AFX_NOTIFY notify;
notify.pResult = pResult;
notify.pNMHDR = pNMHDR;
return CWnd::OnCmdMsg(0, MAKELONG(nCode, WM_REFLECT_BASE+WM_NOTIFY), ¬ify, NULL); //这里表示默认情况下给窗体发WM_NOTIFY反射消息, 而CWnd默认是不处理WM_NOTIFY消息的,所以最终返回FALSE,这样WM_NOTIFY就交给父窗体去处理了
}
// other special cases (WM_CTLCOLOR family)
default:
if (uMsg >= WM_CTLCOLORMSGBOX && uMsg <= WM_CTLCOLORSTATIC)
{
// fill in special struct for compatiblity with 16-bit WM_CTLCOLOR
AFX_CTLCOLOR ctl;
ctl.hDC = (HDC)wParam;
ctl.nCtlType = uMsg - WM_CTLCOLORMSGBOX;
//ASSERT(ctl.nCtlType >= CTLCOLOR_MSGBOX);
ASSERT(ctl.nCtlType <= CTLCOLOR_STATIC);
// reflect the message through the message map as OCM_CTLCOLOR
BOOL bResult = CWnd::OnWndMsg(WM_REFLECT_BASE+WM_CTLCOLOR, 0, (LPARAM)&ctl, pResult);
if ((HBRUSH)*pResult == NULL)
bResult = FALSE;
return bResult;
}
break;
}
return FALSE; // let the parent handle it
}
BOOL CWnd::OnWndMsg(....)
{
....
for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL;
pMessageMap = (*pMessageMap->pfnGetBaseMap)()) //这里是查消息映射表
{
// Note: catches BEGIN_MESSAGE_MAP(CMyClass, CMyClass)!
ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);
if (lpEntry != NULL)
{
// found it
#ifdef _DEBUG
if (nCode == CN_COMMAND)
TRACE(traceCmdRouting, 1, "SENDING command id 0x%04X to %hs target./n", nID,
GetRuntimeClass()->m_lpszClassName);
else if (nCode > CN_COMMAND)
TRACE(traceCmdRouting, 1, "SENDING control notification %d from control id 0x%04X to %hs window./n",
nCode, nID, GetRuntimeClass()->m_lpszClassName);
#endif //_DEBUG
return _AfxDispatchCmdMsg(this, nID, nCode,
lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo); //调用子窗体自身的WndProc过程的WM_NOTIFY响应部分
}
}
....
总结:消息反射并不是反射消息(父窗体向子窗体返回消息),而是当父窗体收到子窗体通知消息后(如WM_COMMAND、WM_NOTIFY),DISPATCH会调用父窗体的处理函数(一般CWnd等基类会定义如CWnd::OnCommand CWnd::OnNotify),在这里,父窗体会试图调用子窗体的ReflectChildNotify()函数,而ReflectChildNotify给了子窗体自己一个扫描反射消息映射表的机会,有则调用AfxDispatchMessage进行处理,并返回TRUE,通知父窗体OnNotify过程不用处理通知了。
另外,MFC基类默认已经书写了一些消息处理函数,如CWnd::OnNotify等,在这些函数里,它们搜索自己关心的消息MAP表,并进行处理。这就是消息MAP的本质。如果我们不用MAP表,完全可以在OnWndMsg或是WndProc里进行处理。关于这些函数的调用级别是:
WndProc()
-> OnWndMsg()
-> OnNotify()
->OnCmdMsg()
->消息MAP表
->AfxDispatchMessage 即 直接调用处理函数
DefWindowProc()
copyright:2016-2020|邮箱:imalib@vip.163.com
蜀ICP备16020986号