Thinking in MFC---消息機制2(消息映射)

Thinking in MFC---消息機制2

引言

在這一篇,我們會介紹一下在窗口類中,如何將消息路由到消息處理函數中去。


一、AFX_MSGMAP_ENTRY

在開始之前,我們有必要了解這個結構體。

struct AFX_MSGMAP_ENTRY
{
    UINTnMessage;   //windows message
    UINTnCode;      //control code or WM_NOTIFY code
    UINTnID;        //control ID (or 0 for windows messages)
    UINTnLastID;    //used for entries specifying a range of control id's
    UINT_PTRnSig;       //signature type (action) or pointer to message #
    AFX_PMSGpfn;    //routine to call (or special value)
};

其中pfn便是消息處理函數指針。

對於這個結構體中的其他成員變量,從它的命名和註釋應該可以瞭解大概。接下來就看一個相關的宏。


二、GetMessageMap

這個函數就如它的命名,獲得消息映射。

我們在使用MFC ClassWizard添加消息響應函數的時候,會自動在我們的窗口類中添加幾行代碼。

DECLARE_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(yourclass, baseclass)
END_MESSAGE_MAP()

讓我們一個一個看上面三個宏。

第一個DECLARE_MESSAGE_MAP(),從它的命名上就能明白它的作用,它是申明MessageMap。我們去它的定義處看看。

#define DECLARE_MESSAGE_MAP() \
protected: \
    static constAFX_MSGMAP* PASCAL GetThisMessageMap(); \
    virtual constAFX_MSGMAP* GetMessageMap() const; \

這裏申明瞭兩個函數。至於這兩個函數是做什麼,接下來就看它們的定義。就要看接下來兩個宏。

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
    PTM_WARNING_DISABLE\
    const AFX_MSGMAP* theClass::GetMessageMap() const \
        { return GetThisMessageMap(); } \
    const AFX_MSGMAP* PASCALtheClass::GetThisMessageMap() \
    { \
        typedef theClass ThisClass;                        \
        typedef baseClass TheBaseClass;                    \
        static constAFX_MSGMAP_ENTRY _messageEntries[] =  \
        {

上面的第一個函數GetMessageMap,其實在調用GetThisMessageMap,所以我們重點就看第二個函數。

GetThisMessageMap其實就是定義了 AFX_MSGMAP_ENTRY結構體數組,而這個結構體就是我們第一節中的消息結構體。

接下來看一下

#define END_MESSAGE_MAP() \
        {0, 0,0, 0, AfxSig_end, (AFX_PMSG)0 } \
    }; \
        static constAFX_MSGMAP messageMap = \
        {&TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
        return &messageMap; \
    }                                 \
    PTM_WARNING_RESTORE

上面這個宏主要是對GetThisMessageMap的結束處理。

從BEGIN_MESSAGE_MAP和END_MESSAGE_MAP這兩個宏來看,GetThisMessageMap便是返回該類的消息映射結構體的數組。

接下來我們就要看MFC是怎麼使用這個消息映射結構體的。

這裏還有兩個宏PTM_WARNING_DISABLE和PTM_WARNING_RESTORE,這兩個宏作用就是在定義上面兩個函數的時候,不會產生warning。

它們兩個的源碼是這樣的

#define PTM_WARNING_DISABLE \
    __pragma(warning( push )) \
    __pragma(warning( disable : 4867 ))
#define PTM_WARNING_RESTORE \
    __pragma(warning( pop ))

有興趣的讀者可以看一下關於warning的一些知識,這個不是我們這裏的重點,所以就不做介紹了。


接下來看一下這個宏

ON_WM_PAINT()

這個宏你在class Wizard中添加WM_PAINT事件之後,便會自動添加在

BEGIN_MESSAGE_MAP(yourclass, baseclass)
ON_WM_PAINT()
END_MESSAGE_MAP()

讓我們轉向ON_WM_PAINT()這個宏定義處看看

#define ON_WM_PAINT() \
    { WM_PAINT, 0, 0, 0, AfxSig_vv, \
        (AFX_PMSG)(AFX_PMSGW) \
        (static_cast<void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },
我們聯繫之前所講的BEGIN_MESSAGE_MAP中的代碼,其實這個ON_WM_PAINT()爲messageEntries賦值,這裏是一個結構體AFX_MSGMAP_ENTRY。你可以看到第一個參數是WM_PAINT便是nMessage,最後重要的是消息最終路由到那個函數處理,ThisClass :: OnPaint便是處理函數。

類似ON_WM_PAINT()其它宏都是註冊了消息處理事件,告訴MFC什麼消息交給什麼函數處理。


三、窗口消息路由

至於怎麼路由,筆者在網上大致找到了一下這麼一段代碼,說明這段代碼只是用來介紹,並非MFC的源碼。

BOOLCWnd::OnWndMsg(……)//
{
       if(message == WM_COMMAND)
              OnCommand(wParam,lParam);
       if(message == WM_NOTIFY)
              OnNotify(wParam,lParam,&lResult);
       pMessage = GetMessageMap();
       for(; pMessageMap!=NULL; pMessageMap =pMessageMap->pBaseMap)
       {
              if((lpEntry=AfxFindMessageEntry(pMessageMap->lpEntries,
                     message,0,0))!=NULL)
                     break;
       }
       (this->*(lpEntry->pnf))(……);//調用消息響應函數
}
AFX_MSGMAP_ENTRYAfxFindMessageEntry(……)
{
       while(lpEntry->nSign!=AfxSig_end)
       {
              if(lpEntry->nMessage==nMsg&&lpEntry->nCode==nCode&&nID>=lpEntry->nID
                            &&nID<=lpEntry->nLastID)
              {
                     return lpEntry;
              }
              lpEntry++;
       }
}

在上面的代碼中可以找到GetMessageMap的聲影,它返回一個消息映射並存放在pMessage變量中。

接下來進行遍歷,找到符合要求的消息映射,找到之後使用存放在其中的消息處理函數來調用消息處理函數。

上面提到的OnCommand和OnNotify,筆者這裏不做介紹,留在下一篇介紹。


四、總結

開始編程不久,便接觸到了MFC。用了MFC寫了自己第一個視窗程序,寫了第一個遊戲,在學校課題演示的時候也是喜歡用MFC做演示。

現在工作了,項目的界面部分也在使用MFC開發的,值得慶幸的是,現在微軟對於MFC的界面做了很大的改善,想起剛開始不斷重寫控件類,那個真是記憶猶新。

在平時編程的時候,VS這個集成工具確實很方便。但現在想把一些MFC零零碎碎的知識整合起來。所以這個系列可能會深入地去介紹MFC。


 

發佈了38 篇原創文章 · 獲贊 81 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章