原文网址:http://blog.csdn.net/hyhnoproblem/article/details/6182585

该示例通过研究基本的单文档程序的“文件”--“打开”命令,分析WM_COMMAND消息投递流程。基于VS 2005 代码

AfxWndProc最终调用的是OnWndMsg,这个函数负责消息的分发处理。当消息是WM_COMMAND时,将消息投递给OnCommand函数。

  1. // wincore.cpp 1746
  2. BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  3. {
  4. LRESULT lResult = 0;
  5. union MessageMapFunctions mmf;
  6. mmf.pfn = 0;
  7. CInternalGlobalLock winMsgLock;
  8. // special case for commands
  9. if (message == WM_COMMAND)
  10. {
  11. if (OnCommand(wParam, lParam))
  12. {
  13. lResult = 1;
  14. goto LReturnTrue;
  15. }
  16. return FALSE;
  17. }
  18. //...
  19. }

OnCommand是个虚函数,因为消息是主窗口产生的,所以调用的是CFrameWnd::OnCommand函数该函数先检查该消息是不是在线请求帮助,如果是,则程序给框架窗口发送一个WM_COMMANDHELP消息,否则交由基类CWnd::OnCommand()处理。

  1. // winfrm.cpp 299
  2. BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
  3. // return TRUE if command invocation was attempted
  4. {
  5. HWND hWndCtrl = (HWND)lParam;
  6. UINT nID = LOWORD(wParam);
  7. CFrameWnd* pFrameWnd = GetTopLevelFrame();
  8. ENSURE_VALID(pFrameWnd);
  9. if (pFrameWnd->m_bHelpMode && hWndCtrl == NULL &&
  10. nID != ID_HELP && nID != ID_DEFAULT_HELP && nID != ID_CONTEXT_HELP)
  11. {
  12. // route as help
  13. if (!SendMessage(WM_COMMANDHELP, 0, HID_BASE_COMMAND+nID))
  14. SendMessage(WM_COMMAND, ID_DEFAULT_HELP);
  15. return TRUE;
  16. }
  17. // route as normal command
  18. return CWnd::OnCommand(wParam, lParam);
  19. }

CWnd::OnCommand首先检查表示控件的LPARAM。如果消息是由控件产生的,LPARAM就会包含控件窗口句柄。如果消息是控件通知,框架就会执行特定的处理过程。如果消息是为某个控件产生的,OnCommand会将消息直接发送给控件,然后OnCommand返回。否则,CWnd::OnCommand()要确保产生命令的界面元素没有被禁用,然后将消息传递给OnCmdMsg函数,调用的是CFrameWnd::OnCmdMsg函数

  1. // wParam
  2. //  The high-order word specifies the notification code if the message is from a control.
  3. //  If the message is from an accelerator, this value is 1. If the message is from a menu,
  4. //  this value is zero.
  5. //  The low-order word specifies the identifier of the menu item, control, or accelerator.
  6. // lParam
  7. //  Handle to the control sending the message if the message is from a control.
  8. //  Otherwise, this parameter is NULL.
  9. BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
  10. // return TRUE if command invocation was attempted
  11. {
  12. UINT nID = LOWORD(wParam);
  13. HWND hWndCtrl = (HWND)lParam;
  14. int nCode = HIWORD(wParam);
  15. // default routing for command messages (through closure table)
  16. if (hWndCtrl == NULL)
  17. {   // 菜单命令先走这里
  18. // zero IDs for normal commands are not allowed
  19. if (nID == 0)
  20. return FALSE;
  21. // make sure command has not become disabled before routing
  22. CTestCmdUI state;
  23. state.m_nID = nID;
  24. OnCmdMsg(nID, CN_UPDATE_COMMAND_UI, &state, NULL);      // CFrameWnd::OnCmdMsg
  25. if (!state.m_bEnabled)
  26. {
  27. TRACE(traceAppMsg, 0, "Warning: not executing disabled command %d/n", nID);
  28. return TRUE;
  29. }
  30. // menu or accelerator
  31. nCode = CN_COMMAND;
  32. }
  33. else
  34. {
  35. // ToolBar命令走这里,或控件通知
  36. // control notification
  37. ASSERT(nID == 0 || ::IsWindow(hWndCtrl));
  38. if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd)
  39. return TRUE;        // locked out - ignore control notification
  40. // reflect notification to child window control
  41. if (ReflectLastMsg(hWndCtrl))
  42. return TRUE;    // eaten by child
  43. // zero IDs for normal commands are not allowed
  44. if (nID == 0)
  45. return FALSE;
  46. }
  47. // 调用CFrameWnd::OnCmdMsg
  48. return OnCmdMsg(nID, nCode, NULL, NULL);
  49. }

CFrameWnd::OnCmdMsg函数调用时,pExtra和pHandlerInfo是NULL,因为处理命令不需要这一信息。取出的消息按照以下顺序经过应用程序的各个部分:活动视图、活动视图的文档、文档模板、主窗口、应用程序。

  1. // winfrm.cpp 880
  2. BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  3. AFX_CMDHANDLERINFO* pHandlerInfo)
  4. {
  5. CPushRoutingFrame push(this);
  6. // 将消息传递给活动视图,调用CView::OnCmdMsg
  7. CView* pView = GetActiveView();
  8. if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  9. return TRUE;
  10. // 由框架处理该消息,CWnd没有覆盖OnCmdMsg,调用CCmdTarget::OnCmdMsg
  11. if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  12. return TRUE;
  13. // 将消息传递给应用程序,CWinApp没有覆盖OnCmdMsg,所以调用CCmdTarget::OnCmdTarget
  14. CWinApp* pApp = AfxGetApp();
  15. if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  16. return TRUE;
  17. return FALSE;
  18. }
  19. // viewcore.cpp 154
  20. BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  21. AFX_CMDHANDLERINFO* pHandlerInfo)
  22. {
  23. // 首先由活动视图处理该消息,调用CCmdTarget::OnCmdMsg
  24. if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  25. return TRUE;
  26. // then pump through document
  27. if (m_pDocument != NULL)
  28. {
  29. // 将该消息传递给视图对应的文档,调用CDocument::OnCmdMsg
  30. CPushRoutingView push(this);
  31. return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
  32. }
  33. return FALSE;
  34. }
  35. // doccore.cpp 834
  36. BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  37. AFX_CMDHANDLERINFO* pHandlerInfo)
  38. {
  39. if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  40. return TRUE;
  41. // otherwise check template,调用CDocTemplate::OnCmdMsg
  42. if (m_pDocTemplate != NULL &&
  43. m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  44. return TRUE;
  45. return FALSE;
  46. }
  47. // doctempl.cpp 370
  48. BOOL CDocTemplate::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  49. AFX_CMDHANDLERINFO* pHandlerInfo)
  50. {
  51. BOOL bReturn;
  52. CCmdTarget* pFactory = DYNAMIC_DOWNCAST(CCmdTarget, m_pAttachedFactory);
  53. if (nCode == CN_OLE_UNREGISTER && pFactory != NULL)
  54. bReturn = pFactory->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
  55. else
  56. bReturn = CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
  57. return bReturn;
  58. }

上面的一些函数确定了程序执行流程,最终会调用到CCmdTarget::OnCmdMsg,该函数通过查找消息映射表,进而调用到消息处理函数。

  1. // cmdtarg.cpp 297
  2. BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  3. AFX_CMDHANDLERINFO* pHandlerInfo)
  4. {
  5. // ...
  6. // determine the message number and code (packed into nCode)
  7. const AFX_MSGMAP* pMessageMap;
  8. const AFX_MSGMAP_ENTRY* lpEntry;
  9. UINT nMsg = 0;
  10. //...
  11. if (nCode != CN_UPDATE_COMMAND_UI)
  12. {
  13. nMsg = HIWORD(nCode);
  14. nCode = LOWORD(nCode);
  15. }
  16. //...
  17. // for backward compatibility HIWORD(nCode)==0 is WM_COMMAND
  18. if (nMsg == 0)
  19. nMsg = WM_COMMAND;
  20. // look through message map to see if it applies to us
  21. for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL;
  22. pMessageMap = (*pMessageMap->pfnGetBaseMap)())
  23. {
  24. // Note: catches BEGIN_MESSAGE_MAP(CMyClass, CMyClass)!
  25. ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
  26. lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);
  27. if (lpEntry != NULL)
  28. {
  29. // found it
  30. return _AfxDispatchCmdMsg(this, nID, nCode,
  31. lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
  32. }
  33. }
  34. return FALSE;   // not handled
  35. }

【转】Windows消息投递流程:WM_COMMAND消息流程的更多相关文章

  1. WM_COMMAND消息

    原文地址:https://blog.csdn.net/whm243149796/article/details/78966065 当用户点击菜单.按钮.下拉列表框等控件时候,会触发WM_COMMAND ...

  2. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(五) 之 加好友,加群流程,消息管理和即时消息提示的实现

    前言 前前一篇留了个小问题,在上一篇中忘了写了,就是关于LayIM已经封装好的上传文件或者图片的问题.对接好接口之后,如果上传速度慢,界面就会出现假死情况,虽然文件正在上传.于是我就简单做了个图标替代 ...

  3. Windows各种各种消息投递函数

    1.SendMessage:发送消息给指定的窗口过程:直到窗口过程处理了消息才返回. 2.PostMessage:将消息放入消息队列(与指定窗口创建的线程相关)中:无需等待消息处理,立即返回.   不 ...

  4. [8]windows内核情景分析--窗口消息

    消息与钩子 众所周知,Windows系统是消息驱动的,现在我们就来看Windows的消息机制. 早期的Windows的窗口图形机制是在用户空间实现的,后来为了提高图形处理效率,将这部分移入内核空间,在 ...

  5. skynet1.0阅读笔记2_skynet的消息投递skynet.call

    为了了解 skynet.call 的调用过程,需要先看看 skynet的队列是如何把包分到不同工作线程的.看下图 查看 global_queue 的skynet_globalmq_push和skyne ...

  6. 【转】深入Windows内核——C++中的消息机制

    上节讲了消息的相关概念,本文将进一步聊聊C++中的消息机制. 从简单例子探析核心原理 在讲之前,我们先看一个简单例子:创建一个窗口和两个按钮,用来控制窗口的背景颜色.其效果 图1.效果图  Win32 ...

  7. 深刻:截获windows的消息并分析实例(DefWindowProc),以WM_NCHITTEST举例(Windows下每一个鼠标消息都是由 WM_NCHITTEST 消息产生的,这个消息的参数包含了鼠标位置的信息)

    1,回调函数工作机制 回调函数由操作系统自动调用,回调函数的返回值当然也是返回给操作系统了. 2,截获操作系统发出的消息,截获到后,将另外一个消息返回给操作系统,已达到欺骗操作系统的目的. 下面还是以 ...

  8. Kakfa消息投递语义

    Message Delivery Semantics At most once -- Messages may be lost but are never redelivered(消息可能丢失但不会重 ...

  9. Windows消息【一】 消息队列

    看了MSDN后,以下是我个人的理解! 消息能够被分为「队列化消息」和「非队列化消息」. 队列化消息是指当程序发生某事件时,由Windows主动捕获并把消息放入系统消息队列中,而程序在运行时会初始化一个 ...

随机推荐

  1. ubuntu16.04安装tensorflow官方教程与机器学习资料【学习笔记】

    tensorflow官网有官方的安装教程:https://www.tensorflow.org/install/install_linux google的机器学习官方快速入门教程:https://de ...

  2. spark-sql执行流程分析

    spark-sql 架构 图1 图1是sparksql的执行架构,主要包括逻辑计划和物理计划几个阶段,下面对流程详细分析. sql执行流程 总体流程 parser:基于antlr框架对 sql解析,生 ...

  3. CGI, FCGI, SCGI, WSGI 释异

    IKI Links: CGI - http://en.wikipedia.org/wiki/Common_Gateway_Interface FCGI - http://en.wikipedia.or ...

  4. p4c-bm安装

    Generates the JSON configuration for the behavioral-model (bmv2).它是用来形成 行为模型BMV2 的 JSON配置 的. Importa ...

  5. POJ 2407 Relatives(欧拉函数)

    http://poj.org/problem?id=2407 题意: 给出一个n,求小于等于的n的数中与n互质的数有几个. 思路: 欧拉函数的作用就是用来求这个的. #include<iostr ...

  6. Spring IOC和IOC容器

    IOC的核心理念即是控制反转.将对依赖的控制从具体业务对象手中转交到平台或框架中,需要的时候再由平台或框架注入到具体业务对象中.可以说依赖注入是控制反转的实现方式. IOC的优点: 降低代码耦合度 减 ...

  7. mong大牛的blog

    MongoDB权威指南(3)-查询1.find方法介绍在不传入参数的情况下,find方法缺省使用  http://www.educity.cn/wenda/389594.html 这个归纳的比较好:可 ...

  8. OC 数据持久化(数据本地化)- 本地存储

    // // ViewController.m // IOS_0113_本地存储 // // Created by ma c on 16/1/13. // Copyright (c) 2016年 博文科 ...

  9. Reverse engineer powerdesigner link odbc

    Reverse engineer powerdesigner link odbc           Option Explicit ValidationMode = True Interactive ...

  10. [转载]c语言指针segmentation fault 指针常常错误的小地方

    http://www.cnblogs.com/qingjoin/archive/2012/03/20/2408944.html #include <stdio.h> ] = ] = ] = ...