继续分析下MFC的消息映射机制
CWinApp::Run() - int CWinApp::Run()
- {
- if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
- {
- // Not launched /Embedding or /Automation, but has no main window!
- TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");
- AfxPostQuitMessage(0);
- }
- return CWinThread::Run();
- }
复制代码CWinThread::Run() - int CWinThread::Run()
- {
- ASSERT_VALID(this);
- _AFX_THREAD_STATE* pState = AfxGetThreadState();
- // for tracking the idle time state
- BOOL bIdle = TRUE;
- LONG lIdleCount = 0;
- // acquire and dispatch messages until a WM_QUIT message is received.
- for (;;)
- {
- // phase1: check to see if we can do idle work
- while (bIdle &&
- !::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
- {
- // call OnIdle while in bIdle state
- if (!OnIdle(lIdleCount++))
- bIdle = FALSE; // assume "no idle" state
- }
- // phase2: pump messages while available
- do
- {
- // pump message, but quit on WM_QUIT
- if (!PumpMessage())
- return ExitInstance();
- // reset "no idle" state after pumping "normal" message
- //if (IsIdleMessage(&m_msgCur))
- if (IsIdleMessage(&(pState->m_msgCur)))
- {
- bIdle = TRUE;
- lIdleCount = 0;
- }
- } while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
- }
- }
复制代码CWinThread::PumpMessage() BOOL CWinThread::PumpMessage() { return AfxInternalPumpMessage(); }
AfxInternalPumpMessage() - BOOL AFXAPI AfxInternalPumpMessage()
- {
- _AFX_THREAD_STATE *pState = AfxGetThreadState();
- if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
- {
- #ifdef _DEBUG
- TRACE(traceAppMsg, 1, "CWinThread::PumpMessage - Received WM_QUIT.\n");
- pState->m_nDisablePumpCount++; // application must die
- #endif
- // Note: prevents calling message loop things in 'ExitInstance'
- // will never be decremented
- return FALSE;
- }
- #ifdef _DEBUG
- if (pState->m_nDisablePumpCount != 0)
- {
- TRACE(traceAppMsg, 0, "Error: CWinThread::PumpMessage called when not permitted.\n");
- ASSERT(FALSE);
- }
- #endif
- #ifdef _DEBUG
- _AfxTraceMsg(_T("PumpMessage"), &(pState->m_msgCur));
- #endif
- // process this message
- if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
- {
- ::TranslateMessage(&(pState->m_msgCur));
- ::DispatchMessage(&(pState->m_msgCur));
- }
- return TRUE;
- }
复制代码以上是MFC的消息循环,接着重写CFrame的WindowProc虚函数,然后下断点调试跟入: 堆栈窗口显示 AfxWndProc调用的 WindowProc - BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
- {
- if (message == WM_COMMAND)
- {
- if (OnCommand(wParam, lParam))
- {
- lResult = 1;
- goto LReturnTrue;
- }
- return FALSE;
- }
- const AFX_MSGMAP* pMessageMap = GetMessageMap();
- //这个赋值很有用 消息映射的关键
- const AFX_MSGMAP_ENTRY* lpEntry;
- //这个结构也是很重要的
- AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
- //建立一条512大小的 消息缓存,本地建立的。或者说本类建立的,
- //下次捕捉到就立马跳转执行
- if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
- {
- // cache hit
- lpEntry = pMsgCache->lpEntry;
- winMsgLock.Unlock();
- if (lpEntry == NULL)
- return FALSE;
- // cache hit, and it needs to be handled
- if (message < 0xC000)
- goto LDispatch;
- else
- goto LDispatchRegistered;
- }
- else
- {
- //保存消息缓存中,下次遇到不就不再去搜索消息了直接hit击中。
- pMsgCache->nMsg = message;
- pMsgCache->pMessageMap = pMessageMap;
- //一个死循环 具体在做什么下面进行分析
- for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;
- pMessageMap = (*pMessageMap->pfnGetBaseMap)())
- {
- ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
- if (message < 0xC000)
- {
- // constant window message
- if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
- message, 0, 0)) != NULL)
- {
- pMsgCache->lpEntry = lpEntry;
- winMsgLock.Unlock();
- goto LDispatch;
- }
- }
- }
- }
- }
复制代码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) };
接下来手动实现消息映射代码: 了解消息映射机制的关键在于读取 三条宏里面的代码: DECLARE_MESSAGE_MAP() BEGIN_MESSAGE_MAP(MyWnd, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP() 宏解开后 还是可以正常运行程序的: 消息映射机制的原理就在这两个函数 和静态变量中: static const AFX_MSGMAP* PASCAL GetThisMessageMap(); virtual const AFX_MSGMAP* GetMessageMap() const; static const AFX_MSGMAP_ENTRY _messageEntries[] static const AFX_MSGMAP messageMap 响应消息这里下断点然后看堆栈窗口: 来自OnWndMsg函数调用用进入后发现: 这行代码调用 往上走: 发现是从上面的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的地址 也就是 基类的 一个函数指针 和 当前类 静态数组的首地址。 那么大胆推测下 如果基类也有这样的宏(不会变的内容) 那么 这样就会形成一个链表了。 基类确实存在消息映射宏。那么可以画一张图来演示了。 首先一路追,追到CCmdTarget 类的时候 显示: DECLARE_MESSAGE_MAP() // base class - no {{ }} macros 那么也就是说这个类是终点,里面没有东西了。 链表的头节点就是 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流程。
|