在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 深入剖析MFC中Windows消息處理機(jī)制

            深入剖析MFC中Windows消息處理機(jī)制

            作者: 時(shí)間:2007-05-11 來(lái)源:網(wǎng)絡(luò) 收藏
              Windows程序和DOS程序的主要不同點(diǎn)之一是:Windows程序是以事件為驅(qū)動(dòng)、為基礎(chǔ)

              本人對(duì)Windows系統(tǒng)、MFC談不上有深入的了解,但對(duì)MFC本身包裝API的機(jī)制很有興趣,特別是讀了候老師的《深入淺出MFC》后,感覺(jué)到Visual C++的Application FrameWork十分精制。在以前,我對(duì)SDI結(jié)構(gòu)處理消息有一定的認(rèn)識(shí),但對(duì)于模式對(duì)話框的不了解,讀了《深入》一書也沒(méi)能得到解決,近日,通過(guò)在網(wǎng)友的幫助和查閱MSDN,自認(rèn)為已經(jīng)了解。一時(shí)興起,寫下這些文字,沒(méi)有其它目的,只是希望讓后來(lái)者少走彎路,也希望和我一樣 喜歡“鉆牛角尖”的人共同討論、學(xué)習(xí)。如果你是牛人,那么你現(xiàn)在要慎重考慮有沒(méi)有充足的時(shí)間讀這些幼稚文字。

              正文:

              Windows程序和DOS程序的主要不同點(diǎn)之一是:Windows程序是以事件為驅(qū)動(dòng)、為基礎(chǔ)。如何理解?

              舉了例子,當(dāng)你CLICK Windows “開(kāi)始”BUTTON時(shí),為什么就會(huì)彈出一個(gè)菜單呢?

              當(dāng)你單擊鼠標(biāo)左鍵時(shí),操作系統(tǒng)中與MOUSE相關(guān)的驅(qū)動(dòng)程序在第一時(shí)間內(nèi)得到這個(gè)信號(hào)[LBUTTONDOWN],然后它通知操作系統(tǒng)―――“嗨,鼠標(biāo)左鍵被單擊了!”,操作系統(tǒng)得到這一信號(hào)后,馬上要判斷――用戶單擊鼠標(biāo)左鍵,這是針對(duì)哪個(gè)窗口呢?如何判斷?這很簡(jiǎn)單!當(dāng)前狀態(tài)中,具有焦點(diǎn)的窗口[或控件]就是了[這里當(dāng)然是“開(kāi)始”BUTTON了]。然后操作系統(tǒng)馬上向這個(gè)窗口發(fā)送一條消息到這個(gè)窗口所在進(jìn)程的消息隊(duì)列,消息內(nèi)容應(yīng)是消息本身的代號(hào)、附加參數(shù)、窗口句柄…等等了。那么,只有操作系統(tǒng)才有資格發(fā)送消息至某一窗口的消息隊(duì)列嗎?不然,其它程序也有資格。你可以在你的程序中調(diào)用:SendMessage、PostMessage。這樣,被單擊的窗口得到了一條由操作系統(tǒng)發(fā)送的包含CLICK的消息,操作系統(tǒng)已經(jīng)暫時(shí)不再管窗口的任何事,因?yàn)樗€要忙于處理其它事務(wù)。你的程序得到一條消息后如何做呢?Windows對(duì)于你在“開(kāi)始”BUTTON上的單擊事件做出如下反映:彈出一菜單??墒牵玫较⒌阶龀龇从尺@一過(guò)程是如何實(shí)現(xiàn)的呢?這就是本文討論的主要內(nèi)容[當(dāng)然只是針對(duì)MFC了]。

              我首先簡(jiǎn)要談一下SDI,然后會(huì)花更多文字描述模式對(duì)話框。

              對(duì)于SDI窗口,你的應(yīng)用程序類的InitInstance()大約如下:

            BOOL CEx06aApp::InitInstance()
            {
              ……………
             CSingleDocTemplate* pDocTemplate;
             pDocTemplate = new CSingleDocTemplate(
              IDR_MAINFRAME,
              RUNTIME_CLASS(CEx06aDoc),
              RUNTIME_CLASS(CMainFrame), // main SDI frame window
              RUNTIME_CLASS(CEx06aView));
             AddDocTemplate(pDocTemplate);
             CCommandLineInfo cmdInfo;
             ParseCommandLine(cmdInfo);
             if (!ProcessShellCommand(cmdInfo))
              return FALSE;
             m_pMainWnd->ShowWindow(SW_SHOW);
             m_pMainWnd->UpdateWindow();
             return TRUE;
            }

              完成一些如動(dòng)態(tài)生成相關(guān)文檔、視,顯示主框架窗口、處理參數(shù)行信息等工作。這些都是顯示在你工程中的“明碼”。我們現(xiàn)在把斷點(diǎn)設(shè)置到return TRUE;一句,跟入MFC源碼中,看看到底MFC內(nèi)部做了什么。

              程序進(jìn)入SRCWinMain.cpp,下一個(gè)大動(dòng)作應(yīng)是:

            nReturnCode = pThread->Run();

              注意了,重點(diǎn)來(lái)了。F11進(jìn)入

            int CWinApp::Run()
            {
             if (m_pMainWnd == NULL AfxOleGetUserCtrl())
             {
              // Not launched /Embedding or /Automation, but has no main window!
              TRACE0(Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application. );
              AfxPostQuitMessage(0);
             }
             return CWinThread::Run();
            }

              再次F11進(jìn)入:

            int CWinThread::Run()
            {
             ASSERT_VALID(this);

             // for tracking the idle time state
             BOOL bIdle = TRUE;
             LONG lIdleCount = 0;

             // acquire and dispatch messages until a WM_QUIT message is received.
             for (;;)
             {
              // phase1: check to see if we can do idle work
              while (bIdle !::PeekMessage(m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
              {
               // call OnIdle while in bIdle state
               if (!OnIdle(lIdleCount++))
               bIdle = FALSE; // assume no idle state
              }

              // phase2: pump messages while available
              do
              {
               // pump message, but quit on WM_QUIT
               if (!PumpMessage())
                return ExitInstance();

               // reset no idle state after pumping normal message
               if (IsIdleMessage(m_msgCur))
               {
                bIdle = TRUE;
                lIdleCount = 0;
               }

              } while (::PeekMessage(m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
             }

             ASSERT(FALSE); // not reachable
            }

            BOOL CWinThread::IsIdleMessage(MSG* pMsg)
            {
             // Return FALSE if the message just dispatched should _not_
             // cause OnIdle to be run. Messages which do not usually
             // affect the state of the user interface and happen very
             // often are checked for.

             // redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE
             if (pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE)
             {
              // mouse move at same position as last mouse move?
              if (m_ptCursorLast == pMsg->pt pMsg->message == m_nMsgLast)
               return FALSE;

              m_ptCursorLast = pMsg->pt; // remember for next time
              m_nMsgLast = pMsg->message;
              return TRUE;
             }

             // WM_PAINT and WM_SYSTIMER (caret blink)
             return pMsg->message != WM_PAINT pMsg->message != 0x0118;
            }

              這是SDI處理消息的中心機(jī)構(gòu),但請(qǐng)注意,它覺(jué)對(duì)不是核心!

              分析一下,在無(wú)限循環(huán)FOR內(nèi)部又出現(xiàn)一個(gè)WHILE循環(huán)

            while (bIdle
            !::PeekMessage(m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
            {
             // call OnIdle while in bIdle state
             if (!OnIdle(lIdleCount++))
              bIdle = FALSE; // assume no idle state
            }

              這段代碼是當(dāng)你程序進(jìn)程的消息隊(duì)列中沒(méi)有消息時(shí),會(huì)調(diào)用OnIdle做一些后備工作,臨時(shí)對(duì)象在這里被刪除。當(dāng)然它是虛函數(shù)。其中的PeekMessage,是查看消息隊(duì)列,如果有消息返回TRUE,如果沒(méi)有消息返回FALSE,這里指定PM_NOREMOVE,是指查看過(guò)后不移走消息隊(duì)列中剛剛被查看
            到的消息,也就是說(shuō)這里的PeekMessage只起到一個(gè)檢測(cè)作用,顯然返回FALSE時(shí)[即沒(méi)有消息],才會(huì)進(jìn)入循環(huán)內(nèi)部,執(zhí)行OnIdle,當(dāng)然了,你的OnIdle返回FLASE,會(huì)讓程序不再執(zhí)行OnIdle。你可能要問(wèn):

            當(dāng)bidle=0或消息隊(duì)例中有消息時(shí),程序又執(zhí)行到哪了呢?

            do
            {
             // pump message, but quit on WM_QUIT
             if (!PumpMessage())
              return ExitInstance();

             // reset no idle state after pumping normal message
             if (IsIdleMessage(m_msgCur))
             {
              bIdle = TRUE;
              lIdleCount = 0;
             }

            } while (::PeekMessage(m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

              看啊,又進(jìn)入一個(gè)循環(huán)!

              其中有個(gè)重要的函數(shù),PumpMessage,內(nèi)容如下:

            BOOL CWinThread::PumpMessage()
            {
             ASSERT_VALID(this);

             if (!::GetMessage(m_msgCur, NULL, NULL, NULL))
             {
              #ifdef _DEBUG
              if (afxTraceFlags traceAppMsg)
               TRACE0(CWinThread::PumpMessage - Received WM_QUIT. );
               m_nDisablePumpCount++; // application must die
               // Note: prevents calling message loop things in ’ExitInstance’
               // will never be decremented
              #endif
              return FALSE;
             }

             #ifdef _DEBUG
             if (m_nDisablePumpCount != 0)
             {
              TRACE0(Error: CWinThread::PumpMessage called when not permitted. );
              ASSERT(FALSE);
             }
             #endif

             #ifdef _DEBUG
             if (afxTraceFlags traceAppMsg)
              _AfxTraceMsg(_T(PumpMessage), m_msgCur);
             #endif

             // process this message

             if (m_msgCur.message != WM_KICKIDLE !PreTranslateMessage(m_msgCur))
             {
              ::TranslateMessage(m_msgCur);
              ::DispatchMessage(m_msgCur);
             }
             return TRUE;
            }

              如你所想,這才是MFC消息處理的核心基地[也是我個(gè)人認(rèn)為的]。

              GetMessage不同于PeekMessae,它是不得到消息不罷體,PeekMessage如果發(fā)現(xiàn)消息隊(duì)列中沒(méi)有消息會(huì)返回0,而GetMessage如果發(fā)現(xiàn)沒(méi)有消息,等,直到有了消息,而且,GetMessage不同于PeekMessage,它會(huì)將消息移走[當(dāng)然,PeekMessage也可以做到這點(diǎn)]。我想當(dāng)你讀了這個(gè)函數(shù)后,
            你應(yīng)明白PreTranslateMessage函數(shù)的用法了吧[我比較喜歡在程序中充分利用這個(gè)函數(shù)]。

            ::TranslateMessage(m_msgCur);
            ::DispatchMessage(m_msgCur);

              將消息發(fā)送到窗口的處理函數(shù)[它是由窗口類指定的],之后的動(dòng)作一直到你的程序做出反映的過(guò)程,你可以在《深入》一書中得到完美的解釋。我們還是通過(guò)reurn
            TRUE;回到CWinThread::Run()中的Do{}while;循環(huán)。然后還是對(duì)IDLE的處理,即便剛才你的ONIDLE返回了FALSE,在這里你看到,你的程序還是有機(jī)會(huì)執(zhí)行它的。然后又是利用PeekMessage檢測(cè)消息隊(duì)列:

              如果有消息[這個(gè)消息不被移動(dòng)的原因是因?yàn)樗獮镻umpMessage內(nèi)的GetMessage所利用。]再次進(jìn)入PumpMessage[叫它“消息泵”吧]。

              如果沒(méi)有消息,退出DO循環(huán),但它還在FOR內(nèi)部,所以又執(zhí)行第一個(gè)While循環(huán)。

              這是CwinThread::Run的一個(gè)執(zhí)行過(guò)程。

              不用擔(dān)心退不出for(;;)如果你的消息隊(duì)列中有一條WM_QUIT,會(huì)使GetMessage返回0,然后PumpMessage返回0而RUN()內(nèi)部:

            if (!PumpMessage())
            return ExitInstance();

              SDI就說(shuō)到這,下面我來(lái)談一下模式對(duì)話框。我分2種情況討論:

              一當(dāng)你的工程以模式對(duì)話框?yàn)榛A(chǔ)時(shí)[沒(méi)父窗口,或?yàn)樽烂妫荨?

              與SDI不同處在于,在應(yīng)用程序類的InItInstance內(nèi)部:

            BOOL CComboBoxApp::InitInstance()
            {
             AfxEnableControlContainer();
             // Standard initialization
             // If you are not using these features and wish to reduce the size
             // of your final executable, you should remove from the following
             // the specific initialization routines you do not need.
             #ifdef _AFXDLL
              Enable3dControls(); // Call this when using MFC in a shared DLL
             #else
              Enable3dControlsStatic(); // Call this when linking to MFC statically
             #endif
             this->m_nCmdShow = SW_HIDE;
             CComboBoxDlg dlg;
             m_pMainWnd = dlg;
             int nResponse = dlg.DoModal();
             if (nResponse == IDOK)
             {
              // TODO: Place code here to handle when the dialog is
              // dismissed with OK
             }
             else if (nResponse == IDCANCEL)
             {
              // TODO: Place code here to handle when the dialog is
              // dismissed with Cancel
             }
             // Since the dialog has been closed, return FALSE so that we exit the
             // application, rather than start the application’s message pump.
             return FALSE;
            }

              int nResponse = dlg.DoModal();一句使你的整個(gè)程序都在DoModal()內(nèi)部進(jìn)行。而且,你退出DoMal()時(shí)[你一定結(jié)束了你的對(duì)話框],InitInstance返回的是False,我們知道,這樣,CwinThread::Run是不會(huì)執(zhí)行的。

              但對(duì)話框程序是在哪里進(jìn)行消息處理的呢。

              原來(lái),dlg.DoModal()內(nèi)部會(huì)調(diào)用CwinThread::RunModalLoop,它起到的作用和RUN()是一樣的[當(dāng)然內(nèi)部有細(xì)小差別,請(qǐng)參考MSDN]!!!

              第二種情況,你是在SDI[或其它]程序中調(diào)用Dlg.DoModal() 產(chǎn)生了一模式對(duì)話框[有父窗口].

              這又是如何運(yùn)作的呢?

              建了這樣一個(gè)工程做為例子。

              SDI,在View中處理LBUTTONDOWN:

            MyDLg.DoModal();

              MyDLg內(nèi)有按鈕,以憊后用.

              沒(méi)有顯示模式對(duì)話框前,消息處理一直在Cthread::Run()中進(jìn)行.

              你單擊后,程序執(zhí)行點(diǎn)進(jìn)入DoModal()內(nèi)部的RunModalLoop,這又是一個(gè)消息處理機(jī)制.

              不過(guò)DoModal()中調(diào)用RunModalLoop,前會(huì)Disable掉它的父窗口.從RunModalLoop中出來(lái)后,再 Enable它.

              模式對(duì)話框和非模式對(duì)話框都是通過(guò)調(diào)用CreateDialogIndirect()產(chǎn)生創(chuàng)建對(duì)話框.那它和非模式對(duì)話框區(qū)別是什么造成的呢?

              1 模式對(duì)話框?qū)⒏复翱贒ISABLE掉.

              我原以為被Disable的窗口是不接收消息的.但后來(lái)我馬上發(fā)現(xiàn)我是錯(cuò)的.但,為什么你對(duì)被Disable的窗口進(jìn)行KeyBorad,Mouse動(dòng)作時(shí),窗口沒(méi)反映呢,我想,這可能是操作系統(tǒng)從中搞的鬼.我在本文一開(kāi)始,就寫出操作系統(tǒng)向窗口發(fā)送消息的過(guò)程,我想當(dāng)它發(fā)現(xiàn)目標(biāo)窗口處理Disabled狀態(tài)時(shí),
            不會(huì)將消息發(fā)送給它,但這不能說(shuō)窗口不接收消息,其它程序[或它本身]發(fā)送給它的消息還是可以接收并處理的.

              2 模式對(duì)話框本身有消息處理機(jī)制 RunModalLoop.

              對(duì)以上兩點(diǎn)加以實(shí)驗(yàn).

              我在我的剛才建的項(xiàng)目中的模式對(duì)話框中加上一個(gè)BUTTON,其中加入如下代碼:

            OnButton1()
            {
             GetParaent()->EnableWindow(1);
            }

              單擊,后我們發(fā)現(xiàn),此時(shí)它已經(jīng)不再表現(xiàn)為”模態(tài)”,我試著點(diǎn)擊菜單,還是會(huì)作出正常反映.

              我想這此消息[對(duì)于父窗口的,如:菜單動(dòng)作]的處理也應(yīng)是在模式對(duì)話框中的RunModalLoop中進(jìn)行處理的吧[這點(diǎn)我不能確定].
            Windows程序和DOS程序的主要不同點(diǎn)之一是:Windows程序是以事件為驅(qū)動(dòng)、消息機(jī)制為基礎(chǔ)

              本人對(duì)Windows系統(tǒng)、MFC談不上有深入的了解,但對(duì)MFC本身包裝API的機(jī)制很有興趣,特別是讀了候老師的《深入淺出MFC》后,感覺(jué)到Visual C++的Application
            FrameWork十分精制。在以前,我對(duì)SDI結(jié)構(gòu)處理消息有一定的認(rèn)識(shí),但對(duì)于模式對(duì)話框的消息機(jī)制不了解,讀了《深入》一書也沒(méi)能得到解決,近日,通過(guò)在網(wǎng)友的幫助和查閱MSDN,自認(rèn)為已經(jīng)了解。一時(shí)興起,寫下這些文字,沒(méi)有其它目的,只是希望讓后來(lái)者少走彎路,也希望和我一樣
            喜歡“鉆牛角尖”的人共同討論、學(xué)習(xí)。如果你是牛人,那么你現(xiàn)在要慎重考慮有沒(méi)有充足的時(shí)間讀這些幼稚文字。

              正文:

              Windows程序和DOS程序的主要不同點(diǎn)之一是:Windows程序是以事件為驅(qū)動(dòng)、消息機(jī)制為基礎(chǔ)。如何理解?

              舉了例子,當(dāng)你CLICK Windows “開(kāi)始”BUTTON時(shí),為什么就會(huì)彈出一個(gè)菜單呢?

              當(dāng)你單擊鼠標(biāo)左鍵時(shí),操作系統(tǒng)中與MOUSE相關(guān)的驅(qū)動(dòng)程序在第一時(shí)間內(nèi)得到這個(gè)信號(hào)[LBUTTONDOWN],然后它通知操作系統(tǒng)―――“嗨,鼠標(biāo)左鍵被單擊了!”,操作系統(tǒng)得到這一信號(hào)后,馬上要判斷――用戶單擊鼠標(biāo)左鍵,這是針對(duì)哪個(gè)窗口呢?如何判斷?這很簡(jiǎn)單!當(dāng)前狀態(tài)中
            ,具有焦點(diǎn)的窗口[或控件]就是了[這里當(dāng)然是“開(kāi)始”BUTTON了]。然后操作系統(tǒng)馬上向這個(gè)窗口發(fā)送一條消息到這個(gè)窗口所在進(jìn)程的消息隊(duì)列,消息內(nèi)容應(yīng)是消息本身的代號(hào)、附加參數(shù)、窗口句柄…等等了。那么,只有操作系統(tǒng)才有資格發(fā)送消息至某一窗口的消息隊(duì)列嗎?不然,其
            它程序也有資格。你可以在你的程序中調(diào)用:SendMessage
            、PostMessage。這樣,被單擊的窗口得到了一條由操作系統(tǒng)發(fā)送的包含CLICK的消息,操作系統(tǒng)已經(jīng)暫時(shí)不再管窗口的任何事,因?yàn)樗€要忙于處理其它事務(wù)。你的程序得到一條消息后如何做呢?Windows對(duì)于你在“開(kāi)始”BUTTON上的單擊事件做出如下反映:彈出一菜單??墒?,得到消息
            到做出反映這一過(guò)程是如何實(shí)現(xiàn)的呢?這就是本文討論的主要內(nèi)容[當(dāng)然只是針對(duì)MFC了]。

              我首先簡(jiǎn)要談一下SDI,然后會(huì)花更多文字描述模式對(duì)話框。

              對(duì)于SDI窗口,你的應(yīng)用程序類的InitInstance()大約如下:

            BOOL CEx06aApp::InitInstance()
            {
              ……………
             CSingleDocTemplate* pDocTemplate;
             pDocTemplate = new CSingleDocTemplate(
              IDR_MAINFRAME,
              RUNTIME_CLASS(CEx06aDoc),
              RUNTIME_CLASS(CMainFrame), // main SDI frame window
              RUNTIME_CLASS(CEx06aView));
             AddDocTemplate(pDocTemplate);
             CCommandLineInfo cmdInfo;
             ParseCommandLine(cmdInfo);
             if (!ProcessShellCommand(cmdInfo))
              return FALSE;
             m_pMainWnd->ShowWindow(SW_SHOW);
             m_pMainWnd->UpdateWindow();
             return TRUE;
            }

              完成一些如動(dòng)態(tài)生成相關(guān)文檔、視,顯示主框架窗口、處理參數(shù)行信息等工作。這些都是顯示在你工程中的“明碼”。我們現(xiàn)在把斷點(diǎn)設(shè)置到return TRUE;一句,跟入MFC源碼中,看看到底MFC內(nèi)部做了什么。

              程序進(jìn)入SRCWinMain.cpp,下一個(gè)大動(dòng)作應(yīng)是:

            nReturnCode = pThread->Run();

              注意了,重點(diǎn)來(lái)了。F11進(jìn)入

            int CWinApp::Run()
            {
             if (m_pMainWnd == NULL AfxOleGetUserCtrl())
             {
              // Not launched /Embedding or /Automation, but has no main window!
              TRACE0(Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application. );
              AfxPostQuitMessage(0);
             }
             return CWinThread::Run();
            }

              再次F11進(jìn)入:

            int CWinThread::Run()
            {
             ASSERT_VALID(this);

             // for tracking the idle time state
             BOOL bIdle = TRUE;
             LONG lIdleCount = 0;

             // acquire and dispatch messages until a WM_QUIT message is received.
             for (;;)
             {
              // phase1: check to see if we can do idle work
              while (bIdle !::PeekMessage(m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
              {
               // call OnIdle while in bIdle state
               if (!OnIdle(lIdleCount++))
               bIdle = FALSE; // assume no idle state
              }

              // phase2: pump messages while available
              do
              {
               // pump message, but quit on WM_QUIT
               if (!PumpMessage())
                return ExitInstance();

               // reset no idle state after pumping normal message
               if (IsIdleMessage(m_msgCur))
               {
                bIdle = TRUE;
                lIdleCount = 0;
               }

              } while (::PeekMessage(m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
             }

             ASSERT(FALSE); // not reachable
            }

            BOOL CWinThread::IsIdleMessage(MSG* pMsg)
            {
             // Return FALSE if the message just dispatched should _not_
             // cause OnIdle to be run. Messages which do not usually
             // affect the state of the user interface and happen very
             // often are checked for.

             // redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE
             if (pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE)
             {
              // mouse move at same position as last mouse move?
              if (m_ptCursorLast == pMsg->pt pMsg->message == m_nMsgLast)
               return FALSE;

              m_ptCursorLast = pMsg->pt; // remember for next time
              m_nMsgLast = pMsg->message;
              return TRUE;
             }

             // WM_PAINT and WM_SYSTIMER (caret blink)
             return pMsg->message != WM_PAINT pMsg->message != 0x0118;
            }

              這是SDI處理消息的中心機(jī)構(gòu),但請(qǐng)注意,它覺(jué)對(duì)不是核心!

              分析一下,在無(wú)限循環(huán)FOR內(nèi)部又出現(xiàn)一個(gè)WHILE循環(huán)

            while (bIdle
            !::PeekMessage(m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
            {
             // call OnIdle while in bIdle state
             if (!OnIdle(lIdleCount++))
              bIdle = FALSE; // assume no idle state
            }

              這段代碼是當(dāng)你程序進(jìn)程的消息隊(duì)列中沒(méi)有消息時(shí),會(huì)調(diào)用OnIdle做一些后備工作,臨時(shí)對(duì)象在這里被刪除。當(dāng)然它是虛函數(shù)。其中的PeekMessage,是查看消息隊(duì)列,如果有消息返回TRUE,如果沒(méi)有消息返回FALSE,這里指定PM_NOREMOVE,是指查看過(guò)后不移走消息隊(duì)列中剛剛被查看
            到的消息,也就是說(shuō)這里的PeekMessage只起到一個(gè)檢測(cè)作用,顯然返回FALSE時(shí)[即沒(méi)有消息],才會(huì)進(jìn)入循環(huán)內(nèi)部,執(zhí)行OnIdle,當(dāng)然了,你的OnIdle返回FLASE,會(huì)讓程序不再執(zhí)行OnIdle。你可能要問(wèn):

            當(dāng)bidle=0或消息隊(duì)例中有消息時(shí),程序又執(zhí)行到哪了呢?

            do
            {
             // pump message, but quit on WM_QUIT
             if (!PumpMessage())
              return ExitInstance();

             // reset no idle state after pumping normal message
             if (IsIdleMessage(m_msgCur))
             {
              bIdle = TRUE;
              lIdleCount = 0;
             }

            } while (::PeekMessage(m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

              看啊,又進(jìn)入一個(gè)循環(huán)!

              其中有個(gè)重要的函數(shù),PumpMessage,內(nèi)容如下:

            BOOL CWinThread::PumpMessage()
            {
             ASSERT_VALID(this);

             if (!::GetMessage(m_msgCur, NULL, NULL, NULL))
             {
              #ifdef _DEBUG
              if (afxTraceFlags traceAppMsg)
               TRACE0(CWinThread::PumpMessage - Received WM_QUIT. );
               m_nDisablePumpCount++; // application must die
               // Note: prevents calling message loop things in ’ExitInstance’
               // will never be decremented
              #endif
              return FALSE;
             }

             #ifdef _DEBUG
             if (m_nDisablePumpCount != 0)
             {
              TRACE0(Error: CWinThread::PumpMessage called when not permitted. );
              ASSERT(FALSE);
             }
             #endif

             #ifdef _DEBUG
             if (afxTraceFlags traceAppMsg)
              _AfxTraceMsg(_T(PumpMessage), m_msgCur);
             #endif

             // process this message

             if (m_msgCur.message != WM_KICKIDLE !PreTranslateMessage(m_msgCur))
             {
              ::TranslateMessage(m_msgCur);
              ::DispatchMessage(m_msgCur);
             }
             return TRUE;
            }

              如你所想,這才是MFC消息處理的核心基地[也是我個(gè)人認(rèn)為的]。

              GetMessage不同于PeekMessae,它是不得到消息不罷體,PeekMessage如果發(fā)現(xiàn)消息隊(duì)列中沒(méi)有消息會(huì)返回0,而GetMessage如果發(fā)現(xiàn)沒(méi)有消息,等,直到有了消息,而且,GetMessage不同于PeekMessage,它會(huì)將消息移走[當(dāng)然,PeekMessage也可以做到這點(diǎn)]。我想當(dāng)你讀了這個(gè)函數(shù)后,
            你應(yīng)明白PreTranslateMessage函數(shù)的用法了吧[我比較喜歡在程序中充分利用這個(gè)函數(shù)]。

            ::TranslateMessage(m_msgCur);
            ::DispatchMessage(m_msgCur);

              將消息發(fā)送到窗口的處理函數(shù)[它是由窗口類指定的],之后的動(dòng)作一直到你的程序做出反映的過(guò)程,你可以在《深入》一書中得到完美的解釋。我們還是通過(guò)reurn
            TRUE;回到CWinThread::Run()中的Do{}while;循環(huán)。然后還是對(duì)IDLE的處理,即便剛才你的ONIDLE返回了FALSE,在這里你看到,你的程序還是有機(jī)會(huì)執(zhí)行它的。然后又是利用PeekMessage檢測(cè)消息隊(duì)列:

              如果有消息[這個(gè)消息不被移動(dòng)的原因是因?yàn)樗獮镻umpMessage內(nèi)的GetMessage所利用。]再次進(jìn)入PumpMessage[叫它“消息泵”吧]。

              如果沒(méi)有消息,退出DO循環(huán),但它還在FOR內(nèi)部,所以又執(zhí)行第一個(gè)While循環(huán)。

              這是CwinThread::Run的一個(gè)執(zhí)行過(guò)程。

              不用擔(dān)心退不出for(;;)如果你的消息隊(duì)列中有一條WM_QUIT,會(huì)使GetMessage返回0,然后PumpMessage返回0而RUN()內(nèi)部:

            if (!PumpMessage())
            return ExitInstance();

              SDI就說(shuō)到這,下面我來(lái)談一下模式對(duì)話框。我分2種情況討論:

              一當(dāng)你的工程以模式對(duì)話框?yàn)榛A(chǔ)時(shí)[沒(méi)父窗口,或?yàn)樽烂妫荨?

              與SDI不同處在于,在應(yīng)用程序類的InItInstance內(nèi)部:

            BOOL CComboBoxApp::InitInstance()
            {
             AfxEnableControlContainer();
             // Standard initialization
             // If you are not using these features and wish to reduce the size
             // of your final executable, you should remove from the following
             // the specific initialization routines you do not need.
             #ifdef _AFXDLL
              Enable3dControls(); // Call this when using MFC in a shared DLL
             #else
              Enable3dControlsStatic(); // Call this when linking to MFC statically
             #endif
             this->m_nCmdShow = SW_HIDE;
             CComboBoxDlg dlg;
             m_pMainWnd = dlg;
             int nResponse = dlg.DoModal();
             if (nResponse == IDOK)
             {
              // TODO: Place code here to handle when the dialog is
              // dismissed with OK
             }
             else if (nResponse == IDCANCEL)
             {
              // TODO: Place code here to handle when the dialog is
              // dismissed with Cancel
             }
             // Since the dialog has been closed, return FALSE so that we exit the
             // application, rather than start the application’s message pump.
             return FALSE;
            }

              int nResponse = dlg.DoModal();一句使你的整個(gè)程序都在DoModal()內(nèi)部進(jìn)行。而且,你退出DoMal()時(shí)[你一定結(jié)束了你的對(duì)話框],InitInstance返回的是False,我們知道,這樣,CwinThread::Run是不會(huì)執(zhí)行的。

              但對(duì)話框程序是在哪里進(jìn)行消息處理的呢。

              原來(lái),dlg.DoModal()內(nèi)部會(huì)調(diào)用CwinThread::RunModalLoop,它起到的作用和RUN()是一樣的[當(dāng)然內(nèi)部有細(xì)小差別,請(qǐng)參考MSDN]!!!

              第二種情況,你是在SDI[或其它]程序中調(diào)用Dlg.DoModal() 產(chǎn)生了一模式對(duì)話框[有父窗口].

              這又是如何運(yùn)作的呢?

              建了這樣一個(gè)工程做為例子。

              SDI,在View中處理LBUTTONDOWN:

            MyDLg.DoModal();

              MyDLg內(nèi)有按鈕,以憊后用.

              沒(méi)有顯示模式對(duì)話框前,消息處理一直在Cthread::Run()中進(jìn)行.

              你單擊后,程序執(zhí)行點(diǎn)進(jìn)入DoModal()內(nèi)部的RunModalLoop,這又是一個(gè)消息處理機(jī)制.

              不過(guò)DoModal()中調(diào)用RunModalLoop,前會(huì)Disable掉它的父窗口.從RunModalLoop中出來(lái)后,再 Enable它.

              模式對(duì)話框和非模式對(duì)話框都是通過(guò)調(diào)用CreateDialogIndirect()產(chǎn)生創(chuàng)建對(duì)話框.那它和非模式對(duì)話框區(qū)別是什么造成的呢?

              1 模式對(duì)話框?qū)⒏复翱贒ISABLE掉.

              我原以為被Disable的窗口是不接收消息的.但后來(lái)我馬上發(fā)現(xiàn)我是錯(cuò)的.但,為什么你對(duì)被Disable的窗口進(jìn)行KeyBorad,Mouse動(dòng)作時(shí),窗口沒(méi)反映呢,我想,這可能是操作系統(tǒng)從中搞的鬼.我在本文一開(kāi)始,就寫出操作系統(tǒng)向窗口發(fā)送消息的過(guò)程,我想當(dāng)它發(fā)現(xiàn)目標(biāo)窗口處理Disabled狀態(tài)時(shí),
            不會(huì)將消息發(fā)送給它,但這不能說(shuō)窗口不接收消息,其它程序[或它本身]發(fā)送給它的消息還是可以接收并處理的.

              2 模式對(duì)話框本身有消息處理機(jī)制 RunModalLoop.

              對(duì)以上兩點(diǎn)加以實(shí)驗(yàn).

              我在我的剛才建的項(xiàng)目中的模式對(duì)話框中加上一個(gè)BUTTON,其中加入如下代碼:

            OnButton1()
            {
             GetParaent()->EnableWindow(1);
            }

              單擊,后我們發(fā)現(xiàn),此時(shí)它已經(jīng)不再表現(xiàn)為”模態(tài)”,我試著點(diǎn)擊菜單,還是會(huì)作出正常反映.

              我想這此消息[對(duì)于父窗口的,如:菜單動(dòng)作]的處理也應(yīng)是在模式對(duì)話框中的RunModalLoop中進(jìn)行處理的吧[這點(diǎn)我不能確定].

            c++相關(guān)文章:c++教程




            關(guān)鍵詞: 消息機(jī)制

            評(píng)論


            技術(shù)專區(qū)

            關(guān)閉