<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

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

深入剖析MFC中Windows消息處理機制

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

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

  正文:

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

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

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

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

  對于SDI窗口,你的應用程序類(lèi)的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)文檔、視,顯示主框架窗口、處理參數行信息等工作。這些都是顯示在你工程中的“明碼”。我們現在把斷點(diǎn)設置到return TRUE;一句,跟入MFC源碼中,看看到底MFC內部做了什么。

  程序進(jìn)入SRCWinMain.cpp,下一個(gè)大動(dò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處理消息的中心機構,但請注意,它覺(jué)對不是核心!

  分析一下,在無(wú)限循環(huán)FOR內部又出現一個(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
}

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

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

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è)重要的函數,PumpMessage,內容如下:

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è)人認為的]。

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

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

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

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

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

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

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

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

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

  一當你的工程以模式對話(huà)框為基礎時(shí)[沒(méi)父窗口,或為桌面]。

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

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()內部進(jìn)行。而且,你退出DoMal()時(shí)[你一定結束了你的對話(huà)框],InitInstance返回的是False,我們知道,這樣,CwinThread::Run是不會(huì )執行的。

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

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

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

  這又是如何運作的呢?

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

  SDI,在View中處理LBUTTONDOWN:

MyDLg.DoModal();

  MyDLg內有按鈕,以憊后用.

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

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

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

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

  1 模式對話(huà)框將父窗口DISABLE掉.

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

  2 模式對話(huà)框本身有消息處理機制 RunModalLoop.

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

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

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

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

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

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

  正文:

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

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

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

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

  對于SDI窗口,你的應用程序類(lèi)的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)文檔、視,顯示主框架窗口、處理參數行信息等工作。這些都是顯示在你工程中的“明碼”。我們現在把斷點(diǎn)設置到return TRUE;一句,跟入MFC源碼中,看看到底MFC內部做了什么。

  程序進(jìn)入SRCWinMain.cpp,下一個(gè)大動(dò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處理消息的中心機構,但請注意,它覺(jué)對不是核心!

  分析一下,在無(wú)限循環(huán)FOR內部又出現一個(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
}

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

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

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è)重要的函數,PumpMessage,內容如下:

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è)人認為的]。

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

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

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

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

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

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

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

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

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

  一當你的工程以模式對話(huà)框為基礎時(shí)[沒(méi)父窗口,或為桌面]。

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

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()內部進(jìn)行。而且,你退出DoMal()時(shí)[你一定結束了你的對話(huà)框],InitInstance返回的是False,我們知道,這樣,CwinThread::Run是不會(huì )執行的。

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

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

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

  這又是如何運作的呢?

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

  SDI,在View中處理LBUTTONDOWN:

MyDLg.DoModal();

  MyDLg內有按鈕,以憊后用.

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

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

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

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

  1 模式對話(huà)框將父窗口DISABLE掉.

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

  2 模式對話(huà)框本身有消息處理機制 RunModalLoop.

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

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

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

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

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

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




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

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>