VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

搜索
查看: 310|回复: 3

[分享] 浅谈MFC框架内部结构 三

[复制链接]
08_avatar_middle
最佳答案
10 
在线会员 发表于 2022-4-3 14:32:10 | 显示全部楼层 |阅读模式
继续分析下MFC的消息映射机制
CWinApp::Run()
  1. int CWinApp::Run()
  2. {
  3.         if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
  4.         {
  5.                 // Not launched /Embedding or /Automation, but has no main window!
  6.                 TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");
  7.                 AfxPostQuitMessage(0);
  8.         }
  9.         return CWinThread::Run();
  10. }
复制代码
CWinThread::Run()
  1. int CWinThread::Run()
  2. {
  3.         ASSERT_VALID(this);
  4.         _AFX_THREAD_STATE* pState = AfxGetThreadState();

  5.         // for tracking the idle time state
  6.         BOOL bIdle = TRUE;
  7.         LONG lIdleCount = 0;

  8.         // acquire and dispatch messages until a WM_QUIT message is received.
  9.         for (;;)
  10.         {
  11.                 // phase1: check to see if we can do idle work
  12.                 while (bIdle &&
  13.                         !::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
  14.                 {
  15.                         // call OnIdle while in bIdle state
  16.                         if (!OnIdle(lIdleCount++))
  17.                                 bIdle = FALSE; // assume "no idle" state
  18.                 }

  19.                 // phase2: pump messages while available
  20.                 do
  21.                 {
  22.                         // pump message, but quit on WM_QUIT
  23.                         if (!PumpMessage())
  24.                                 return ExitInstance();

  25.                         // reset "no idle" state after pumping "normal" message
  26.                         //if (IsIdleMessage(&m_msgCur))
  27.                         if (IsIdleMessage(&(pState->m_msgCur)))
  28.                         {
  29.                                 bIdle = TRUE;
  30.                                 lIdleCount = 0;
  31.                         }

  32.                 } while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
  33.         }
  34. }
复制代码
CWinThread::PumpMessage()
BOOL CWinThread::PumpMessage()
{
  return AfxInternalPumpMessage();
}

AfxInternalPumpMessage()
  1. BOOL AFXAPI AfxInternalPumpMessage()
  2. {
  3.         _AFX_THREAD_STATE *pState = AfxGetThreadState();

  4.         if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
  5.         {
  6. #ifdef _DEBUG
  7.                 TRACE(traceAppMsg, 1, "CWinThread::PumpMessage - Received WM_QUIT.\n");
  8.                         pState->m_nDisablePumpCount++; // application must die
  9. #endif
  10.                 // Note: prevents calling message loop things in 'ExitInstance'
  11.                 // will never be decremented
  12.                 return FALSE;
  13.         }

  14. #ifdef _DEBUG
  15.   if (pState->m_nDisablePumpCount != 0)
  16.         {
  17.           TRACE(traceAppMsg, 0, "Error: CWinThread::PumpMessage called when not permitted.\n");
  18.           ASSERT(FALSE);
  19.         }
  20. #endif

  21. #ifdef _DEBUG
  22.         _AfxTraceMsg(_T("PumpMessage"), &(pState->m_msgCur));
  23. #endif

  24.   // process this message

  25.         if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
  26.         {
  27.                 ::TranslateMessage(&(pState->m_msgCur));
  28.                 ::DispatchMessage(&(pState->m_msgCur));
  29.         }
  30.   return TRUE;
  31. }
复制代码
以上是MFC的消息循环,接着重写CFrame的WindowProc虚函数,然后下断点调试跟入:
浅谈MFC框架内部结构 三
堆栈窗口显示 AfxWndProc调用的 WindowProc
  1. BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  2. {
  3. if (message == WM_COMMAND)
  4.                 {
  5.                         if (OnCommand(wParam, lParam))
  6.                         {
  7.                                 lResult = 1;
  8.                                 goto LReturnTrue;
  9.                         }
  10.                 return FALSE;
  11. }


  12. const AFX_MSGMAP* pMessageMap = GetMessageMap();
  13. //这个赋值很有用 消息映射的关键
  14. const AFX_MSGMAP_ENTRY* lpEntry;
  15. //这个结构也是很重要的
  16. AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
  17. //建立一条512大小的 消息缓存,本地建立的。或者说本类建立的,
  18. //下次捕捉到就立马跳转执行
  19. if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
  20.                 {
  21.                 // cache hit
  22.                         lpEntry = pMsgCache->lpEntry;
  23.                         winMsgLock.Unlock();
  24.                         if (lpEntry == NULL)
  25.                                 return FALSE;

  26.                         // cache hit, and it needs to be handled
  27.                         if (message < 0xC000)
  28.                                 goto LDispatch;
  29.                         else
  30.                                 goto LDispatchRegistered;
  31.         }
  32. else
  33. {
  34. //保存消息缓存中,下次遇到不就不再去搜索消息了直接hit击中。
  35. pMsgCache->nMsg = message;
  36. pMsgCache->pMessageMap = pMessageMap;

  37. //一个死循环 具体在做什么下面进行分析
  38. for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;
  39.                         pMessageMap = (*pMessageMap->pfnGetBaseMap)())
  40. {
  41. ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
  42.                         if (message < 0xC000)
  43.                         {
  44.                                 // constant window message
  45.                                 if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
  46.                                         message, 0, 0)) != NULL)
  47.                                 {
  48.                                         pMsgCache->lpEntry = lpEntry;
  49.                                         winMsgLock.Unlock();
  50.                                         goto LDispatch;
  51.                                 }
  52.         }
  53. }
  54. }

  55. }
复制代码
struct AFX_MSGMAP
{
        const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
        const AFX_MSGMAP_ENTRY* lpEntries;
};

struct AFX_MSGMAP_ENTRY
{
        UINT nMessage;   // windows message
        UINT nCode;      // control code or WM_NOTIFY code
        UINT nID;        // control ID (or 0 for windows messages)
        UINT nLastID;    // used for entries specifying a range of control id's
        UINT_PTR nSig;       // signature type (action) or pointer to message #
        AFX_PMSG pfn;    // routine to call (or special value)
};

接下来手动实现消息映射代码:
浅谈MFC框架内部结构 三
了解消息映射机制的关键在于读取 三条宏里面的代码:
DECLARE_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(MyWnd, CFrameWnd)
        ON_WM_CREATE()
END_MESSAGE_MAP()
宏解开后 还是可以正常运行程序的:
浅谈MFC框架内部结构 三
消息映射机制的原理就在这两个函数 和静态变量中:
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
static const AFX_MSGMAP_ENTRY _messageEntries[]
static const AFX_MSGMAP messageMap
浅谈MFC框架内部结构 三
响应消息这里下断点然后看堆栈窗口:
浅谈MFC框架内部结构 三
来自OnWndMsg函数调用用进入后发现:
浅谈MFC框架内部结构 三
这行代码调用 往上走:
浅谈MFC框架内部结构 三
发现是从上面的OnWndMsg()函数跳转而来的。停止调试分析 这个函数是怎么执行的。
const AFX_MSGMAP* pMessageMap = GetMessageMap();
这是一个虚函数 所以会调用我自己的函数 进入执行代码:
return GetThisMessageMap();执行这个静态函数。定义了一个静态数组 存放两个内容:
static const AFX_MSGMAP_ENTRY _messageEntries[]
ON_WM_CREATE() 宏里面的和{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
ON_WM_CREATE() == {WM_CREATE,....OnCreate)}通过观察可以发现 这个其实就是消息对应响应函数的数组集合。
定义了一个静态结构体变量        static const AFX_MSGMAP messageMap
存放了 &TheBaseClass::GetThisMessageMap   & _messageEntries[0]
GetThisMessageMap 返回的就是 messageMap的地址
也就是 基类的 一个函数指针 和 当前类 静态数组的首地址。
那么大胆推测下 如果基类也有这样的宏(不会变的内容) 那么 这样就会形成一个链表了。
浅谈MFC框架内部结构 三
基类确实存在消息映射宏。那么可以画一张图来演示了。
首先一路追,追到CCmdTarget 类的时候 显示:
DECLARE_MESSAGE_MAP()       // base class - no {{ }} macros
那么也就是说这个类是终点,里面没有东西了。
浅谈MFC框架内部结构 三
链表的头节点就是 CMyWnd了。因为virtual const AFX_MSGMAP* GetMessageMap() const;
OnWndMsg()函数的代码执行下面的步骤来找消息。
1、const AFX_MSGMAP* pMessageMap = GetMessageMap();
//获取子类CMyWnd类的AFX_MSGMAP 结构体指针
//里面存放的 1、父类GetThisMessageMap        2、当前类的_messageEntries[0]首地址
2、const AFX_MSGMAP_ENTRY* lpEntry;
//定义一个消息与函数对应的指针
3、for循环遍历链表:
(1) 判断下 pMessageMap->pfnGetBaseMap != NULL;//第一个成员 父类的GetThisMessageMap函数 是否为空。是否链表遍历结束了。
(2) lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,message, 0, 0)
//寻找消息 从当前类的_messageEntries中寻找。MFC用汇编代码实现
(3) pMsgCache->lpEntry = lpEntry;//找到了 缓存中的内容补齐
(4) goto LDispatch;//跳转函数实现代码
(5) mmf.pfn = lpEntry->pfn;//函数保存
(6) switch (lpEntry->nSig)//检索函数类型
(7) lResult = (this->*mmf.pfn_l_w_l)(wParam, lParam);//执行函数 这里的this是当前类。

下面来分析下 MFC的 动态运行和动态创建,以及序列化,然后分析 MFC对话框的执行流程,SDI流程和MDI流程。

评分

参与人数 2威望 +15 驿站币 +17 热心值 +5 收起 理由
58_avatar_small thzzl + 2 + 2 很给力!
75_avatar_small wl1383838438 + 15 + 15 + 3 赞一个!

查看全部评分





上一篇:浅谈MFC框架内部结构 二
下一篇:浅谈MFC框架内部结构 四

本帖被以下淘专辑推荐:

08_avatar_middle
最佳答案
10 
ico_lz  楼主| 发表于 2022-4-3 14:36:58 | 显示全部楼层
浅谈MFC框架内部结构 三 MFC的原理及框架剖析.zip (534.97 KB, 下载次数: 11)
75_avatar_middle
最佳答案
23 
online_supermod 发表于 2022-4-4 09:32:01 | 显示全部楼层
编程就应该有这研究得心
98_avatar_middle
最佳答案
0 
在线会员 发表于 2022-4-4 15:52:27 | 显示全部楼层
谢谢分享

您需要登录后才可以回帖 登录 | 加入驿站 qq_login

本版积分规则

×【发帖 友情提示】
1、请回复有意义的内容,请勿恶意灌水;
2、纯数字、字母、表情等无意义的内容系统将自动删除;
3、若正常回复后帖子被自动删除,为系统误删的情况,请重新回复其他正常内容或等待管理员审核通过后会自动发布;
4、感谢您对VC驿站一如既往的支持,谢谢合作!

关闭

站长提醒上一条 /2 下一条

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号-4 )|网站地图wx_jqr

GMT+8, 2022-10-5 04:13

Powered by CcTry.CoM

© 2009-2021 cctry.com

快速回复 返回顶部 返回列表