MFC原理第五講.消息映射.以及如何添加消息


        MFC原理第五講.消息映射.以及如何添加消息

一丶消息映射是什么

  我們知道.Win32程序.都是通過消息去驅動的. 不斷的在處理消息. 只要我們使用固定的宏.就可以讓我們的框架知道一旦消息發生.該往哪一個類傳遞. 每一個類可以擁有一個映射表格.

也可以沒有.  

  關鍵宏
  1. DECLARE_MESSAGE_MAP  聲明宏.放在類中

  2. BEGIN_MESSAGE_MAP      實現宏放在類實現外

  3. END_MESSAGE_MAP   實現宏放在類外面

如何添加消息.

  如果我們添加了 BEGIN 跟 END 兩個宏之后. 我們在中間添加他們的消息就可以.

  例如:

  

BEGIN_MESSAGE_MAP(CMainWnd,CFrameWnd)   //兩個參數.第一個是自己的類.第二個是父類.
    ON_WM_LBUTTONDOWN()                 我們的消息. 需要添加聲明以及實現.
END_MESSAGE_MAP()

 我們的消息.MFC都給我們封裝好了.如果實現消息. 則一律 ON_WM開頭.  消息 是WM_XXX.  

 我們可以在 afxmsg.h 中查看.

我們在類中聲明消息.並且添加消息處理函數即可.

例如以下代碼:

  

上面是聲明.下面是實現.

應用程序截圖

    

二丶消息映射原理分析

  不管學習任何東西. 先學會用.再去學習原理.這樣是最快的.

現在對我們的宏進行拆分來看.

  1.DECLARE_MESSAGE_MAP 宏查看.

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

這個宏一幕了然. 添加了兩個方法.一個是虛方法.

  那么這個是聲明宏.那么另外兩個應該就有實現宏了.看下實現宏的拆解.

  2.BEGIN_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[] =  \
        {

可以看得出.拆解開是一個一半的東西. 這里需要添加我們的消息了.所以下面是END_MESSAGE_MAP

所以兩個一起解析.

  

const AFX_MSGMAP* CMainWnd::GetMessageMap() const
{
    return GetThisMessageMap();
}

const AFX_MSGMAP* PASCAL CMainWnd::GetThisMessageMap()
{
    typedef CMainWnd ThisClass;
    typedef CFrameWnd TheCFrameWnd;
    static const AFX_MSGMAP_ENTRY _messageEntries[] =
    {
        //這里添加我們的消息

    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }      //END_MESSAGE_MAP 宏.下面都是.
    };
    static const AFX_MSGMAP messageMap = 
    { 
        &TheCFrameWnd::GetThisMessageMap, &_messageEntries[0] 
    }; 
        return &messageMap; 
}

可以看出.這兩個宏就是對聲明的兩個宏的實現. 而上面我們添加的ON_WM_LBUTTONDOWN 就是放在 END_MESSAGE_MAP 上面的. 我們看一下格式.

const AFX_MSGMAP* CMainWnd::GetMessageMap() const
{
    return GetThisMessageMap();
}

const AFX_MSGMAP* PASCAL CMainWnd::GetThisMessageMap()
{
    typedef CMainWnd ThisClass;
    typedef CFrameWnd TheCFrameWnd;
    static const AFX_MSGMAP_ENTRY _messageEntries[] =
    {
        //這里添加我們的消息
        ON_WM_LBUTTONDOWN()                                //我們要解析這個消息
    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
    };
    static const AFX_MSGMAP messageMap = 
    { 
        &TheCFrameWnd::GetThisMessageMap, &_messageEntries[0] 
    }; 
        return &messageMap; 
}
#define ON_WM_LBUTTONDOWN() \
    { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
        (AFX_PMSG)(AFX_PMSGW) \
        (static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, CPoint) > ( &ThisClass :: OnLButtonDown)) },

其實上面可以看出.其實是一個 _messageEntries數組.保存着我們的消息. 我們替換這個宏看一下.

    { 
        WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, 
        (AFX_PMSG)(AFX_PMSGW)(static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, CPoint) > ( &ThisClass :: OnLButtonDown)) 
    },

 

解析完成可以看到.第一個是 WM_LBUTTONDOWN 對應着他的處理函數 OnLButtonDown 也就是最后一個參數.

所以現在明白了我們添加消息的時候.為什么要按照規定的格式. 因為這個宏已經使消息一一對應了.

因為上面是一個結構體數組用來保存.所以我們可以看一下這個數組是什么格式的.

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函數的返回值類型 pfn 消息處理對應函數.

但是我們要具體解釋一下. 第5個跟第6個參數.

第五個參數指明了我們函數的返回值

比如上圖:

  AfxSig_vwp 類型. 我們可以點擊F12取頭文件中查看.例如下圖:

 

所以我們知道了我們的函數定義的返回以及參數類型了.

我是按照VS2015下的MFC講解. 如果是VC6.0下. 那么 需要添加三個成員方法. 不過實現是類似了. 具體可以查看VC60的MFC源碼.

根據VS2015給我們添加了兩個成員方法. 可以看到返回值也是一個結構體.那么看下這個結構體內容吧.

struct AFX_MSGMAP
{
    const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();  父類的MAP
    const AFX_MSGMAP_ENTRY* lpEntries;            存放自己當前的消息結構.
};

 

上面總結一下.

  1.首先聲明宏會為我們添加兩個方法.  獲取消息映射表

  2.實現宏則實現這兩個方法.   而且還有兩個結構. 一個是存放消息信息的結構. 另一個結構則是存放父類的MessageMap .以及自己當前存放信息的結構

那么知道了消息映射表.我們可以在按鈕點擊的時候. 按一下打印出我們消息的時候對應的地址了. 這個對於逆向很有幫助. 學到最后我們可以寫一個工具. 可以有很好的切入點了.

具體代碼如下:

  

void CMainWnd::OnMyLButtonDown(UINT flag, CPoint point) //知道了結構我們自己改也可以.
{
	//獲取消息映射表.看看消息傳遞.
	const AFX_MSGMAP * pMsg = GetMessageMap();
	//打印出自己當前按鈕點擊的時候的地址.
	CString str;
	str.Format(TEXT("地址 = %p\r\n"), pMsg->lpEntries->pfn);  從消息映射表中獲取我們的函數地址. 其實應該遍歷.並且判斷是否是消息.這里直接就偷懶了.因為只有一個消息.
	OutputDebugString(str);
	AfxMessageBox(TEXT("OnLButtonDown"));
}

應用程序截圖:

 

很方便的看到了地址. 我們可以使用逆向工具 到這個地址查看.看看是否是我們點擊的時候的消息.

因為是Dbg版本.所以有Jmp跳轉的一層.我們直接跳轉進去查看.

可以很簡單的看到.就是我們編寫的代碼的位置.

下一講講解消息傳遞.

 

鏈接:https://pan.baidu.com/s/1HQo3muBivkRcM0koKjICeQ 密碼:p5dj


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM