【转】Windows消息投递流程:WM_COMMAND消息流程

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

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

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

[cpp] view plaincopy

  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()处理。

[cpp] view plaincopy

  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函数

[cpp] view plaincopy

  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,因为处理命令不需要这一信息。取出的消息按照以下顺序经过应用程序的各个部分:活动视图、活动视图的文档、文档模板、主窗口、应用程序。

[cpp] view plaincopy

  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,该函数通过查找消息映射表,进而调用到消息处理函数。

[cpp] view plaincopy

  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. }
时间: 2024-08-07 13:21:12

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

MFC窗口WM_COMMAND消息

通过分析MFC的源代码,我们可以得到WM_COMMAND的消息响应顺序如下: 多文档框架中,有打开的文档时:视图 > 文档 > 子框架窗口 > 应用程序 >主框架窗口 多文档框架在没有打开文档时,应用程序和主框架窗口的顺序相反:主框架窗口 > 应用程序 在单文档框架应用程序中,因为没有子框架窗口,所以顺序应该是:视图 > 文档 >主框架窗口> 应用程序.无论有没有打开文档,主框架窗口都比应用程序类更优先. 事实上在多文档框架中,系统自动生成的代码使用的是CD

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

前言 前前一篇留了个小问题,在上一篇中忘了写了,就是关于LayIM已经封装好的上传文件或者图片的问题.对接好接口之后,如果上传速度慢,界面就会出现假死情况,虽然文件正在上传.于是我就简单做了个图标替代来增强用户体验. 上传中... 上传完成后 是不是很简单啊,接下来进入正题. 业务介绍 LayIM中的加好友可以说是不太必要的逻辑,而且其实大部分在模仿QQ,当然业务复杂度肯定没法和QQ比.主线,就是用户A请求添加用户B为好友,用户B收到消息提示之后点击同意或者拒绝或者直接忽略,然后在将消息反馈到用

如何保障消息中间件 100% 消息投递成功?如何保证消息幂等性?

一.前言 二.分析问题 三.持久化 四.confirm机制 五.消息提前持久化 + 定时任务 六.幂等含义 6.1.为什么要有幂等这种场景? 6.2.乐观锁方案 6.3.唯一ID + 指纹码 6.4.Redis原子操作 <Java 2019 超神之路> <Dubbo 实现原理与源码解析 —— 精品合集> <Spring 实现原理与源码解析 —— 精品合集> <MyBatis 实现原理与源码解析 —— 精品合集> <Spring MVC 实现原理与源码解

深入Windows内核——C++中的消息机制

<编程思想之消息机制>一文中我们讲了消息的相关概念和消息机制的模拟,本文将进一步聊聊C++中的消息机制. 从简单例子探析核心原理 在讲之前,我们先看一个简单例子:创建一个窗口和两个按钮,用来控制窗口的背景颜色.其效果如下: 图 2 :效果图 Win32Test.h #pragma once #include <windows.h> #include <atltypes.h> #include <tchar.h> //资源ID #define ID_BUTTO

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

上节讲了消息的相关概念,本文将进一步聊聊C++中的消息机制. 从简单例子探析核心原理 在讲之前,我们先看一个简单例子:创建一个窗口和两个按钮,用来控制窗口的背景颜色.其效果 图1.效果图  Win32Test.h 1 #pragma once 2 3 #include <windows.h> 4 #include <atltypes.h> 5 #include <tchar.h> 6 7 //资源ID 8 #define ID_BUTTON_DRAW 1000 9 #d

Windows Server 2012 R2 WSUS-10:流程概述

本篇文章来大概说一说打补丁的流程,一般来说打补丁的流程分为测试环境测试和生产环境安装两个部分.如果企业规模比较小,没有完善的流程制度,也是有一些打补丁的原则可以遵循的,比如: 对于安全级别为Low以上的各种安全补丁应该分发: 对于操作系统的安全补丁应该分发: 对于各种IE版本安全补丁应该分发: 对于其他各种安全补丁(如Media Player.OutLook Express等)应该分发: 对于状态为Updates修订版本的安全补丁,无需手工批准,系统会自动发布: 那么对于规模稍微复杂的企业来说,

Windows核心编程06-Windows的消息循环

Windows的消息机制 程序的执行机制 过程驱动-程序的执行过程是按照预定好的顺序执行 事件驱动-程序的执行是无序,用户根据需要随机触发相应的事件 Win32窗口程序就是采用时间驱动方式执行,也就是消息机制 什么是消息 当系统通知窗口工作时,就采用消息的方式派发(DispatchMessage来派发)给窗口的自定义的处理函数函数WNDCLASSEX.lpfnWndProc 消息组成: 窗口句柄 消息ID 消息的两个参数 如鼠标单击,两个参数如下: 消息产生的时间 消息产生的鼠标位置 窗口处理函

Gitlab 与 Git Windows 客户端一起使用的入门流程

我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3824934.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验. 准确来说,大部分的功能都还没有探索过,这只是暂时摸索出来的一个简单流程~ 先建个branch吧 用命令行建肯定是可以的.我采用的方法是在gitlab的web端上直接建.方法如下: 来到工程的branch列表页面 (链接是现在培训的地址,作为例子). 点击左侧的New Branch. 在Name

ActiveMQ(18):Message之延迟和定时消息投递

一.简介 延迟和定时消息投递(Delay and Schedule Message Delivery) 有时候我们不希望消息马上被broker投递出去,而是想要消息60秒以后发给消费者,或者我们想让消息没隔 一定时间投递一次,一共投递指定的次数...类似这种需求,ActiveMQ提供了一种broker端消息定时调度 机制. 注意: 我们只需要把几个描述消息定时调度方式的参数作为属性添加到消息,broker端的调度器就会按照我们想要的行为去处理消息. 当然需要在xml中配置schedulerSup