MFC下WM_NOTIFY消息处理流程

参考文章: MFC的消息反射机制
    在前一篇文章:MFC消息处理流程概述中描述了MFC消息处理的大体流程。由CWnd::OnWndMsg函数可知,当消息为WM_NOTIFY消息时,调用的是virtual CWnd::OnNotify处理。
if (message == WM_NOTIFY)
{
    NMHDR* pNMHDR = (NMHDR*)lParam;
    if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
	goto LReturnTrue;
    return FALSE;
}
BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult)
{
	NMHDR* pNMHDR = (NMHDR*)lParam;
	HWND hWndCtrl = pNMHDR->hwndFrom;

	// get the child ID from the window itself
	UINT_PTR nID = _AfxGetDlgCtrlID(hWndCtrl);
	int nCode = pNMHDR->code;

	// 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((UINT)nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL);
}
    以上hWndCtrl为子窗口的句柄,通过调用ReflectLastMsg(hWndCtrl, pResult)给子窗口一个自身处理的机会,将消息反射给子窗口处理。ReflectLastMsg返回TRUE,表明子窗口处理了此消息,则OnNotify返回并不交由父窗口处理;反之,表示子窗口未处理此消息,此时,调用OnCmdMsg(...)由父窗口进行处理。
    ReflectLastMsg通过层层调用最终会调用CWnd::ReflectChildNotify函数来处理WM_NOTIFY反射消息。
BOOL PASCAL CWnd::ReflectLastMsg(HWND hWndChild, LRESULT* pResult)
{
    CWnd* pWnd = (CWnd*)pMap->LookupPermanent(hWndChild);
    return pWnd->SendChildNotifyLastMsg(pResult);
}

BOOL CWnd::SendChildNotifyLastMsg(LRESULT* pResult)
{
    return OnChildNotify(pThreadState->m_lastSentMsg.message,
        pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam, pResult);
}

BOOL CWnd::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    return ReflectChildNotify(uMsg, wParam, lParam, pResult);
}
    CWnd::ReflectChildNotify函数如下:
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)
	{
        ......
	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);
		}
        ......
}
    很显然,调用了virtual CWnd::OnCmdMsg函数来处理,实际上是virtual CCmdTarget::OnCmdMsg函数,CWnd继承自CCmdTarget。

注意:以上return CWnd::OnCmdMsg(0, MAKELONG(nCode, WM_REFLECT_BASE+WM_NOTIFY), ¬ify, NULL)语句,在WM_NOTIFY的基础上+WM_REFLECT_BASE,因为消息流程走到这步,是在子控件窗口的消息映射表中查找反射消息处理函数。
    注意区分WM_NOTIFY消息与WM_NOTIFY反射消息,WM_NOTIFY反射消息即消息WM_REFLECT_BASE+WM_NOTIFY,父窗口收到WM_NOTIFY消息而扔给子窗口处理时,为了区分,子窗口处理的消息被译成WM_REFLECT_BASE+WM_NOTIFY消息,否则岂不和子窗口本身的WM_NOTIFY消息混淆了。

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
	AFX_CMDHANDLERINFO* pHandlerInfo)
{
    //从消息映射中查找对应的处理函数
    for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL;
	  pMessageMap = (*pMessageMap->pfnGetBaseMap)())
	{
		lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);
		if (lpEntry != NULL)
		{
			// 找到对应的消息处理函数
			return _AfxDispatchCmdMsg(this, nID, nCode,
				lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
		}
	}
	return FALSE;   // not handled  没有对应的消息处理函数则返回FALSE
}
    在子控件窗口类的消息映射表中没有找到对应的WM_REFLECT_BASE+WM_WM_NOTIFY消息(即对应WM_NOTIFY的反射消息)处理函数返回FALSE,也即以上ReflectLastMsg(hWndCtrl, pResult)返回FALSE,这样父窗口就会处理。
    否则,在_AfxDispatchCmdMsg函数中会调用对应的反射消息处理函数处理,ReflectLastMsg(hWndCtrl, pResult)返回TRUE,这样父窗口就不会处理了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Visual C++/MFC 入门教程闻怡洋 ON_WM_CREATEEND_MESSAGE_MAP2.2 在窗口中输出文字.2.3 使用点、刷子、笔进行绘2.4 在窗口中绘制设备相关位图、图标、设备无关位图CDC memDC;//定义一个兼容DC2.5 使用各种映射方式2.6 多边形和剪贴区域3.1 文档视图框架窗口间的关系和消息传送规律3.2 接收用户输入DrawRect();//恢复上次所画的矩形DrawRect();//恢复上次所画的矩形3.4 文档、视、框架之间相互作用//修改链表CView 类是最基本的视类只支持最基本的操作4.1 Button4.2 Static 4.3 Edit Box4.4 Scroll Bar4.5 List Box/Check List Box4.6 Combo Box/Combo Box Ex4.7 Tree CtrlCtrl4.9 Tab CtrlDialog Bar 支持ON_UPDATE_COMMAND_UI 的相关操作,如SetText,Enable。........... 45 ID_SEPARATOR, // status line indicatorON_WM_CREATE46 //修改OnCreate 函数,重新设置状态条第二部分ID 值47 5.1 使用资源编辑器编辑对话框51 5.2 创建有模式对话框52 5.3 创建无模式对话框54 END_MESSAGE_MAP() 56 5.4 在对话框中进行消息映射57 5.5 在对话框中进行数据交换和数据检查60 5.6 使用属性对话框63 5.7 使用通用对话框65 CString CFileDialog::GetFileName( ) 得到完整的文件名,包括扩展名如:test1.txt 66 CString CFileDialog::GetExtName( ) 得到完整的文件扩展名,如:txt 66 CString CFileDialog::GetFileTitle ( ) 得到完整的文件名,不包括目录名和扩展名如:test1 66 CString CFontDialog::GetFaceName( ) 得到所选字体名字。.....................................................67 COLORREF CFontDialog::GetColor( ) 得到所选字体的颜色。................................................ 67 BOOL CFontDialog::IsStrikeOut( )................................................................................................. 67 BOOL CFontDialog::IsUnderline( ).................................................................................................67 5.8 建立以对话框为基础的应4.F 关于WM_NOTIFY 的使用方法 第五章对话框 5.1 使用资源编辑器编辑对话框 5.2 创建有模式对话框 5.3 创建无模式对话框 5.4 在对话框中进行消息映射 5.5 在对话框中进行数据交换和数据检查 5.6 使用属性对话框 5.7 使用通用对话框 5.8 建立以对话框为基础的应用 5.9 使用对话框作为子窗口 第六章网络通信开发 6.1 WinSock 介绍 6.2 利用WinSock 进行无连接的通信 6.3 利用WinSock 建立有连接的通信

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值