• <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>

            大龍的博客

            常用鏈接

            統(tǒng)計(jì)

            最新評(píng)論

            消息如何流動(dòng) ----------- 注意 GetMessageMap為虛函數(shù)

             一、傳統(tǒng)SDK程序的消息循環(huán)

            在傳統(tǒng)的SDK程序中,消息循環(huán)是很簡(jiǎn)單的,也許你不信,那我們就看看下面這段代碼吧:
            #include <windows.h>
            LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
            int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
            PSTR szCmdLine, int iCmdShow)
            {
            static TCHAR szAppName[] = TEXT ("HelloWin") ;
            WNDCLAS	wndclass ;
            wndclass.style	  = CS_HREDRAW | CS_VREDRAW ;
            wndclass.lpfnWndProc  = WndProc ;
            wndclass.lpszClassName	= szAppName ;
            RegisterClass (&wndclass);
            hwnd = CreateWindow( szAppName,……,NULL);
            ShowWindow (hwnd, iCmdShow) ;
            UpdateWindow (hwnd) ;
            while (GetMessage (&msg, NULL, 0, 0))
            {
            TranslateMessage (&msg) ;
            DispatchMessage (&msg) ;
            }
            return msg.wParam ;
            }
            LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            {
            switch (message)
            {
            case WM_CREATE:
            ………
            case WM_PAINT:
            ………
            case WM_DESTROY:
            PostQuitMessage (0) ;
            return 0 ;
            }
            return DefWindowProc (hwnd, message, wParam, lParam) ;
            }
            
              在WinMain 中 CreateWindow通過(guò)一個(gè)參數(shù)將創(chuàng)建的窗口和窗口類(lèi)(見(jiàn)"窗口類(lèi)的誕生"一文)聯(lián)系起來(lái),這樣該窗口的所有消息都將發(fā)送到該窗口類(lèi)的窗口函數(shù)WndProc,其后WndProc根據(jù)不同的消息給予不同的動(dòng)作。

            二、MFC期望的消息循環(huán)
              在傳統(tǒng)的SDK程序中消息循環(huán)是非常簡(jiǎn)單的,并且將窗口和窗口函數(shù)綁定在一起。而在MFC中就出現(xiàn)了問(wèn)題,比如CDocument類(lèi),不是窗口,所以沒(méi)有窗口類(lèi),但是我也想讓它響應(yīng)消息,怎辦?問(wèn)題不僅僅如此,我們?cè)倏纯碝FC的消息,就會(huì)發(fā)現(xiàn)更多問(wèn)題。
              MFC將消息分為三大類(lèi):1.標(biāo)準(zhǔn)消息,即除WM_COMMAND之外的任何WM_開(kāi)頭的消息,任何派生自CWnd的類(lèi)都可以接受該消息,并按照繼承關(guān)系接受(如從CScrollView到CView再到CWnd)。2.命令消息,即WM_COMMAND,任何派生自CCmdTarget的類(lèi),兼可接受該消息,接受順序如下圖所示,其中標(biāo)號(hào)標(biāo)注了接受消息的順序,箭頭代表調(diào)用順序 :


            圖1 消息的拐彎流動(dòng)

            3.Control Notification,通知類(lèi)消息,也以WM_COMMAND形式出現(xiàn),由控件產(chǎn)生,通知其父窗口。

            三、消息宏背后的秘密
              知道了MFC消息流動(dòng)的要求,那MFC是怎樣實(shí)現(xiàn)的呢?當(dāng)一個(gè)消息出現(xiàn)時(shí),Application FrameWork怎么知道將該消息發(fā)送給哪個(gè)對(duì)象的呢?其實(shí)都是CCmdTarget類(lèi)在作怪,所有能夠接受消息的類(lèi)都必須繼承于CCmdTarget類(lèi),因?yàn)檫@些類(lèi)都一個(gè)共同的特征:含有DECLARE_MESSAGE_MAP、BEGIN_MESSAGE_MAP、END_MESSAGE_MAP三個(gè)宏。啊!就這三個(gè)宏組織了一張龐大的消息映射網(wǎng),也許你不信,那我們就看看這三個(gè)宏是怎樣定義的:
            #define DECLARE_MESSAGE_MAP()\
            private:\
            static const AFX_MSGMAP_ENTRY _messageEntries[];\
            protected:
            static AFX_DATA const AFX_MSGMAP messageMap;\
            virtual const AFX_MSGMAP* GetMessageMap() const;\
            #define BEGIN_MESSAGE_MAP(theClass, baseClass)\
            const AFX_MSGMAP* theClass::GetMessageMap() const\
            {return &theClass::messageMap;}\
            AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
            {&baseClass::messageMap, &theClass::_messageEntries[0]};\
            const AFX_MSGMAP_ENTRY theClass::_messageEntries[]=\
            {\
            #define END_MESSAGE_MAP()\
            {0,0,0,0,AfxSig_end,(AFX_pMSG)0}\
            };\
            typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
            struct AFX_MSGMAP_ENTRY
            {
            UINT  nMessage;
            UINT  nCode;
            UINT  nID;
            UINT  nLastID;
            UINT  nSig;
            AFX_PMSG pfn;
            };
            struct AFX_MSGMAP
            {
            const AFX_MSGMAP* pBaseMap;
            const AFX_MSGMAP_ENTRY* lpEntries;
            };
            
              可以看出DECLARE_MESSAGE_MAP宏在其類(lèi)中申請(qǐng)了一個(gè)全局結(jié)構(gòu)和獲得該結(jié)構(gòu)的函數(shù),而在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之間填寫(xiě)剛才的全局結(jié)構(gòu),將消息和對(duì)應(yīng)的處理函數(shù)聯(lián)系起來(lái),并通過(guò)AFX_MSGMAP中的pBaseMap指針,將各類(lèi)按繼承順序連接起來(lái),從而提供消息流動(dòng)的道路(即消息的直流,滿(mǎn)足標(biāo)準(zhǔn)消息流動(dòng)的要求)。

            下面我們舉個(gè)例子:
            CMyWnd : public CWnd
            {
            ……
            DECLARE_MESSAGE_MAP()
            }
            BEGIN_MESSAGE_MAP(CMyWnd,CWnd)
            ON_WM_CREATE()
            ON_WM_PAINT()
            END_MESSAGE_MAP()
            
            被展開(kāi)后,代碼如下:
            CMyWnd:public CWnd
            {
            ……
            private:
            static const AFX_MSGMAP_ENTRY _messageEntries[];
            protected:
            static AFX_DATA const AFX_MSGMAP messageMap;
            virtual const AFX_MSGMAP* GetMessageMap() const;
            }
            const AFX_MSGMAP* CMyWnd::GetMessageMap() const
            {  return &CMyWnd::messageMap;}
            AFX_DATADEF const AFX_MSGMAP CMyWnd::messageMap=
            {&CWnd::messageMap, &CMyWnd::_messageEntries[0]};
            const AFX_MSGMAP_ENTRY CMyWnd::_messageEntries[]=
            {
            {WM_CREATE,0,0,0,AfxSig_is,
            (AFX_PMSG)(AFX_PMSGW)(int(AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT))OnCreate},
            {WM_PAINT,0,0,0,AfxSig_vv,
            (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnPaint},
            {0,0,0,0,AfxSig_end,(AFX_PMSG)0}
            };
            
              這樣 WM_CREATE,WM_PAINT 在消息網(wǎng)中流動(dòng),當(dāng)流到CMyWnd類(lèi)的 messageMap 結(jié)構(gòu)時(shí),發(fā)現(xiàn)有該消息的記錄,則調(diào)用記錄中記載的 OnCreate 和 OnPaint 函數(shù),進(jìn)行響應(yīng)消息,從而完成了 Windows 消息驅(qū)動(dòng)機(jī)制。

            四、MFC消息的起點(diǎn)
              我們已經(jīng)建立了一張消息流動(dòng)網(wǎng)絡(luò),但是消息是怎樣從產(chǎn)生到響應(yīng)函數(shù)收到該消息,而且標(biāo)準(zhǔn)消息需要直流,命令消息還有許多拐彎(在標(biāo)題二中可以看到)。不要緊張,我們只需要看看MFC是怎樣實(shí)現(xiàn)的。
              不管怎么說(shuō),對(duì) Windows 系統(tǒng)來(lái)說(shuō)都是一樣的,它都是不斷地用GetMessage(或者其它)從消息隊(duì)列中取出消息,然后用DispatchMessage將消息發(fā)送到窗口函數(shù)中去。在"窗口類(lèi)的誕生"中知道,MFC將所有的窗口處理函數(shù)都注冊(cè)成DefWndProc,那是不是MFC將所有的消息都發(fā)送到DefWndProc中去了呢?很抱歉不是,而是都發(fā)送到了AfxWndProc函數(shù)去了。你可能要問(wèn)為什么,這也是我想知道的,那我們就看看MFC代碼吧:
            BOOL CWnd::CreateEx(……)
            {
            ……
            PreCreateWindow(cs);
            AfxHookWindowCreate(this);
            HWND hWnd = ::CreateWindowEx(……);
            ……
            }
            void AFXAPI AfxHookWindowCreate(CWnd *pWnd)
            {
            ……
            pThreadState->m_hHookOldCbtFilter =
            ::SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook,NULL,::GetCurrentThreadId());
            ……
            }
            _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
            {
            ……
            if(!afxData.bWin31)
            {
            _AfxStandardSubclass((HWND)wParam);
            }
            ……
            }
            void AFXAPI _AfxStandardSubclass(HWND hWnd)
            {
            ……
            oldWndProc =
            (WNDPROC)SetWindowLong(hWnd,GWL_WNDPROC,(DWORD)AfxGetAfxWndProc());
            }
            WNDPROC AFXAPI AfxGetAfxWndProc()
            {
            ……
            return &AfxWndProc;
            }
            
              看了上面的代碼,不知你有沒(méi)有了然于胸的感覺(jué)"啊,原來(lái)是這樣呀!"其實(shí)MFC在PreCreateWindow注冊(cè)窗口類(lèi)之后,在創(chuàng)建窗口之前,調(diào)用了AfxHookWindowCreate函數(shù),該函數(shù)設(shè)置了鉤子(鉤子用SetWinowsHook或者SetWindowsHookEx設(shè)置,這樣消息有滿(mǎn)足設(shè)置的消息時(shí),系統(tǒng)就發(fā)送給你設(shè)置的函數(shù),這里是_AfxCbtFilterHook函數(shù)),這樣每次創(chuàng)建窗口的時(shí)候,該函數(shù)就將窗口函數(shù)修改成AfxWndProc。至于為什么這樣做嗎?那是為了包容新的3D控件而又同MFC2.5兼容。

            五、MFC消息的流動(dòng)
              消息的起點(diǎn)是AfxWndProc函數(shù),所有的消息都被發(fā)送到AfxWndProc,也從AfxWndProc再次流向各自的消息響應(yīng)函數(shù)的,怎么流的呢?那只有MFC知道:
            LRESULT CALLBACK AfxWndProc(…….)
            {
            ……
            return AfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);
            }
            LRESULT AFXAPI AfxCallWndProc(……)
            {
            ……
            lResult = pWnd->WindowProc(nMsg,wParam,lParam);
            ……
            }
            LRESULT CWnd::WindowProc(……)
            {
            ……
            if(!OnWndMsg(message,wParam,lParam,&lResult))
            lResult = DefWindowProc(message,wParam,lParam);
            ……
            }
            BOOL CWnd::OnWndMsg(……)//該函數(shù)原來(lái)太過(guò)龐大,被我改造了一下,只反映意思,不能執(zhí)行
            {
            ……
            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))(……);//調(diào)用消息響應(yīng)函數(shù)
            }
            AFX_MSGMAP_ENTRY AfxFindMessageEntry(……)
            {
            ……
            while(lpEntry->nSign!=AfxSig_end)
            {
            if(lpEntry->nMessage==nMsg&&lpEntry->nCode==nCode&&nID>=lpEntry->nID
            &&nID<=lpEntry->nLastID)
            {
            return lpEntry;
            }
            lpEntry++;
            }
            ……
            }
            
              消息被發(fā)送到對(duì)應(yīng)窗口的OnWndMsg后,然后根據(jù)消息的類(lèi)型采取相應(yīng)動(dòng)作:如果是標(biāo)準(zhǔn)消息,則檢查但前類(lèi)中有無(wú)處理函數(shù)(由AfxFindMessageEntry實(shí)現(xiàn)),若沒(méi)有,就在其父親類(lèi)中找(通過(guò)pMessageMap->pBaseMap實(shí)現(xiàn)),這樣望上順序搜索消息網(wǎng),搜索結(jié)束也找不到處理函數(shù),那么回到WindowProc函數(shù)調(diào)用默認(rèn)DefWindowProc函數(shù);如果是命令消息或通知消息則發(fā)送到OnCommand或者OnNotify函數(shù)中去處理,來(lái)實(shí)現(xiàn)消息的拐彎流動(dòng):
            BOOL CWnd::OnCommand(WPARAM wParam,LPARAM lParam)
            {
            ……
            return OnCmdMsg(nID,nCode,NULL,NULL);
            }
            BOOL CFrameWnd::OnCmdMsg(……)
            {
            CView* pView = GetActiveView();
            if(pView!=NULL&&pView->OnCmdMsg(……))   //相當(dāng)于圖1中Frame指向View的箭頭
            return TRUE;
            if(CWnd::OnCmdMsg(……))                    //圖1中Frame自身
            return TRUE;
            CWinApp *pApp = AfxGetApp();
            if(pApp != NULL && pApp->OnCmdMsg(……)) //圖1中CWinApp對(duì)象
            return TRUE;
            return FALSE;
            }
            BOOL CView::OnCmdMsg(……)
            {
            if(CWnd::OnCmdMsg(……))                    //圖1中View本身
            return TRUE;
            if(m_pDocument!=NULL) m_pDocument->OnCmdMsg(……);//圖1中View到Doc箭頭
            ……
            }
            BOOL CDocument::OnCmdMsg(……)
            {
            if(CCmdTarget::OnCmdMsg(……))              //圖1中Doc本身
            return TRUE;
            if(m_pDocTemplate!=NULL&&m_pDocTemplate->OnCmdMsg(……))//圖1中Doc Template
            return TRUE;
            return FALSE;
            }
            BOOL CCmdTarget::OnCmdMsg(……)//注:CWnd沒(méi)有重載CCmdTarget的OnCmdMsg
            {
            ……
            for(pMessageMap=GetMessageMap();pMessageMap!=NULL;
            pMessageMap=pMessageMap->pBaseMap)
            {
            lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,……);
            if(lpEntry!=NULL)
            return DispatchCmdMsg(……lpEntry->pfn,……);
            }
            return FALSE;
            }
            
              從代碼中可以看出,OnCmdMsg各自調(diào)用的順序剛好就是圖1中所要求的順序,這樣也就實(shí)現(xiàn)了消息的拐彎流動(dòng),最后DispatchCmdMsg 函數(shù)是調(diào)用找到的消息處理函數(shù)處理消息。至此消息從出現(xiàn)到找到處理函數(shù)已經(jīng)完成!

            posted on 2007-09-18 02:57 大龍 閱讀(2894) 評(píng)論(0)  編輯 收藏 引用


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


            中文字幕无码精品亚洲资源网久久| 久久综合狠狠综合久久| 一级做a爰片久久毛片人呢| 久久99精品国产麻豆宅宅| 天天综合久久久网| 久久乐国产综合亚洲精品| 久久99精品久久只有精品| 久久夜色精品国产噜噜亚洲AV| 99久久超碰中文字幕伊人| 久久国产热这里只有精品| 少妇高潮惨叫久久久久久 | 国产精品久久久天天影视| 人妻中文久久久久| 久久这里只有精品18| 无码精品久久一区二区三区| 国产精品一区二区久久精品| 久久久久久久久波多野高潮| 国产L精品国产亚洲区久久| 午夜精品久久久久久久| 亚洲欧美国产精品专区久久| 精品人妻伦一二三区久久| 久久99精品久久久久久动态图| 蜜臀久久99精品久久久久久| 久久精品无码一区二区三区| 色综合久久无码中文字幕| 伊人色综合久久天天人守人婷| 久久九九有精品国产23百花影院| 久久久精品人妻一区二区三区蜜桃| 久久有码中文字幕| 久久不见久久见免费影院www日本| 亚洲伊人久久大香线蕉苏妲己| WWW婷婷AV久久久影片| 久久婷婷五月综合色高清| 中文字幕乱码久久午夜| 日产精品久久久久久久| 久久久久无码精品国产| 久久国产精品99精品国产| 精品无码久久久久久尤物| 91精品国产综合久久精品| 久久免费精品视频| 久久精品99无色码中文字幕|