• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            笑看風(fēng)云淡

            寵辱不驚,看庭前花開花落;去留無意,望天空云卷云舒
            posts - 96, comments - 48, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 ::  :: 聚合  :: 管理

            一.引言

            VC++的MFC類庫實際上是Windows下C++編程的一套最為流行的類庫。MFC的框架結(jié)構(gòu)大大方便了程序員的編程工作,但是為了更加有效、靈活的使用MFC編程,了解MFC的體系結(jié)構(gòu)往往可以使編程工作事半功倍。它合理的封裝了WIN32 API函數(shù),并設(shè)計了一套方便的消息映射機(jī)制。但這套機(jī)制本身比較龐大和復(fù)雜,對它的分析和了解無疑有助于我們寫出更為合理的高效的程序。這里我們簡單的分析MFC的消息響應(yīng)機(jī)制,以了解MFC是如何對Windows的消息加以封裝,方便用戶的開發(fā)。

            二.SDK下的消息機(jī)制實現(xiàn)

            這里簡單的回顧一下SDK下我們是如何進(jìn)行Windows的程序開發(fā)的。一般來說,Windows的消息都是和線程相對應(yīng)的。即Windows會把消息發(fā)送給和該消息相對應(yīng)的線程。在SDK的模式下,程序是通過GetMessage函數(shù)從和某個線程相對應(yīng)的消息隊列里面把消息取出來并放到一個特殊的結(jié)構(gòu)里面,一個消息的結(jié)構(gòu)是一個如下的STRUCTURE。

            typedef struct tagMSG {

            HWND   hwnd;

            UINT   message;

            WPARAM wParam;

            LPARAM lParam;

            DWORD  time;

            POINT  pt;

            }MSG;

            其中hwnd表示和窗口過程相關(guān)的窗口的句柄,message表示消息的ID號,wParam和lParam表示和消息相關(guān)的參數(shù),time表示消息發(fā)送的時間,pt表示消息發(fā)送時的鼠標(biāo)的位置。

            然后TranslateMessage函數(shù)用來把虛鍵消息翻譯成字符消息并放到響應(yīng)的消息隊列里面,最后DispatchMessage函數(shù)把消息分發(fā)到相關(guān)的窗口過程。然后窗口過程根據(jù)消息的類型對不同的消息進(jìn)行相關(guān)的處理。在SDK編程過程中,用戶需要在窗口過程中分析消息的類型和跟消息一起的參數(shù)的含義,做不同的處理,相對比較麻煩,而MFC把消息調(diào)用的過程給封裝起來,使用戶能夠通過ClassWizard方便的使用和處理Windows的各種消息。

            三.MFC的消息實現(xiàn)機(jī)制

            我們可以看到,在MFC的框架結(jié)構(gòu)下,可以進(jìn)行消息處理的類的頭文件里面都會含有DECLARE_MESSAGE_MAP()宏,這里主要進(jìn)行消息映射和消息處理函數(shù)的聲明。可以進(jìn)行消息處理的類的實現(xiàn)文件里一般都含有如下的結(jié)構(gòu)。

            BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass)

            //{{AFX_MSG_MAP(CInheritClass)

            //}}AFX_MSG_MAP

            END_MESSAGE_MAP()

            這里主要進(jìn)行消息映射的實現(xiàn)和消息處理函數(shù)的實現(xiàn)。 

            所有能夠進(jìn)行消息處理的類都是基于CCmdTarget類的,也就是說CCmdTarget類是所有可以進(jìn)行消息處理類的父類。CCmdTarget類是MFC處理命令消息的基礎(chǔ)和核心。

            同時MFC定義了下面的兩個主要結(jié)構(gòu):

            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 nSig;       

            // signature type (action) or pointer to message #

            AFX_PMSG pfn;    // routine to call (or special value)

            };

            和AFX_MSGMAP

            struct AFX_MSGMAP

            {

            #ifdef _AFXDLL

            const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();

            #else

            const AFX_MSGMAP* pBaseMap;

            #endif

            const AFX_MSGMAP_ENTRY* lpEntries;

            };

            其中AFX_MSGMAP_ENTRY結(jié)構(gòu)包含了

            一個消息的所有相關(guān)信息,其中

            nMessage為Windows消息的ID號

            nCode為控制消息的通知碼

            nID為Windows控制消息的ID

            nLastID表示如果是一個指定范圍的消息被映射的話,

            nLastID用來表示它的范圍。

            nSig表示消息的動作標(biāo)識

            AFX_PMSG pfn 它實際上是一個指向

            和該消息相應(yīng)的執(zhí)行函數(shù)的指針。

            而AFX_MSGMAP主要作用是兩個,一:用來得到基類的消息映射入口地址。二:得到本身的消息映射入口地址。

            實際上,MFC把所有的消息一條條填入到AFX_MSGMAP_ENTRY結(jié)構(gòu)中去,形成一個數(shù)組,該數(shù)組存放了所有的消息和與它們相關(guān)的參數(shù)。同時通過AFX_MSGMAP能得到該數(shù)組的首地址,同時得到基類的消息映射入口地址,這是為了當(dāng)本身對該消息不響應(yīng)的時候,就調(diào)用其基類的消息響應(yīng)。

            現(xiàn)在我們來分析MFC是如何讓窗口過程來處理消息的,實際上所有MFC的窗口類都通過鉤子函數(shù)_AfxCbtFilterHook截獲消息,并且在鉤子函數(shù)_AfxCbtFilterHook中把窗口過程設(shè)定為AfxWndProc。原來的窗口過程保存在成員變量m_pfnSuper中。

            所以在MFC框架下,一般一個消息的處理過程是這樣的。

            函數(shù)AfxWndProc接收Windows操作系統(tǒng)發(fā)送的消息。

            函數(shù)AfxWndProc調(diào)用函數(shù)AfxCallWndProc進(jìn)行消息處理,這里一個進(jìn)步是把對句柄的操作轉(zhuǎn)換成對CWnd對象的操作。

            函數(shù)AfxCallWndProc調(diào)用CWnd類的方法WindowProc進(jìn)行消息處理。注意AfxWndProc和AfxCallWndProc都是AFX的API函數(shù)。而WindowProc已經(jīng)是CWnd的一個方法。所以可以注意到在WindowProc中已經(jīng)沒有關(guān)于句柄或者是CWnd的參數(shù)了。

            方法WindowProc調(diào)用方法OnWndMsg進(jìn)行正式的消息處理,即把消息派送到相關(guān)的方法中去處理。消息是如何派送的呢?實際上在CWnd類中都保存了一個AFX_MSGMAP的結(jié)構(gòu),而在AFX_MSGMAP結(jié)構(gòu)中保存有所有我們用ClassWizard生成的消息的數(shù)組的入口,我們把傳給OnWndMsg的message和數(shù)組中的所有的message進(jìn)行比較,找到匹配的那一個消息。實際上系統(tǒng)是通過函數(shù)AfxFindMessageEntry來實現(xiàn)的。找到了那個message,實際上我們就得到一個AFX_MSGMAP_ENTRY結(jié)構(gòu),而我們在上面已經(jīng)提到AFX_MSGMAP_ENTRY保存了和該消息相關(guān)的所有信息,其中主要的是消息的動作標(biāo)識和跟消息相關(guān)的執(zhí)行函數(shù)。然后我們就可以根據(jù)消息的動作標(biāo)識調(diào)用相關(guān)的執(zhí)行函數(shù),而這個執(zhí)行函數(shù)實際上就是通過ClassWizard在類實現(xiàn)中定義的一個方法。這樣就把消息的處理轉(zhuǎn)化到類中的一個方法的實現(xiàn)上。舉一個簡單的例子,比如在View中對WM_LButtonDown消息的處理就轉(zhuǎn)化成對如下一個方法的操作。

            void CInheritView::OnLButtonDown

            (UINT nFlags, CPoint point)

            {

            // TODO: Add your message

            handler code here and/or call default

            CView::OnLButtonDown(nFlags, point);

            }

            注意這里CView::OnLButtonDown(nFlags, point)實際上就是調(diào)用CWnd的Default()方法。 而Default()方法所做的工作就是調(diào)用DefWindowProc對消息進(jìn)行處理。這實際上是調(diào)用原來的窗口過程進(jìn)行缺省的消息處理。

            如果OnWndMsg方法沒有對消息進(jìn)行處理的話,就調(diào)用DefWindowProc對消息進(jìn)行處理。這是實際上是調(diào)用原來的窗口過程進(jìn)行缺省的消息處理。 

            所以如果正常的消息處理的話,MFC窗口類是完全脫離了原來的窗口過程,用自己的一套體系結(jié)構(gòu)實現(xiàn)消息的映射和處理。即先調(diào)用MFC窗口類掛上去的窗口過程,再調(diào)用原先的窗口過程。并且用戶面對和消息相關(guān)的參數(shù)不再是死板的wParam和lParam,而是和消息類型具體相關(guān)的參數(shù)。比如和消息WM_LbuttonDown相對應(yīng)的方法OnLButtonDown的兩個參數(shù)是nFlags和point。nFlags表示在按下鼠標(biāo)左鍵的時候是否有其他虛鍵按下,point更簡單,就是表示鼠標(biāo)的位置。

            同時MFC窗口類消息傳遞中還提供了兩個函數(shù),分別為WalkPreTranslateTree和PreTranslateMessage。我們知道利用MFC框架生成的程序,都是從CWinApp開始執(zhí)行的,而CWinapp實際繼承了CWinThread類。在CWinThread的運行過程中會調(diào)用窗口類中的WalkPreTranslateTree方法。而WalkPreTranslateTree方法實際上就是從當(dāng)前窗口開始查找愿意進(jìn)行消息翻譯的類,直到找到窗口沒有父類為止。在WalkPreTranslateTree方法中調(diào)用了PreTranslateMessage方法。實際上PreTranslateMessage最大的好處是我們在消息處理前可以在這個方法里面先做一些事情。舉一個簡單的例子,比如我們希望在一個CEdit對象里,把所有的輸入的字母都以大寫的形式出現(xiàn)。我們只需要在PreTranslateMessage方法中判斷message是否為WM_CHAR,如果是的話,把wParam(表示鍵值)由小寫字母的值該為大寫字母的值就實現(xiàn)了這個功能。

            繼續(xù)上面的例子,根據(jù)我們對MFC消息機(jī)制的分析,我們很容易得到除了上面的方法,我們至少還可以在另外兩個地方進(jìn)行操作。 

            一,在消息的處理方法里面即OnChar中,當(dāng)然最后我們不再調(diào)用CEdit::OnChar(nChar, nRepCnt, nFlags),而是直接調(diào)用DefWindowProc(WM_CHAR,nChar,MAKELPARAM (nRepCnt,nFlags))。因為從我們上面的分析可以知道CEdit::OnChar(nChar, nRepCnt, nFlags)實際上也就是對DefWindowProc方法的調(diào)用。 

            二,我們可以直接重載DefWindowProc方法,對message類型等于WM_CHAR的,直接修改nChar的值即可。

            四.小結(jié)

            通過對MFC類庫的分析和了解,不僅能夠使我們更好的使用MFC類庫,同時,對于我們自己設(shè)計和實現(xiàn)框架和類,無疑也有相當(dāng)大的幫助。


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久久久亚洲AV成人片 | 国产精品久久免费| 久久精品国产亚洲一区二区| AA级片免费看视频久久| 深夜久久AAAAA级毛片免费看 | 色偷偷久久一区二区三区| 久久99国产精品久久99果冻传媒| 久久99精品久久久久久不卡| 午夜天堂精品久久久久| 99久久国产免费福利| 久久夜色精品国产噜噜噜亚洲AV | 久久e热在这里只有国产中文精品99| 日本五月天婷久久网站| 久久99精品久久久久久水蜜桃 | 日日狠狠久久偷偷色综合免费| 91精品国产综合久久婷婷| 久久久久亚洲精品日久生情| 国内精品久久久久国产盗摄| 国产精品99久久免费观看| 久久精品国产精品亚洲精品| 伊人 久久 精品| 久久久久18| 97精品国产97久久久久久免费| 少妇精品久久久一区二区三区| 亚洲日本va午夜中文字幕久久| 国产精品成人精品久久久| 99久久国产亚洲高清观看2024 | 久久久久99精品成人片三人毛片 | 久久综合亚洲色HEZYO国产| 国产精品视频久久久| 国产午夜免费高清久久影院| 中文字幕无码精品亚洲资源网久久| 久久久这里只有精品加勒比| 久久人妻少妇嫩草AV蜜桃| 国产成人无码精品久久久久免费 | 99久久综合国产精品免费| 2021国产精品久久精品| 久久精品国产乱子伦| 久久久久亚洲AV无码网站| 久久成人影院精品777| 日本精品久久久久中文字幕|