MFC消息映射


 
突然間想起這個是因爲最近看到了AFX_MSGMAP_ENTRY這個結構,結果愣了半天,於是乎乾脆又將這一塊的相關知識看了一遍,哎,人上了歲數記憶就不行,需要寫點東西幫忙記錄一下自己的學習歷程,不提這個,跑題了:
提起MFC消息,到底程序在哪裏處理消息的,因爲MFC對windows程序的很多地方進行了封裝,導致入口地方不是那麼容易找到,不過其實無非就是封裝而已——CWinApp(應用程序),在MFC類窗口CxxxApp中查看基類就能找到,調試一下就會發現MFC程序執行的大致流程是:
CWinApp------>AfxWinMain(AfxGetApp,AfxInit)------>CWinApp::InitApplication------>CWinApp::InitINstance(Create)------>PreCreateWindow------>CWnd::ShowWindow,CWnd::UpdateWindow
AfxWinMain------>CWinApp::Run------>AfxInternalPumpMessage
Run這個函數來創建和處理消息循環
BOOL AFXAPI AfxInternalPumpMessage()
    {
        MSG msg;
        ::GetMessage(&msg, NULL, NULL, NULL);
        if (!AfxPreTranslateMessage(&msg))
        {
            ::TranslateMessage(&msg);
	    ::DispatchMessage(&msg);
        }
        return TRUE;
    }</span></span>
顯而易見,MFC中處理消息也是利用了win32下的消息處理
那麼還是這樣的結構:
typedef struct tagMSG { 
HWND hwnd; 
UINT message;  //消息的類型(即事件的類型) 
WPARAM wParam;  //附加或稱數據參數1 
LPARAM lParam; 
DWORD time; 
POINT pt; 
} MSG; </span>
有了這個概念之後我們知道,MFC通過消息映射機制來派發或處理各種消息,關於消息映射,我去偷張圖片過來:

沒有高清無碼大圖,只能湊合着看了,關於上圖可以這樣理解:
子窗口在處理消息時需要用FUN1與是在自己的表中找到了,可是要用FUN100的時候,就找不到了,然後通過ParentTable到父類中MsgHandles找,找不到了,那就調用DefWindowProc默認處理了。
而MFC在實現這一過程中,需要去構建這個表,我們經常看到這樣的代碼,剛開始只是知道這個由vs自動生成,可是並不知道爲什麼
BEGIN_MESSAGE_MAP(CexampleDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()



我們來詳細講講這個地方:
轉到定義:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
    PTM_WARNING_DISABLE \
    const AFX_MSGMAP* theClass::GetMessageMap() const \
        { return GetThisMessageMap(); } \
    const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
    { \
        typedef theClass ThisClass;                           \
        typedef baseClass TheBaseClass;                       \
        static const AFX_MSGMAP_ENTRY _messageEntries[] =  \
        {
#define END_MESSAGE_MAP() \
        {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
    }; \
        static const AFX_MSGMAP messageMap = \
        { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
        return &messageMap; \
    }                                  \
    PTM_WARNING_RESTORE




上面代碼實質上添加了_messageEntries和messageMap(這是構建消息映射的關鍵)


AFX_MSGMAP_ENTRY _messageEntries[]
   這是一個AFX_MSGMAP_ENTRY 類型的數組變量,是一個靜態成員變量,用來容納類的消息映射條目。一個消息映射條目可以用AFX_MSGMAP_ENTRY結構來描述。
 
AFX_MSGMAP_ENTRY結構的定義如下:
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)
};


 
    從上述結構可以看出,每條映射有兩部分的內容:第一部分是關於消息ID的,包括前四個域;第二部分是關於消息對應的執行函數,包括後兩個域。
 
    在上述結構的六個域中,pfn是一個指向CCmdTarger成員函數的指針。函數指針的類型定義如下:
 
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
 
    當使用一條或者多條消息映射條目初始化消息映射數組時,各種不同類型的消息函數都被轉換成這樣的類型:不接收參數,也不返回參數的類型。因爲所有可以有消息映射的類都是從CCmdTarge派生的,所以可以實現這樣的轉換。
    nSig是一個標識變量,用來標識不同原型的消息處理函數,每一個不同原型的消息處理函數對應一個不同的nSig。在消息分發時,MFC內部根據nSig把消息派發給對應的成員函數處理,實際上,就是根據nSig的值把pfn還原成相應類型的消息處理函數並執行它。
 
  第二個成員變量的聲明
   AFX_MSGMAP messageMap;
   這是一個AFX_MSGMAP類型的靜態成員變量,從其類型名稱和變量名稱可以猜出,它是一個包含了消息映射信息的變量。的確,它把消息映射的信息(消息映射數組)和相關函數打包在一起,也就是說,得到了一個消息處理類的該變量,就得到了它全部的消息映射數據和功能。AFX_MSGMAP結構的定義如下:
struct AFX_MSGMAP
{
    const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
    const AFX_MSGMAP_ENTRY* lpEntries;
};


 
    成員函數 GetMessageMap() 用來得到自身消息映射的函數。

有了上面的一個概念之後,應該很容易理解MFC在處理一個消息的時候,往_messageEntries里加入要處理的函數,然後通過messageMap就可以得到消息映射數組_messageEntries和基類的消息映射數組的地址了


回頭看看MFC的消息映射機制,瞭然了,在處理消息的時候構建的那張表,

BEGIN_MESSAGE_MAP(CexampleDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()



第一個處理的消息是ON_WM_SYSCOMMAND(),處理消息函數(右擊轉到定義)OnSysCommand

建好消息映射表後,消息就會流向到指定的處理函數裏。


差不多消息映射機制就回顧了這麼多了,寫的很亂,放元旦擱了好幾天又開始寫的,思路接不上了呃。




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