---恢复内容开始---

                  MFC原理第六讲.消息传递

一丶简介  

    通过上一讲我们的消息映射表.我们得知. 消息映射表 会保存父类的MessageMap 以及自己当前的消息结构体数组.

消息传递是一层一层的递进的.那么我们现在要看一下怎么递进的.

要学习的知识

    1.窗口创建的流程.以及默认的回调函数

    2.消息处理流程

二丶窗口创建的流程.以及默认的回调函数

  我们要看窗口创建.那么就需要跟进 MFC源码去看. 首先就是对我们的Create函数下断点.看一下做了什么事情.

进入Create函数内部.

  1. BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
  2. LPCTSTR lpszWindowName,
  3. DWORD dwStyle,
  4. const RECT& rect,
  5. CWnd* pParentWnd,
  6. LPCTSTR lpszMenuName,
  7. DWORD dwExStyle,
  8. CCreateContext* pContext)
  9. {
  10. HMENU hMenu = NULL;
  11. if (lpszMenuName != NULL) //首先判断我们有彩蛋吗.如果有加载我们的菜单.
  12. {
  13. // load in a menu that will get destroyed when window gets destroyed
  14. HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);
  15. if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
  16. {
  17. TRACE(traceAppMsg, , "Warning: failed to load menu for CFrameWnd.\n");
  18. PostNcDestroy(); // perhaps delete the C++ object
  19. return FALSE;
  20. }
  21. }
  22.  
  23. m_strTitle = lpszWindowName; // save title for later
  24.  
  25. if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
  26. rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, //内部还是调用的CreateEx函数.所以我们继续跟进去查看.
  27. pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
  28. {
  29. TRACE(traceAppMsg, , "Warning: failed to create CFrameWnd.\n");
  30. if (hMenu != NULL)
  31. DestroyMenu(hMenu);
  32. return FALSE;
  33. }
  34.  
  35. return TRUE;
  36. }

CreateEx查看. 窗口过程处理函数.

  1. BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
  2. LPCTSTR lpszWindowName, DWORD dwStyle,
  3. int x, int y, int nWidth, int nHeight,
  4. HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
  5. {
  6. ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) ||
  7. AfxIsValidAtom(lpszClassName));
  8. ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));
  9.  
  10. // allow modification of several common create parameters
  11. CREATESTRUCT cs; //熟悉的窗口类创建但是是一个新的结构.
  12. cs.dwExStyle = dwExStyle;
  13. cs.lpszClass = lpszClassName;
  14. cs.lpszName = lpszWindowName;
  15. cs.style = dwStyle;
  16. cs.x = x;
  17. cs.y = y;
  18. cs.cx = nWidth; //其中高版本在这里就会设置默认的窗口回调.
  19. cs.cy = nHeight;
  20. cs.hwndParent = hWndParent;
  21. cs.hMenu = nIDorHMenu;
  22. cs.hInstance = AfxGetInstanceHandle();
  23. cs.lpCreateParams = lpParam;
  24.  
  25. if (!PreCreateWindow(cs)) //内部进行风格设置以及注册窗口类.
  26. {
  27. PostNcDestroy();
  28. return FALSE;
  29. }
  30.  
  31. AfxHookWindowCreate(this); //Hook 窗口回调函数.设置窗口回调函数. Create消息来到的时候
  32. HWND hWnd = CreateWindowEx(cs.dwExStyle, cs.lpszClass,
  33. cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
  34. cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
  35.  
  36. #ifdef _DEBUG
  37. if (hWnd == NULL)
  38. {
  39. TRACE(traceAppMsg, , "Warning: Window creation failed: GetLastError returns 0x%8.8X\n",
  40. GetLastError());
  41. }
  42. #endif
  43.  
  44. if (!AfxUnhookWindowCreate())
  45. PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
  46.  
  47. if (hWnd == NULL)
  48. return FALSE;
  49. ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
  50. return TRUE;
  51. }

新的结构

  1. typedef struct tagCREATESTRUCTA {
  2. LPVOID lpCreateParams;
  3. HINSTANCE hInstance;
  4. HMENU hMenu;
  5. HWND hwndParent;
  6. int cy;
  7. int cx;
  8. int y;
  9. int x;
  10. LONG style;
  11. LPCSTR lpszName;
  12. LPCSTR lpszClass;
  13. DWORD dwExStyle;
  14. } CREATESTRUCTA, *LPCREATESTRUCTA;

新的类跟注册窗口的时候很相似. 我们看一下窗口回调在哪里设置的吧.

窗口回调函数 是通过

  1. AfxHookWindowCreate 函数来进行设置.而这个函数本身就是一个Windows自带的HOOK. 其真正的窗口回调函数.是在内部中.设置回调的时候 新的回调函数进行设置的.
  1. void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
  2. {
  3. _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
  4. if (pThreadState->m_pWndInit == pWnd)
  5. return;
  6.  
  7. if (pThreadState->m_hHookOldCbtFilter == NULL)
  8. {
  9. pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT, //设置CBT HOOK . _AfxCbtFilterHook里面才是真正的替换窗口过程处理函数.
  10. _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
  11. if (pThreadState->m_hHookOldCbtFilter == NULL)
  12. AfxThrowMemoryException();
  13. }
  14. ASSERT(pThreadState->m_hHookOldCbtFilter != NULL);
  15. ASSERT(pWnd != NULL);
  16. ASSERT(pWnd->m_hWnd == NULL); // only do once
  17.  
  18. ASSERT(pThreadState->m_pWndInit == NULL); // hook not already in progress
  19. pThreadState->m_pWndInit = pWnd;
  20. }

看一下函数内部

  1. LRESULT CALLBACK
  2. _AfxCbtFilterHook( int code, WPARAM wParam, LPARAM lParam) {
  3. // …
  4. WNDPROC afxWndProc = AfxGetAfxWndProc();
  5. oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc); //重要的位置就是这里.使用的SetWindowLong这个函数.将窗口过程函数替换为了 afxWndProc
  6. // …
  7. }
  8. WNDPROC AFXAPI AfxGetAfxWndProc() {
  9. // …
  10. return & AfxWndProc;
  11. }

总结: 通过上面代码我们得知了.窗口在创建的时候以及窗口回调进行的一些列设置

  1.调用Create创建窗口

  2.设置窗口类.

  3.注册窗口类.

  4.通过AfxHookWindowsCreate 将我们的默认窗口回调改成了 afxWndProc

  5.窗口创建完毕.

上面五条则是我们创建窗口的时候进行的一系列操作. 所以我们的消息处理函数变成了 afxWndProc了这个消息处理函数就会在发生消息的时候第一个来到.

三丶消息处理流程

  通过上面我们得知了窗口处理回调已经更改了. 现在我们直接对我们的消息下段点.就可以验证一下.是否是我们的函数首次来到.

对我们的按钮点击下段点. 通过栈回朔一层一层往上看.

第一层

第一层级就是判断我们的消息.进行不同的处理. 所以不重要.跳过.

  1. 第二层消息处理层
    这一层就是我们要进行的消息处理的一层.如果消息不处理则默认交给默认的处理函数进行处理
  1. LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3. // OnWndMsg does most of the work, except for DefWindowProc call
  4. LRESULT lResult = ;
  5. if (!OnWndMsg(message, wParam, lParam, &lResult))
  6. lResult = DefWindowProc(message, wParam, lParam);
  7. return lResult;
  8. }

第n层.因为不重要了.所以我们栈回朔到最顶层即可.

  1. AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
  2. {
  3. // special message which identifies the window as using AfxWndProc
  4. if (nMsg == WM_QUERYAFXWNDPROC)
  5. return ;
  6.  
  7. // all other messages route through message map
  8. CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
  9. ASSERT(pWnd != NULL);
  10. ASSERT(pWnd==NULL || pWnd->m_hWnd == hWnd);
  11. if (pWnd == NULL || pWnd->m_hWnd != hWnd)
  12. return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
  13. return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
  14. }

我们如果自己去看.可以看到.WindProc函数是被外部调用的. 而且这个函数是一个虚函数.也就是说如果我们重写了消息处理函数.那么我们自己就可以处理消息了.

如果自己不处理.那么默认就调用 CWnd里面的消息处理函数了

而里面的 OnMsg函数同样也是一个虚函数. 如果不该写一样调用父类的

调试可以看一下.

只是部分代码截图.如果有兴趣可以深究. 我们知道. Windows 消息分为三大类.

1.普通消息.

2.菜单消息. WM_COMMAND

3.WM_NOTIFY

而我们的鼠标点击消息就是普通消息.  如果来菜单消息了就统一为WM_COMMAND消息. 代表的是通知类消息.

而我们的这个方法就是判断消息是什么类型的. 进行不同消息的处理.

如果说来的消息都不包括的话.那么下面就开始遍历消息映射表.然后进行消息查找.

完整代码

  1. 代码太多删减一下.
  2. BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  3. {
  4. LRESULT lResult = ;
  5. union MessageMapFunctions mmf;
  6. mmf.pfn = ;
  7. CInternalGlobalLock winMsgLock;
  8. // special case for commands
  9. if (message == WM_COMMAND)
  10. {
  11. if (OnCommand(wParam, lParam))
  12. {
  13. lResult = ;
  14. goto LReturnTrue;
  15. }
  16. return FALSE;
  17. }
  18.  
  19. if (message == WM_CREATE && m_pDynamicLayout != NULL)
  20. {
  21. ASSERT_VALID(m_pDynamicLayout);
  22.  
  23. if (!m_pDynamicLayout->Create(this))
  24. {
  25. delete m_pDynamicLayout;
  26. m_pDynamicLayout = NULL;
  27. }
  28. else
  29. {
  30. InitDynamicLayout();
  31. }
  32. }
  33.  
  34. // special case for notifies
  35. if (message == WM_NOTIFY)
  36. {
  37. NMHDR* pNMHDR = (NMHDR*)lParam;
  38. if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
  39. goto LReturnTrue;
  40. return FALSE;
  41. }
  42.  
  43. // special case for activation
  44. if (message == WM_ACTIVATE)
  45. _AfxHandleActivate(this, wParam, CWnd::FromHandle((HWND)lParam));
  46.  
  47. // special case for set cursor HTERROR
  48. if (message == WM_SETCURSOR &&
  49. _AfxHandleSetCursor(this, (short)LOWORD(lParam), HIWORD(lParam)))
  50. {
  51. lResult = ;
  52. goto LReturnTrue;
  53. }
  54.  
  55. // special case for windows that contain windowless ActiveX controls
  56. .......const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap(); //获得自己当前的消息映射表. 下面就开始遍历消息判断消息了
  57. UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-);
  58. winMsgLock.Lock(CRIT_WINMSGCACHE);
  59. AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
  60. const AFX_MSGMAP_ENTRY* lpEntry;
  61. if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
  62. {
  63. // cache hit
  64. lpEntry = pMsgCache->lpEntry;
  65. winMsgLock.Unlock();
  66. if (lpEntry == NULL)
  67. return FALSE;
  68.  
  69. // cache hit, and it needs to be handled
  70. if (message < 0xC000)
  71. goto LDispatch;
  72. else
  73. goto LDispatchRegistered;
  74. }
  75. else
  76. {
  77. // not in cache, look for it
  78. pMsgCache->nMsg = message;
  79. pMsgCache->pMessageMap = pMessageMap;
  80.  
  81. for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;
  82. pMessageMap = (*pMessageMap->pfnGetBaseMap)())
  83. {
  84. // Note: catch not so common but fatal mistake!!
  85. // BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)
  86. ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
  87. if (message < 0xC000)
  88. {
  89. // constant window message
  90. if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
  91. message, , )) != NULL)
  92. {
  93. pMsgCache->lpEntry = lpEntry;
  94. winMsgLock.Unlock();
  95. goto LDispatch;
  96. }
  97. }
  98. else
  99. {
  100. // registered windows message
  101. lpEntry = pMessageMap->lpEntries;
  102. while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, , )) != NULL)
  103. {
  104. UINT* pnID = (UINT*)(lpEntry->nSig);
  105. ASSERT(*pnID >= 0xC000 || *pnID == );
  106. // must be successfully registered
  107. if (*pnID == message)
  108. {
  109. pMsgCache->lpEntry = lpEntry;
  110. winMsgLock.Unlock();
  111. goto LDispatchRegistered;
  112. }
  113. lpEntry++; // keep looking past this one
  114. }
  115. }
  116. }
  117.   pMsgCache->lpEntry = NULL;
  118.   winMsgLock.Unlock();
  119.   return FALSE;
  120. }
  121.  
  122. LDispatch:                              因为自己的当前MessageMapi中保存着消息结构体数组. 所以遍历可以得出 消息.以及对应的函数指针
  123. ASSERT(message < 0xC000);
  124.  
  125. mmf.pfn = lpEntry->pfn; 然后其结果保存在 mmf.pfn中. mmf是一个结构.联合体结构. 具体下方可以看一下这个结构.其实结构其实就是保存了函数返回值以及类型信息
  126.  
  127. switch (lpEntry->nSig) 我们消息结构体中前边也讲过.有一个sig标识.代表了函数的返回值以及参数类型. 进而通过不同的函数.调用不同的消息处理函数
  128. {
  129. default:
  130. ASSERT(FALSE);
  131. break;
  132. case AfxSig_l_p:
  133.  
  134. 结构.只显示部分
  135.  
  136. union MessageMapFunctions
  137. {
  138. AFX_PMSG pfn; // generic member function pointer
  139.  
  140. BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_D)(CDC*);
  141. BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_b)(BOOL);
  142. BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_u)(UINT);
  143. BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_h)(HANDLE);
  144. BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_u_u)(CWnd*, UINT, UINT);
  145. BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_COPYDATASTRUCT)(CWnd*, COPYDATASTRUCT*);
  146. BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_HELPINFO)(LPHELPINFO);
  147. HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_W_u)(CDC*, CWnd*, UINT);
  148. HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_u)(CDC*, UINT);
  149. int (AFX_MSG_CALL CCmdTarget::*pfn_i_u_W_u)(UINT, CWnd*, UINT);
  150.  
  151. }

如果是 WM_COMMAND 或者 WM_NOTIFY 消息.则取对应的 OnCommand中. 这个函数跟上面类似.也是遍历消息映射表去寻找.有兴趣的可以自己看下源码.

MFC原理第六讲.消息传递的更多相关文章

  1. MFC原理第四讲.动态创建机制

    MFC原理第四讲.动态创建机制 一丶要学习的知识点以及简介 动态创建是什么意思? 动态创建其实就是跟C++的new一样.都是创建对象.但是规避了C++语法的缺陷. 例如: char * ClassNa ...

  2. MFC原理第二讲.MFC的初始化过程

    MFC原理第二讲MFC的初始化过程 一丶简介 通过上一讲.我们手工模拟了一个MFC程序. 但是上一讲留下了疑问. 就是WinMain在哪.以及消息处理在哪里. 这一节主要就是讲解Winmain在哪里. ...

  3. MFC原理第一讲.MFC的本质.以及手工编写MFC的程序

    MFC原理第一讲.MFC的本质.以及手工编写MFC的程序 PS: 这个博客属于复习知识.从头开始讲解. 在写这篇博客之前.已经写了3篇MFC的本质了.不过掌握知识点太多.所以从简重新开始. 一丶MFC ...

  4. MFC原理第五讲.消息映射.以及如何添加消息

    MFC原理第五讲.消息映射.以及如何添加消息 一丶消息映射是什么 我们知道.Win32程序.都是通过消息去驱动的. 不断的在处理消息. 只要我们使用固定的宏.就可以让我们的框架知道一旦消息发生.该往哪 ...

  5. MFC原理第三讲.RTTI运行时类型识别

    MFC原理第三讲.RTTI运行时类型识别 一丶什么是RTTI RTTI. 运行时的时候类型的识别. 运行时类型信息程序.能够使用基类(父类)指针 或者引用 来检查这些指针或者引用所指的对象. 实际派生 ...

  6. C++反汇编第六讲,认识C++中的Try catch语法,以及在反汇编中还原

    C++反汇编第六讲,认识C++中的Try catch语法,以及在反汇编中还原 我们以前讲SEH异常处理的时候已经说过了,C++中的Try catch语法只不过是对SEH做了一个封装. 如果不懂SEH异 ...

  7. Stanford机器学习---第六讲. 怎样选择机器学习方法、系统

    原文:http://blog.csdn.net/abcjennifer/article/details/7797502 本栏目(Machine learning)包括单参数的线性回归.多参数的线性回归 ...

  8. 机器学习基石的泛化理论及VC维部分整理(第六讲)

    第六讲 第五讲主要讲了机器学习可能性,两个问题,(1)\(E_{in} 要和 E_{out}\) 有很接近,(2)\(E_{in}\)要足够小. 对于第一个假设,根据Hoefding's Inequa ...

  9. 《ArcGIS Engine+C#实例开发教程》第六讲 右键菜单添加与实现

    原文:<ArcGIS Engine+C#实例开发教程>第六讲 右键菜单添加与实现 摘要:在这一讲中,大家将实现TOCControl控件和主地图控件的右键菜单.在AE开发中,右键菜单有两种实 ...

随机推荐

  1. 小白的CTF学习之路4——内存

    明天要进行二模考试了,沉住气,加油,能过 内存是学C路上必不可少的一环,一定要非常认真的去学 内存的物理结构: ROM:只读内存——早期的手机 RAM:读写(数据断点既消) DRAM:经常更新 SRA ...

  2. linux 安装mysql5.7.25

    这两天一直在弄mysql.一直安装.终于可以安装一个成一个了.哈哈哈 自己又写了个脚本希望对大家有所帮助 脚本非常简单 不错操作起来也很容易 重要提示 我的linux 是centos7.不是6. 7和 ...

  3. MAC帧和PPP帧区别

  4. 哈夫曼(Huffman)树和哈夫曼编码

    一.哈夫曼(Huffman)树和哈夫曼编码 1.哈夫曼树(Huffman)又称最优二叉树,是一类带权路径长度最短的树, 常用于信息检测. 定义: 结点间的路径长度:树中一个结点到另一个结点之间分支数目 ...

  5. linux pxe 安装Centos7

    服务端 需要3种服务 dhcp + tftp + vsftp tftp 提供引导 为什么不使用其他协议来进行pxe引导 是因为网卡只会集成tftp这种服务     写明到镜像的方式 dhcp 下发tf ...

  6. Sql- Group By ,Where,having用法

    Group by,where,having 是数据库查询中最常用的几个关键字.在工作中,时常用到,前面遇到一个问题,一个查询中使用了where ,group by ,having及聚集函数时 ,执行顺 ...

  7. WebPackBrows

    一个http工具,通过java编写 调用方法 s.y.webpackbrows.fac.WebPackFactor.getConnection 还会继续完善 下载位置 https://pan.baid ...

  8. “Xavier”安卓木马分析:可静默收集数据并远程代码执行

    趋势科技研究发现了一款Android恶意木马——Xavier.在谷歌Play应用市场中,超过800款Android应用感染了该恶意木马,影响数百万Android用户.感染的应用范围覆盖图片编辑器,墙纸 ...

  9. 删除坏掉的 Active Directory Domain

    最近公司的某个 Domain Controller 报告可能由于长时间没在线,同步失败,然后用 Repldiag 工具清理 lingering objects 的过程中,该工具报告存在 serious ...

  10. Vue实现懒加载的基本思路

    懒加载是前端开发者的基本功之一.实现懒加载肯定是要直接操作DOM的,这个没得跑,但我们可以想办法让流程尽可能优雅些. 基本结构 父组件是列表容器,子组件是列表中的项,如卡片.帖子等,承载图片的DOM对 ...