分享

WM_NOTIFY与消息反射—耗费我两天时间才解决的问题

 DCW 2010-12-16
关于WM_NOTIFY与消息反射—耗费我两天时间才解决的问题
www. 时间 : 2010-06-22 作者:佚名 编辑:壹枝雪糕 点击: 1306 [ 评论 ]
综合资源电子书社区 其实,问题很简单,我想在listctrl响应NM_SETFOCUS的同时通知其父窗口(其实我这句话说错了,listctrl只能响应=NM_SETFOCUS,为什么有个“=”呢?稍后解释),最幼稚的想法是让在listctrl和父窗口中都添加对此消息的响应,很不幸,我在一开始就是这么想的-_-| 。。。很明显我失败了!
后来我又发现,如果在listctrl中添加对=NM_SETFOCUS的响应,父窗口就无法响应NM_SETFOCUS,反之就可以响应。于是很容易想到在listctrl的消息响应中SendMessage给父窗口,控件发送的消息有两种:WM_COMMAND和WM_NOTIFY,像button,edit,combobox等控件,主要使用WM_COMMAND消息,而listctrl这类控件主要使用WM_NOTIFY消息,发送消息时有两个很重要的参数wParam和lParam,MSDN说明如下:
lResult = SendMessage( // returns LRESULT in lResult
(HWND) hWndControl, // handle to destination control
(UINT) WM_NOTIFY, // message ID
(WPARAM) wParam, // = (WPARAM) (int) idCtrl;
(LPARAM) lParam // = (LPARAM) (LPNMHDR) pnmh;
);
结果我还是失败了!折腾了一天一夜,直到晚上熄灯,我就不信我弄不出来,第二天上CSDN求救,有一个同志的回答提醒了我:消息反射。消息反射是个什么东西,我还是第一次听到,看来我真是菜鸟。于是查阅了一下消息反射的资料,原来,控件根本无法直接给自己发送消息,这样就导致控件无法控制自己,所有的控制必须在父窗口类中实现,这样不符合面向对象的思想,于是出现了消息反射;即子控件发送给父窗口的消息被父窗口马上反射回来,如果子控件响应了反射消息,父窗口就不响应,反之则响应。这就和我刚才发现的现象吻合了。
 那么,消息反射是怎样实现的呢?源代码说明一切!父窗口在接收到子控件的通知消息时调用虚的消息响应函数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), &notify, 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(...)处理,这样,父窗口就可以响应此消息了。
 下面回到我最初的问题中来,我想在listctrl和父窗口中都处理此消息,因此我们可以重载OnNotify(...)如下:
BOOL CWellListView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
 // TODO: Add your specialized code here and/or call the base class
 ASSERT(pResult != NULL);
 NMHDR* pNMHDR = (NMHDR*)lParam;
 HWND hWndCtrl = pNMHDR->hwndFrom;
 
ReflectLastMsg(hWndCtrl, pResult);
 
UINT nID = _AfxGetDlgCtrlID(hWndCtrl);
 int nCode = pNMHDR->code;
 
AFX_NOTIFY notify;
 notify.pResult = pResult;
 notify.pNMHDR = pNMHDR;
 return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), &notify, NULL);
 }//CWellListView继承子CFormView,相当与对话框
还要添加两个头文件:
#include "AFXPRIV.H"
#include "C:\Program Files\Microsoft Visual Studio\VC98\MFC\SRC\AFXIMPL.H"
看到它和CWnd::OnNotify(...)的区别了没?恩,就是这样!
 但是,很不幸,我最终没有采取这种办法,因为我在一开始就没有想到最佳方案,我完全可以用指针来实现我的需求,在子控件类中需要的地方添加如下代码就可以操作父窗口了:
CWellListView* listview=(CWellListView*)GetParent();
 因此,在以后的编程学习中,我们应该尽量用指针操作来代替发送消息来实现我们的需求!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多