关于MFC 消息映射机制剖析.docx

上传人:b****6 文档编号:5258093 上传时间:2022-12-14 格式:DOCX 页数:15 大小:27.93KB
下载 相关 举报
关于MFC 消息映射机制剖析.docx_第1页
第1页 / 共15页
关于MFC 消息映射机制剖析.docx_第2页
第2页 / 共15页
关于MFC 消息映射机制剖析.docx_第3页
第3页 / 共15页
关于MFC 消息映射机制剖析.docx_第4页
第4页 / 共15页
关于MFC 消息映射机制剖析.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

关于MFC 消息映射机制剖析.docx

《关于MFC 消息映射机制剖析.docx》由会员分享,可在线阅读,更多相关《关于MFC 消息映射机制剖析.docx(15页珍藏版)》请在冰豆网上搜索。

关于MFC 消息映射机制剖析.docx

关于MFC消息映射机制剖析

软采用了所谓的消息映射机制,来完成不同对象之间消息的传递,本文就MFC9.0源码进行分析,大致讲解MFC的消息映射机制。

步入正题,在AfxWinMain()函数中,当MFC框架初始化完成后,即pThread->InitInstance()执行完成,就开始进行消息循环,入口函数是pThread->Run():

[cpp] viewplaincopy

1.// Perform specific initializations  

2.if (!

pThread->InitInstance())// MFC初始化框架  

3.{  

4.    if (pThread->m_pMainWnd !

= NULL)  

5.    {  

6.        TRACE(traceAppMsg, 0, "Warning:

 Destroying non-NULL m_pMainWnd\n");  

7.        pThread->m_pMainWnd->DestroyWindow();  

8.    }  

9.    nReturnCode = pThread->ExitInstance();  

10.    goto InitFailure;  

11.}  

12.

nReturnCode = pThread->Run();// 进入消息循环  

执行CWinApp:

:

Run():

[cpp] viewplaincopy

1.// Main running routine until application exits  

2.int CWinApp:

:

Run()  

3.{  

4.    if (m_pMainWnd == NULL && AfxOleGetUserCtrl())  

5.    {  

6.        // Not launched /Embedding or /Automation, but has no main window!

  

7.        TRACE(traceAppMsg, 0, "Warning:

 m_pMainWnd is NULL in CWinApp:

:

Run - quitting application.\n");  

8.        AfxPostQuitMessage(0);  

9.    }  

10.    return CWinThread:

:

Run();  

11.}  

执行CWinThread:

:

Run():

[cpp] viewplaincopy

1.// main running routine until thread exits  

2.int CWinThread:

:

Run()  

3.{  

4.    ASSERT_VALID(this);  

5.    _AFX_THREAD_STATE* pState = AfxGetThreadState();  

6.  

7.    // for tracking the idle time state  

8.    BOOL bIdle = TRUE;  

9.    LONG lIdleCount = 0;  

10.  

11.    // acquire and dispatch messages until a WM_QUIT message is received. 

12.GetMessage:

从系统获取消息,将消息从系统中移除,属于阻塞函数。

当系统无消息时,GetMessage会等待下一条消息。

而函数PeekMesssge是以查看的方式从系统中获取消息,可以不将消息从系统中移除,是非阻塞函数;当系统无消息时,返回FALSE,继续执行后续代码 

13.    for (;;)  

14.    {  

15.        // phase1:

 check to see if we can do idle work  

16.        while (bIdle &&  

17.            !

:

:

PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))  

18.        {  

19.            // call OnIdle while in bIdle state  

20.            if (!

OnIdle(lIdleCount++))  

21.                bIdle = FALSE; // assume "no idle" state  

22.        }  

23.  

24.        // phase2:

 pump messages while available  

25.        do  

26.        {  

27.            // pump message, but quit on WM_QUIT  

28.            if (!

PumpMessage())  

29.                return ExitInstance();  

30.  

31.            // reset "no idle" state after pumping "normal" message  

32.            //if (IsIdleMessage(&m_msgCur))  

33.            if (IsIdleMessage(&(pState->m_msgCur)))  

34.            {  

35.                bIdle = TRUE;  

36.                lIdleCount = 0;  

37.            }  

38.  

39.        } while (:

:

PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));  //查看消息队列中是否有消息

40.    }  

41.}  

在do-while循环中进行消息的路由,主要函数就是 PumpMessage(),我们跟着进入这个函数看看做了什么:

[cpp] viewplaincopy

1.BOOL CWinThread:

:

PumpMessage()  

2.{  

3.  return AfxInternalPumpMessage();  

4.}  

继续跟踪:

[cpp] viewplaincopy

1.BOOL AFXAPI AfxInternalPumpMessage()  

2.{  

3.    _AFX_THREAD_STATE *pState = AfxGetThreadState();  

4.  

5.    if (!

:

:

GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))// 从消息队列获取消息  

6.    {  

7.#ifdef _DEBUG  

8.        TRACE(traceAppMsg, 1, "CWinThread:

:

PumpMessage - Received WM_QUIT.\n");  

9.            pState->m_nDisablePumpCount++; // application must die  

10.#endif  

11.        // Note:

 prevents calling message loop things in 'ExitInstance'  

12.        // will never be decremented  

13.        return FALSE;  

14.    }  

15.  

16.#ifdef _DEBUG  

17.  if (pState->m_nDisablePumpCount !

= 0)  

18.    {  

19.      TRACE(traceAppMsg, 0, "Error:

 CWinThread:

:

PumpMessage called when not permitted.\n");  

20.      ASSERT(FALSE);  

21.    }  

22.#endif  

23.  

24.#ifdef _DEBUG  

25.    _AfxTraceMsg(_T("PumpMessage"), &(pState->m_msgCur));  

26.#endif  

27.  

28.  // process this message  

29.  

30.

    if (pState->m_msgCur.message !

= WM_KICKIDLE && !

AfxPreTranslateMessage(&(pState->m_msgCur)))  

31.    {  

32.:

:

TranslateMessage(&(pState->m_msgCur));  

33.:

:

DispatchMessage(&(pState->m_msgCur));  

34.    }  

35.  return TRUE;  

36.}  

从上面的代码我们可以看到MFC是通过 GetMessage()来获取消息,然后再看下面这几句代码:

[cpp] viewplaincopy

1.if (pState->m_msgCur.message !

= WM_KICKIDLE && !

AfxPreTranslateMessage(&(pState->m_msgCur)))  

2.{  

3.    :

:

TranslateMessage(&(pState->m_msgCur));  

4.    :

:

DispatchMessage(&(pState->m_msgCur));  

5.}  

也就是说当系统获取消息后,先调用 AfxPreTranslateMessage()这个看起来跟我们经常看到的PreTranslateMessage()很像!

我们来看看到底发生了什么:

[cpp] viewplaincopy

1.BOOL __cdecl AfxPreTranslateMessage(MSG* pMsg)  

2.{  

3.  CWinThread *pThread = AfxGetThread();  

4.  if( pThread )  

5.    return pThread->PreTranslateMessage( pMsg );  

6.  else  

7.    return AfxInternalPreTranslateMessage( pMsg );  

8.}  

执行 pThread->PreTranslateMessage():

[cpp] viewplaincopy

1.BOOL CWinThread:

:

PreTranslateMessage(MSG* pMsg)  

2.{  

3.    ASSERT_VALID(this);  

4.    return AfxInternalPreTranslateMessage( pMsg );  

5.}  

执行AfxInternalPreTranslateMessage():

[cpp] viewplaincopy

1.BOOL AfxInternalPreTranslateMessage(MSG* pMsg)  

2.{  

3.//  ASSERT_VALID(this);  

4.  

5.    CWinThread *pThread = AfxGetThread();  

6.    if( pThread )  

7.    {  

8.        // if this is a thread-message, short-circuit this function  

9.        if (pMsg->hwnd == NULL && pThread->DispatchThreadMessageEx(pMsg))  

10.            return TRUE;  

11.    }  

12.  

13.    // walk from target to main window  

14.    CWnd* pMainWnd = AfxGetMainWnd();  

15.    if (CWnd:

:

WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))// 注意这个函数  

16.        return TRUE;  

17.  

18.    // in case of modeless dialogs, last chance route through main  

19.    //   window's accelerator table  

20.    if (pMainWnd !

= NULL)  

21.    {  

22.         CWnd* pWnd = CWnd:

:

FromHandle(pMsg->hwnd);  

23.         if (pWnd->GetTopLevelParent() !

= pMainWnd)  

24.            return pMainWnd->PreTranslateMessage(pMsg);  

25.    }  

26.  

27.    return FALSE;   // no special processing  

28.}  

执行CWnd:

:

WalkPreTranslateTree(pMainWnd->GetSafeHwnd(),pMsg)

[cpp] viewplaincopy

1.BOOL PASCAL CWnd:

:

WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)  

2.{  

3.    ASSERT(hWndStop == NULL || :

:

IsWindow(hWndStop));  

4.    ASSERT(pMsg !

= NULL);  

5.  

6.    // walk from the target window up to the hWndStop window checking  

7.    //  if any window wants to translate this message  

8.  

9.    for (HWND hWnd = pMsg->hwnd; hWnd !

= NULL; hWnd = :

:

GetParent(hWnd))  

10.    {  

11.        CWnd* pWnd = CWnd:

:

FromHandlePermanent(hWnd);  

12.        if (pWnd !

= NULL)  

13.        {  

14.            // target window is a C++ window  

15.            if (pWnd->PreTranslateMessage(pMsg))  

16.                return TRUE; // trapped by target window (eg:

 accelerators)  

17.        }  

18.  

19.        // got to hWndStop window without interest  

20.        if (hWnd == hWndStop)  

21.            break;  

22.    }  

23.    return FALSE;       // no special processing  

24.}  

MFC在后台维护了一个句柄和C++对象指针的映射表,一旦有消息产生时,我们知道这个消息的结构体中包含了该消息所属窗口的句柄,那么通过这个句柄我们可以找到相对于的C++对象的指针,在for循环里面遍历当前窗口的所有父窗口,查找是否有消息重写,一旦有子类重写父类消息的,则通过当前消息所属窗口的句柄来调用CWnd:

:

FromHandlePermanent(hWnd) 函数,从而得到当前C++对象的指针,最后调用PreTranslateMessage(),因为  PreTranslateMessage()是虚函数,所以调用的是子类的 PreTranslateMessage(),该函数可以自定义一部分消息的处理方式。

需要注意的是,PreTranslateMessage()返回为FALSE时,说明没有发生消息的重写,则把消息直接给 TranslateMessage()和DispatchMessage()进行处理,当返回是TRUE时,则去消息队列获取下一条消息。

另外,SendMessage()是直接发送给WindowProc进行处理(该函数是一个DispatchMessage()中的一个回调函数,用于处理默认的一些系统消息),没有进入消息队列,所以不会被GetMessage()抓取到,所以也就不会PreTranslateMessage()抓到了,但是PostMessage()是进入消息队列的,是可以被GetMessage()抓到的。

所以从上面的代码跟踪可见,当我们需要重写一些系统消息时,比如给程序设置一些快捷键等,可以在PreTranslateMessage()中进行操作,当然,不是PreTranslateMessage()并不是可以重写所有消息,有一些消息也是无法处理的,这时可以交给WindowProc进行处理,WindowProc()也是虚函数。

[cpp] viewplaincopy

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 = 0;  

5.

    if (!

OnWndMsg(message, wParam, lParam, &lResult))  

6.        lResult = DefWindowProc(message, wParam, lParam);  

7.    return lResult;  

8.}  

消息的映射主要是通过OnWndMsg()进行处理,考虑到该函数代码很多,就不全贴了,我们分析其中一小段:

[cpp] viewplaincopy

1.for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap !

= NULL;  

2.    pMessageMap = (*pMessageMap->pfnGetBaseMap)())  

3.{  

4.    // Note:

 catch not so common but fatal mistake!

!

  

5.    //      BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)  

6.    ASSERT(pMessageMap !

= (*pMessageMap->pfnGetBaseMap)());  

7.    if (message < 0xC000)  

8.    {  

9.        // constant window message  

10.        if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,  

11.            message, 0, 0)) !

= NULL)  

12.        {  

13.            pMsgCache->lpEntry = lpEntry;  

14.            winMsgLock.Unlock();  

15.            goto LDispatch;  

16.        }  

17.    }  

18.    else  

19.    {  

20.        // registered windows message  

21.        lpEntry = pMessageMap->lpEntries;  

22.        while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) !

= NULL)  

23.        {  

24.            UINT* pnID = (UINT*)(lpEntry->nSig);  

25.            ASSERT(*pnID >= 0xC000 || *pnID == 0);  

26.                // must be successfully registered  

27.            if (*pnID == message)  

28.            {  

29.                pMsgCache->lpEntry = lpEntry;  

30.                winMsgLock.Unlock();  

31.                goto LDispatchRegistered;  

32.            }  

33.            lpEntry++;      // keep looking past this one  

34.        }  

35.    }  

36.}  

在for循环里面有这样一个数据:

 AFX_MSGMAP* pMessageMap(),它代表当前C++对象的消息映射表指针,那么这里的消息映射表到底是个什么样的数据结构,我们来一探究竟。

我们打开

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 艺术

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1