<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    MFC消息映射的原理:筆記

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    多態的實現機制有兩種,一是通過查找絕對位置表,二是查找名稱表;兩者各有優缺點,那么為什么mfc的消息映射采用了第二種方法,而不是c++使用的第一種呢?因為在mfc的gui類庫是一個龐大的繼承體系,而里面的每個類有很多成員函數(只說消息反映相關的成員函數啊),而且在派生類中,需要改寫的也比較少(我用來做練習的程序就是那么一兩個,呵呵)。那么用c++的虛函數的實現機制會導致什么問題呢?就是大量虛表的建立使得空間浪費掉很多。

     

    嗯…怎么辦呢?于是各大c++名庫(比如QT,MFC,VCL…)在消息映射的實現方面,拋開了虛函數的方式,而用了第二種方法:查找名稱表,其原理五花八門,各顯神通,讓我想到了春秋時代,各國諸侯置周天子不顧,挾天子令諸侯,各自為政的階段,呵呵~

     

    現在先說MFC的做法:MFC消息映射機制的原理,也就是MFC是怎么做到一個消息來了,就調用相應的成員函數的?(在VC編程里面用的是消息循環機制,那比較好理解,就是在消息處理程序里面來個大大的swicth…)

     

    好了,在c++ stl成熟的現在,很多人都會想到用map來匹配(據我所知,有些公司很喜歡這樣用,拋開了mfc的做法),但是當時stl沒有流行,所以mfc設計者們就來個鏈表查找。

     

    先看用法:

    首先,要用消息處理的類,必須要繼承自CcmdTarget類;

    然后,在類的聲明中有如下的宏:DECLARE_MESSAGE_MAP()和需要實現的消息映射

    然后,在類的實現文件中,有如下宏:BEGIN_MESSAGE_MAP(…), … END_MESSAGE_MAP()

    具體例子如下所示:

     

    //頭文件中:

    class CMainFrame : public CFrameWnd

    {

    ……

    // 生成的消息映射函數

    protected:

         afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

         DECLARE_MESSAGE_MAP()

    };

    //實現文件中:

    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

         ON_WM_CREATE()

    END_MESSAGE_MAP()

     

     

    那些宏展開之后如下所示:

     

    class CMainFrame : public CFrameWnd

    {

    ……

    // 生成的消息映射函數

    protected:

         int OnCreate(LPCREATESTRUCT lpCreateStruct);

     

    // 下三行為宏DECLARE_MESSAGE_MAP()的展開

    protected:

         static const AFX_MSGMAP* PASCAL GetThisMessageMap();

         virtual const AFX_MSGMAP* GetMessageMap() const;

    };

     

    // 下10行為宏BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)的展開

    const AFX_MSGMAP* CmainFrame::GetMessageMap() const

    {

    return GetThisMessageMap();

    }

    const AFX_MSGMAP* CMainFrame::GetThisMessageMap()

    {

         typedef CmainFrame ThisClass;

         typedef CframeWnd TheBaseClass;

         static const AFX_MSGMAP_ENTRY _messageEntries[] =

         {

             // 下2行為宏ON_WM_CREATE()的展開

    { WM_CREATE, 0, 0, 0, AfxSig_is, (AFX_PMSG) (AFX_PMSGW)(static_cast

    < int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > ( &ThisClass :: OnCreate)) },

             // 下5行為宏END_MESSAGE_MAP()的展開

    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }

         };

    static const AFX_MSGMAP messageMap = { &TheBaseClass::GetThisMessageMap, _messageEntries[0] };

         return &messageMap;

    }

     

     

    上面這些幾乎全是數據結構的初始化代碼,先不管這里面的結構放的是什么內容,先看類聲明那個宏展開的兩個函數究竟在mfc框架的那個地方使用?(先看過程,在看數據結構,這是面向對象分析的不二法門~)

     

    下面,說一下別人總結的mfc流程中的消息分派,(參考文章會放在附錄里面的,這只是筆記嘛,呵呵)

     

    注意:我安裝的是VS2005,MFC源碼在安裝的目錄下面的  VC/atlmfc/src/mfc目錄里面

     

    1、   先假定mfc的消息入口點是CWnd::WindowProc函數(至于是如何流入的,請參考其他文章),其代碼就好像vc編程里面那個WinMain函數的消息循環部分差不多:

     

    LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

    {

        LRESULT lResult = 0;

     

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

     

            lResult = DefWindowProc(message, wParam, lParam);

     

        return lResult;

    }

     

    看,就是看一下該消息在OnWndWsg里是否找到對應的處理函數,如果沒找到,用DefWindowProc處理;

     

    2、   那么,這個OnWndWsg里面就是調用以上宏展開的那個函數來取得相關的消息映射滴~簡化過后的代碼如下:

     

    BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)

    {

        LRESULT lResult = 0;

        const AFX_MSGMAP* pMessageMap;

     

         //取得消息映射結構,GetMessageMap為虛函數,所以實際取的是CmainFrame的消息映射

        pMessageMap = GetMessageMap();

     

        // 查找對應的消息處理函數

        for (pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap)

            if (message < 0xC000)

                if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL)

                    goto LDispatch;

        ... ...

    LDispatch:

        //通過聯合來匹配正確的函數指針類型

        union MessageMapFunctions mmf;

    mmf.pfn = lpEntry->pfn;

    ……

     

    其中的pMessageMap = GetMessageMap();語句要留意,因為GetMessageMap是虛函數,在上面的CmainFrame類中已經重寫了,所以,調用的實際上是CmainFrame的GetMessageMap;返回一個表格的指針pMessageMap,然后根據消息的類型在里面尋找,這個表格的數據結構如下:

    struct AFX_MSGMAP

    {

         const AFX_MSGMAP* (* pfnGetBaseMap)();

         const AFX_MSGMAP_ENTRY* lpEntries;

    };

     

    再看CmainFrame中的代碼:

    static const AFX_MSGMAP messageMap =    { &TheBaseClass::GetThisMessageMap, _messageEntries[0] };

    就知道:這個表格其實是一個鏈表,放的是基類的GetMassageMap函數指針,和當前類的真正表格_messageEntries的入口地址。再看_messageEntries里面放的又是什么:

     

    nMessage

    nCode

    nID

    nLastID

    nSig

    nPfn

    WM_CREATE

    0

    0

    0

    AfxSig_is

    &CmainClass::OnCreate

    0

    0

    0

    0

    AfxSig_end

    0

     

    好了,現在看到了WM_CREATE 就連著 &CmainClass::OnCreate,再結合上面CWnd::OnWndMsg的代碼:

     

        for (; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap)

            if (message < 0xC000)

                if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL)

                    goto LDispatch;

    就可以推理得到,AfxFindMessageEntry就是在找message和_messageEntries.nMessage的相等,如果不等,就往基類里面找: pMessageMap = pMessageMap->pBaseMap

     

    找到了就跳到Ldispatch,其中,有:mmf.pfn = lpEntry->nPfn;這個就是放著該消息處理函數的內容。

     

    3、   總結:

    其實那些宏的作用就是用來初始化那個鏈表messageMap 和當前類處理函數的表格_messageEntries,當框架代碼運行到pMessageMap = GetMessageMap();時候,由于虛函數的作用,使得當面運行當前窗口CmainFrame的GetMassageMap,把CmainFrame的表格給pMessageMap,然后再從派生類往基類一層層找響應的處理函數。

     

    這與c++虛函數機制相比,其好處是可以省空間,每個類里面只有自己重寫的函數信息,其他信息在基類里面找;
    不好的地方就是搜鏈表,從當前類往基類上搜,時間上可能久一點;

    mfc消息分派的基本原理就是這樣的,但是還有很多的細節沒有深入,比如那些不同的成員函數有不同的參數,他們是怎么傳遞的,怎么在表格里面統一參數的信息...這些看下面的參考文獻【1】,有較為詳細的說明。

     

    4、   補充:用的框架代碼里面,最顯著的是一種模式:模板方法模式(template method pattern)。具體

    的說明請參考其他文章。

     

    這里為了補充,舉一個例子說明是怎么調用的:

    class A
    {
    public:
        void callPrint(){ print(); };
    protected:
        static void printThis(){ cout << "this is a A object!/n"; };
    private:
        virtual void print(){ printThis(); };

    };

    class B : public A
    {
    protected:
        static void printThis(){ cout << "This is a B object!/n";}
    private:
        void print(){ printThis();}
    };

    如下語句輸出什么:

     

    B  b;

    A *pa = &b;

    pa->callPrint();

     

    也許你知道輸出的是“this is a B object!/n”,但是,知道為什么嘛?參考文章【3】中有詳細的講解,會令你滿意的。

    (注意上面例子中的virtual和訪問權限,試著利用虛函數的實現機制來解釋他是怎么作用的。)

     

     

    參考文章:

     

    【1】、MFC消息分派:http://blog.csdn.net/linzhengqun/archive/2007/11/28/1905671.aspx

     

    【2】、MFC教程之消息映射的實現:http://www.vczx.com/tutorial/mfc/mfc4.php

     

    【3】、與大蝦對話:領悟設計模式:http://www.myfaq.com.cn/A200508/2005-08-07/183608.html

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全