参考文章:MFC的消息反射机制

    在前一篇文章:MFC消息处理流程概述中描述了MFC消息处理的大体流程。由CWnd::OnWndMsg函数可知,当消息为WM_NOTIFY消息时,调用的是virtual CWnd::OnNotify处理。
  1. if (message == WM_NOTIFY)
  2. {
  3. NMHDR* pNMHDR = (NMHDR*)lParam;
  4. if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
  5. goto LReturnTrue;
  6. return FALSE;
  7. }
  1. BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult)
  2. {
  3. NMHDR* pNMHDR = (NMHDR*)lParam;
  4. HWND hWndCtrl = pNMHDR->hwndFrom;
  5. // get the child ID from the window itself
  6. UINT_PTR nID = _AfxGetDlgCtrlID(hWndCtrl);
  7. int nCode = pNMHDR->code;
  8. // reflect notification to child window control
  9. if (ReflectLastMsg(hWndCtrl, pResult))
  10. return TRUE;        // eaten by child
  11. AFX_NOTIFY notify;
  12. notify.pResult = pResult;
  13. notify.pNMHDR = pNMHDR;
  14. return OnCmdMsg((UINT)nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL);
  15. }
    以上hWndCtrl为子窗口的句柄,通过调用ReflectLastMsg(hWndCtrl, pResult)给子窗口一个自身处理的机会,将消息反射给子窗口处理。ReflectLastMsg返回TRUE,表明子窗口处理了此消息,则OnNotify返回并不交由父窗口处理;反之,表示子窗口未处理此消息,此时,调用OnCmdMsg(...)由父窗口进行处理。
ReflectLastMsg通过层层调用最终会调用CWnd::ReflectChildNotify函数来处理WM_NOTIFY反射消息。
  1. BOOL PASCAL CWnd::ReflectLastMsg(HWND hWndChild, LRESULT* pResult)
  2. {
  3. CWnd* pWnd = (CWnd*)pMap->LookupPermanent(hWndChild);
  4. return pWnd->SendChildNotifyLastMsg(pResult);
  5. }
  6. BOOL CWnd::SendChildNotifyLastMsg(LRESULT* pResult)
  7. {
  8. return OnChildNotify(pThreadState->m_lastSentMsg.message,
  9. pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam, pResult);
  10. }
  11. BOOL CWnd::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  12. {
  13. return ReflectChildNotify(uMsg, wParam, lParam, pResult);
  14. }
    CWnd::ReflectChildNotify函数如下:
  1. BOOL CWnd::ReflectChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  2. {
  3. // Note: reflected messages are send directly to CWnd::OnWndMsg
  4. //  and CWnd::OnCmdMsg for speed and because these messages are not
  5. //  routed by normal OnCmdMsg routing (they are only dispatched)
  6. switch (uMsg)
  7. {
  8. ......
  9. case WM_NOTIFY:
  10. {
  11. // reflect the message through the message map as OCM_NOTIFY
  12. NMHDR* pNMHDR = (NMHDR*)lParam;
  13. int nCode = pNMHDR->code;
  14. AFX_NOTIFY notify;
  15. notify.pResult = pResult;
  16. notify.pNMHDR = pNMHDR;
  17. return CWnd::OnCmdMsg(0, MAKELONG(nCode, WM_REFLECT_BASE+WM_NOTIFY), ¬ify, NULL);
  18. }
  19. ......
  20. }
    很显然,调用了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消息混淆了。

  1. BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  2. AFX_CMDHANDLERINFO* pHandlerInfo)
  3. {
  4. //从消息映射中查找对应的处理函数
  5. for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL;
  6. pMessageMap = (*pMessageMap->pfnGetBaseMap)())
  7. {
  8. lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);
  9. if (lpEntry != NULL)
  10. {
  11. // 找到对应的消息处理函数
  12. return _AfxDispatchCmdMsg(this, nID, nCode,
  13. lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
  14. }
  15. }
  16. return FALSE;   // not handled  没有对应的消息处理函数则返回FALSE
  17. }
    在子控件窗口类的消息映射表中没有找到对应的WM_REFLECT_BASE+WM_WM_NOTIFY消息(即对应WM_NOTIFY的反射消息)处理函数返回FALSE,也即以上ReflectLastMsg(hWndCtrl, pResult)返回FALSE,这样父窗口就会处理。
否则,在_AfxDispatchCmdMsg函数中会调用对应的反射消息处理函数处理,ReflectLastMsg(hWndCtrl, pResult)返回TRUE,这样父窗口就不会处理了。

http://blog.csdn.net/hisinwang/article/details/8045382

MFC下WM_NOTIFY消息处理流程的更多相关文章

  1. WM_NOTIFY消息流程实例分析

    我们以CListCtrl控件为例来分析WM_NOTIFY消息. CListCtrl控件在Report样式下会包含CHeaderCtrl标头控件,即CHeaderCtrl标头控件为CListCtrl控件 ...

  2. MFC下OpenGL入门(可以用)

    MFC下OpenGL入门 源文件 1, 建一工程文件,我这里命名为first,现在first工程里面我们没有添加任何东西,所有的东西都是MFC自动帮我们创建的. 2, 添加链接库.这一步很关键.打开菜 ...

  3. [Sciter系列] MFC下的Sciter–4.HTML与图片资源内置

    [Sciter系列] MFC下的Sciter–4.HTML与图片资源内置,防止代码泄露. 本系列文章的目的就是一步步构建出一个功能可用,接口基本完善的基于MFC框架的SciterFrame程序,以此作 ...

  4. [Sciter系列] MFC下的Sciter–2.Sciter中的事件,tiscript,语法

    [Sciter系列] MFC下的Sciter–2.Sciter中的事件,tiscript,CSS部分自觉学习,重点说明Tiscript部分的常见语法和事件用法. 本系列文章的目的就是一步步构建出一个功 ...

  5. vc++深入跟踪MFC程序的执行流程

    在MFC程序设计的学习过程中最令人感到难受,甚至于有时会动摇学习者信心的就是一种对于程序的一切细节都没有控制权的感觉.这种感觉来源于学习者不知道一个MFC程序是如何运行起来的(即一个MFC程序的执行流 ...

  6. wxWidgets:消息处理流程

    首先解释下EventHandler. wxWidgets中EventHandler并不是简单的指消息(事件)处理函数,而是一个用于处理窗口系统消息的类.收到消息后,wxEventHandler会调用e ...

  7. 深入跟踪MFC程序的执行流程

    来源: http://blog.csdn.net/ljianhui/article/details/8781991 在MFC程序设计的学习过程中最令人感到难受,甚至于有时会动摇学习者信心的就是一种对于 ...

  8. MFC下CSocket编程详解(转)

    原文转自 http://blog.csdn.net/yejiansnake/article/details/2175778 MFC下CSocket编程详解: 1. 常用的函数和注意事项(详细的函数接口 ...

  9. MFC下调用控制台和控制台下MFC库的支持

    1.MFC下调用控制台 在CWinApp的InitInstance中对话框的DoModal之前加入 AllocConsole(); // 开辟控制台 SetConsoleTitle(_T(" ...

随机推荐

  1. HDOJ 5409 CRB and Graph 无向图缩块

    无向图缩块后,以n所在的块为根节点,dp找每块中的最大值. 对于每一个桥的答案为两块中的较小的最大值和较小的最大值加1 CRB and Graph Time Limit: 8000/4000 MS ( ...

  2. Spring Boot使用模板freemarker【从零开始学Spring Boot(转)

    视频&交流平台: à SpringBoot网易云课堂视频 http://study.163.com/course/introduction.htm?courseId=1004329008 à  ...

  3. Java ThreadLocal Example(java中的ThreadLocal例子)

    Java ThreadLocal is used to create thread local variables. We know that all threads of an Object sha ...

  4. [Angular] Design API for show / hide components based on Auth

    Simple Auth service: import { Injectable } from '@angular/core'; import {HttpClient} from '@angular/ ...

  5. [Android 4.4.2] 泛泰A870 Mokee4.4.2 20140531 RC1.0 by syhost

    欢迎关注泛泰非盈利专业第三方开发团队 VegaDevTeam  (本team 由 syhost suky zhaochengw(z大) xuefy(大星星) tenfar(R大师) loogeo cr ...

  6. 使用四种框架分别实现百万websocket常连接的服务器--转

    原文地址:http://colobu.com/2015/05/22/implement-C1000K-servers-by-spray-netty-undertow-and-node-js/#Nett ...

  7. Win10系统如何设置所有程序默认以管理员身份运行?

    原文:Win10系统如何设置所有程序默认以管理员身份运行? 在win10系统中有些用户发现一些程序只有使用管理员身份运行能才打开,这样的话就感觉会麻烦很多,那么有没有办法设置所有程序都默认以管理员身份 ...

  8. Linux环境编程之共享内存区(一):共享内存区简单介绍

    共享内存区是可用IPC形式中最快的.一旦内存区映射到共享它的进程的地址空间,进程间数据的传递就不再涉及内核.然而往该共享内存区存放信息或从中取走信息的进程间通常须要某种形式的同步.不再涉及内核是指:进 ...

  9. PHP解决约瑟夫环问题

    PHP解决约瑟夫环问题 一.总结 二.PHP解决约瑟夫环问题 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到 ...

  10. 算法 Tricks(四)—— 判断序列中的字符/数值是否交替出现

    比如:353, 54545,数字都是交替出现的: bool alternate = true; for (int i = 0; i < M.size(); ++i){ if (M[i] != M ...