久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

BBSHARE - VisualC++ - [轉(zhuǎn)帖]深入探討MFC消息循環(huán)和消息泵 - powered by Discuz!

 jollyme 2005-09-26
BBSHARE

#1  [轉(zhuǎn)帖]深入探討MFC消息循環(huán)和消息泵

文章標(biāo)題:深入探討MFC消息循環(huán)和消息泵
原 作 者:enoloo
原 出 處:vczx.com
發(fā) 布 者:enoloo
發(fā)布類型:原創(chuàng)
發(fā)布日期:2004-12-20
今日瀏覽:32
總 瀏 覽:7447
  




    首先,應(yīng)該清楚MFC的消息循環(huán)(::GetMessage,::PeekMessage),,消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之間的路由是兩件不同的事情,。在MFC的應(yīng)用程序中(應(yīng)用程序類基于CWinThread繼承),必須要有一個(gè)消息循環(huán),,他的作用是從應(yīng)用程序的消息隊(duì)列中讀取消息,,并把它派送出去(::DispatchMessage),。而消息路由是指消息派送出去之后,,系統(tǒng)(USER32.DLL)把消息投遞到哪個(gè)窗口,以及以后消息在窗口之間的傳遞是怎樣的,。

    消息分為隊(duì)列消息(進(jìn)入線程的消息隊(duì)列)和非隊(duì)列消息(不進(jìn)入線程的消息隊(duì)列),。對(duì)于隊(duì)列消息,最常見的是鼠標(biāo)和鍵盤觸發(fā)的消息,,例如WM_MOUSERMOVE,WM_CHAR等消息,;還有例如:WM_PAINT、WM_TIMER和WM_QUIT,。當(dāng)鼠標(biāo),、鍵盤事件被觸發(fā)后,相應(yīng)的鼠標(biāo)或鍵盤驅(qū)動(dòng)程序就會(huì)把這些事件轉(zhuǎn)換成相應(yīng)的消息,,然后輸送到系統(tǒng)消息隊(duì)列,,由Windows系統(tǒng)負(fù)責(zé)把消息加入到相應(yīng)線程的消息隊(duì)列中,于是就有了消息循環(huán)(從消息隊(duì)列中讀取并派送消息),。還有一種是非隊(duì)列消息,,他繞過(guò)系統(tǒng)隊(duì)列和消息隊(duì)列,直接將消息發(fā)送到窗口過(guò)程,。例如,當(dāng)用戶激活一個(gè)窗口系統(tǒng)發(fā)送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR,。創(chuàng)建窗口時(shí)發(fā)送WM_CREATE消息。在后面你將看到,,MS這么設(shè)計(jì)是很有道理的,,以及他的整套實(shí)現(xiàn)機(jī)制,。

  這里講述MFC的消息循環(huán),消息泵,。先看看程序啟動(dòng)時(shí),,怎么進(jìn)入消息循環(huán)的:

_tWinMain ->AfxWinMain ->AfxWinInit ->CWinThread::InitApplication ->CWinThread::InitInstance ->CWinThread::Run



  非對(duì)話框程序的消息循環(huán)的事情都從這CWinThread的一Run開始...

  第一部分:非對(duì)話框程序的消息循環(huán)機(jī)制。

//thrdcore.cpp
// main running routine until thread exits
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));
}    //無(wú)限循環(huán),,退出條件是收到WM_QUIT消息,。

ASSERT(FALSE);  // not reachable
}



這是一個(gè)無(wú)限循環(huán),他的退出條件是收到WM_QUIT消息:

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



   在PumpMessage中,,如果收到WM_QUIT消息,,那么返回FALSE,所以ExitInstance()函數(shù)執(zhí)行,跳出循環(huán),,返回程序的退出代碼,。所以,一個(gè)程序要退出,,只用在代碼中調(diào)用函數(shù)

  VOID PostQuitMessage( int nExitCode ),。指定退出代碼nExitCode就可以退出程序。

  下面討論一下這個(gè)函數(shù)Run的流程,,分兩步:

  1,第一個(gè)內(nèi)循環(huán)phase1,。bIdle代表程序是否空閑。他的意思就是,,如果程序是空閑并且消息隊(duì)列中沒有要處理的消息,,那么調(diào)用虛函數(shù)OnIdle進(jìn)行空閑處理。在這個(gè)處理中將更新UI界面(比如工具欄按鈕的enable和disable狀態(tài)),,刪除臨時(shí)對(duì)象(比如用FromHandle得到的對(duì)象指針,。由于這個(gè)原因,在函數(shù)之間傳遞由FromHandle得到的對(duì)象指針是不安全的,,因?yàn)樗麤]有持久性),。OnIdle是可以重載的,你可以重載他并返回TRUE使消息循環(huán)繼續(xù)處于空閑狀態(tài),。

  NOTE:MS用臨時(shí)對(duì)象是出于效率上的考慮,,使內(nèi)存有效利用,并能夠在空閑時(shí)自動(dòng)撤銷資源,。關(guān)于由句柄轉(zhuǎn)換成對(duì)象,,可以有若干種方法。一般是先申明一個(gè)對(duì)象obj,然后使用obj.Attatch來(lái)和一個(gè)句柄綁定,。這樣產(chǎn)生的對(duì)象是永久的,,你必須用obj.Detach來(lái)釋放對(duì)象。

  2,第二個(gè)內(nèi)循環(huán)phase2,。在這個(gè)循環(huán)內(nèi)先啟動(dòng)消息泵(PumpMessage),如果不是WM_QUIT消息,,消息泵將消息發(fā)送出去(::DispatchMessage)。消息的目的地是消息結(jié)構(gòu)中的hwnd字段所對(duì)應(yīng)的窗口,。
//thrdcore.cpp
BOOL CWinThread::PumpMessage()
{
ASSERT_VALID(this);

//如果是WM_QUIT就退出函數(shù)(return FALSE),,這將導(dǎo)致程序結(jié)束.
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) {
#ifdef _DEBUG
  if (afxTraceFlags & traceAppMsg)
   TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");
  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.\n");
  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); //鍵轉(zhuǎn)換
  ::DispatchMessage(&m_msgCur); //派送消息
}
return TRUE;
}



  在這一步有一個(gè)特別重要的函數(shù)大家一定認(rèn)識(shí):PreTranslateMessage。這個(gè)函數(shù)在::DispatchMessage發(fā)送消息到窗口之前,,進(jìn)行對(duì)消息的預(yù)處理,。PreTranslateMessage函數(shù)是CWinThread的成員函數(shù),大家重載的時(shí)候都是在View類或者主窗口類中,,那么,,它是怎么進(jìn)入別的類的呢?代碼如下:

//thrdcore.cpp
BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
ASSERT_VALID(this);

// 如果是線程消息,,那么將會(huì)調(diào)用線程消息的處理函數(shù)
if (pMsg->hwnd == NULL && DispatchThreadMessageEx(pMsg))
  return TRUE;

// walk from target to main window
CWnd* pMainWnd = AfxGetMainWnd();
if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
  return TRUE;

// in case of modeless dialogs, last chance route through main
//   window‘s accelerator table
if (pMainWnd != NULL)
{
   CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
   if (pWnd->GetTopLevelParent() != pMainWnd)
   return pMainWnd->PreTranslateMessage(pMsg);
}

return FALSE;   // no special processing
}



  由上面這個(gè)函數(shù)可以看出:

  第一,,如果(pMsg->hwnd == NULL),說(shuō)明這是一個(gè)線程消息。調(diào)用CWinThread::DispatchThreadMessageEx到消息映射表找到消息入口,,然后調(diào)用消息處理函數(shù),。

  NOTE: 一般用PostThreadMessage函數(shù)發(fā)送線程之間的消息,他和窗口消息不同,,需要指定線程id,消息激被系統(tǒng)放入到目標(biāo)線程的消息隊(duì)列中,;用ON_THREAD_MESSAGE( message, memberFxn )宏可以映射線程消息和他的處理函數(shù)。這個(gè)宏必須在應(yīng)用程序類(從CWinThread繼承)中,,因?yàn)橹挥袘?yīng)用程序類才處理線程消息,。如果你在別的類(比如視圖類)中用這個(gè)宏,,線程消息的消息處理函數(shù)將得不到線程消息,。

  第二,消息的目標(biāo)窗口的PreTranslateMessage函數(shù)首先得到消息處理權(quán),,如果函數(shù)返回FALSE,,那么他的父窗口將得到消息的處理權(quán),直到主窗口,;如果函數(shù)返回TRUE(表示消息已經(jīng)被處理了),,那么就不需要調(diào)用父類的PreTranslateMessage函數(shù)。這樣,,保證了消息的目標(biāo)窗口以及他的父窗口都可以有機(jī)會(huì)調(diào)用PreTranslateMessage--在消息發(fā)送到窗口之前進(jìn)行預(yù)處理(如果自己處理完然后返回FALSE的話 -_-b),如果你想要消息不傳遞給父類進(jìn)行處理的話,,返回TRUE就行了。

  第三,,如果消息的目標(biāo)窗口和主窗口沒有父子關(guān)系,,那么再調(diào)用主窗口的PreTranslateMessage函數(shù)。為什么這樣?由第二步知道,,一個(gè)窗口的父窗口不是主窗口的話,,盡管它的PreTranslateMessage返回FALSE,主窗口也沒有機(jī)會(huì)調(diào)用PreTranslateMessage函數(shù),。我們知道,,加速鍵的轉(zhuǎn)換一般在框架窗口的PreTranslateMessage函數(shù)中。

  我找遍了MFC中關(guān)于加速鍵轉(zhuǎn)換的處理,,只有CFrameWnd,CMDIFrameWnd,,CMDIChildWnd等窗口類有。所以,,第三步的意思是,,如果消息的目標(biāo)窗口(他的父窗口不是主窗口,比如一個(gè)這樣的非模式對(duì)話框)使消息的預(yù)處理繼續(xù)漫游的話(他的PreTranslateMessage返回FALSE),,那么給一次機(jī)會(huì)給主窗口調(diào)用PreTranslateMessage(萬(wàn)一他是某個(gè)加速鍵消息呢,?),這樣能夠保證在有非模式對(duì)話框的情況下還能保證主窗口的加速鍵好使,。

  我做了一個(gè)小例子,,在對(duì)話框類的PreTranslateMessage中,返回FALSE,。在主窗口顯示這個(gè)非模式對(duì)話框,,在對(duì)話框擁有焦點(diǎn)的時(shí)候,仍然能夠激活主窗口的快捷鍵,。


  總之,,整個(gè)框架就是讓每個(gè)消息的目標(biāo)窗口(包括他的父窗口)都有機(jī)會(huì)參與消息到來(lái)之前的處理。呵呵~

  至此,,非對(duì)話框的消息循環(huán)和消息泵的機(jī)制就差不多了,。這個(gè)機(jī)制在一個(gè)無(wú)限循環(huán)中,不斷地從消息隊(duì)列中獲取消息,,并且保證了程序的線程消息能夠得到機(jī)會(huì)處理,,窗口消息在預(yù)處理之后被發(fā)送到相應(yīng)的窗口處理過(guò)程。那么,,還有一點(diǎn)疑問(wèn),,為什么要一會(huì)兒調(diào)用::PeekMessage,一會(huì)兒調(diào)用::GetMessage呢,他們有什么區(qū)別,?

  NOTE:一般來(lái)說(shuō),,GetMessage被設(shè)計(jì)用來(lái)高效地從消息隊(duì)列獲取消息。如果隊(duì)列中沒有消息,,那么函數(shù)GetMessage將導(dǎo)致線程休眠(讓出CPU時(shí)間),。而PeekMessage是判斷消息隊(duì)列中如果沒有消息,它馬上返回0,不會(huì)導(dǎo)致線程處于睡眠狀態(tài),。

  在上面的phase1第一個(gè)內(nèi)循環(huán)中用到了PeekMessage,,它的參數(shù)PM_NOREMOVE表示并不從消息隊(duì)列中移走消息,而是一個(gè)檢測(cè)查詢,,如果消息隊(duì)列中沒有消息他立刻返回0,,如果這時(shí)線程空閑的話將會(huì)引起消息循環(huán)調(diào)用OnIdle處理過(guò)程(上面講到了這個(gè)函數(shù)的重要性)。如果將::PeekMessage改成::GetMessage(***),那么如果消息隊(duì)列中沒有消息,,線程將休眠,,直到線程下一次獲得CPU時(shí)間并且有消息出現(xiàn)才可能繼續(xù)執(zhí)行,這樣,,消息循環(huán)的空閑時(shí)間沒有得到應(yīng)用,,OnIdle也將得不到執(zhí)行。這就是為什么既要用::PeekMessage(查詢),又要用::GetMessage(做實(shí)際的工作)的緣故,。

  第二部分: 對(duì)話框程序的消息循環(huán)機(jī)制

  基于對(duì)話框的MFC工程和上面的消息循環(huán)機(jī)制不一樣,。實(shí)際上MFC的對(duì)話框工程程序就是模式對(duì)話框。他和上面講到的非對(duì)話框程序的不同之處,,主要在于應(yīng)用程序?qū)ο蟮腎nitInstance()不一樣,。

//dlg_5Dlg.cpp
BOOL CDlg_5App::InitInstance()
{
AfxEnableControlContainer();
#ifdef _AFXDLL
Enable3dControls();   // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

CDlg_5Dlg dlg; //定義一個(gè)對(duì)話框?qū)ο?br>m_pMainWnd = &dlg;
int nResponse = dlg.DoModal(); //對(duì)話框的消息循環(huán)在這里面開始
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;
}



  NOTE: InitInstance函數(shù)返回FALSE,由最上面程序啟動(dòng)流程可以看出,,CWinThread::Run是不會(huì)得到執(zhí)行的,。也就是說(shuō),上面第一部分說(shuō)的消息循環(huán)在對(duì)話框中是不能執(zhí)行的,。實(shí)際上,,對(duì)話框也有消息循環(huán),她的消息循環(huán)在CDialog::DoModal()虛函數(shù)中的一個(gè)RunModalLoop函數(shù)中,。

  這個(gè)函數(shù)的實(shí)現(xiàn)體在CWnd類中:

int CWnd::RunModalLoop(DWORD dwFlags)
{
ASSERT(::IsWindow(m_hWnd)); // window must be created
ASSERT(!(m_nFlags & WF_MODALLOOP)); // window must not already be in modal state

// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);
HWND hWndParent = ::GetParent(m_hWnd);
m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);
MSG* pMsg = &AfxGetThread()->m_msgCur;

// acquire and dispatch messages until the modal state is done
for (;;)
{
  ASSERT(ContinueModal());

  // phase1: check to see if we can do idle work
  while (bIdle &&
   !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
  {
   ASSERT(ContinueModal());

   // show the dialog when the message queue goes idle
   if (bShowIdle)
   {
    ShowWindow(SW_SHOWNORMAL);
    UpdateWindow();
    bShowIdle = FALSE;
   }

   // call OnIdle while in bIdle state
   if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)
   {
    // send WM_ENTERIDLE to the parent
    ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);
   }
   if ((dwFlags & MLF_NOKICKIDLE) ||
    !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))
   {
    // stop idle processing next time
    bIdle = FALSE;
   }
  }

  // phase2: pump messages while available
  do
  {
   ASSERT(ContinueModal());

   // pump message, but quit on WM_QUIT
   //PumpMessage(消息泵)的實(shí)現(xiàn)和上面講的差不多,。都是派送消息到窗口。
   if (!AfxGetThread()->PumpMessage())
   {
    AfxPostQuitMessage(0);
    return -1;
   }

   // show the window when certain special messages rec‘d
   if (bShowIdle &&
    (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))
   {
    ShowWindow(SW_SHOWNORMAL);

    UpdateWindow();
    bShowIdle = FALSE;
   }

   if (!ContinueModal())
    goto ExitModal;

   // reset "no idle" state after pumping "normal" message
   if (AfxGetThread()->IsIdleMessage(pMsg))
   {
    bIdle = TRUE;
    lIdleCount = 0;
   }

  } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
} //無(wú)限循環(huán)

ExitModal:
m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
return m_nModalResult;
}



先說(shuō)說(shuō)怎么退出這個(gè)無(wú)限循環(huán),,在代碼中:

if (!ContinueModal())
goto ExitModal;
  決定是否退出循環(huán),,消息循環(huán)函數(shù)返回也就是快要結(jié)束結(jié)束程序了。
BOOL CWnd::ContinueModal()
{
return m_nFlags & WF_CONTINUEMODAL;
}



  NOTE: CWnd::ContinueModal()函數(shù)檢查對(duì)話框是否繼續(xù)模式,。返回TRUE,表示現(xiàn)在是模式的;返回FALSE,,表示對(duì)話框已經(jīng)不是模式(將要結(jié)束),。

  如果要結(jié)束對(duì)話框,在內(nèi)部最終會(huì)調(diào)用函數(shù)CWnd::EndModalLoop,,它取消m_nFlags的模式標(biāo)志(消息循環(huán)中的ContinueModal函數(shù)將返回FALSE,,消息循環(huán)將結(jié)束,程序?qū)⑼顺?;然后激發(fā)消息循環(huán)讀取消息,。也就是說(shuō),,結(jié)束模式對(duì)話框是一個(gè)標(biāo)志,改變這個(gè)標(biāo)志就可以了,。他的代碼是:

//wincore.cpp
void CWnd::EndModalLoop(int nResult)
{
ASSERT(::IsWindow(m_hWnd));

// this result will be returned from CWnd::RunModalLoop
m_nModalResult = nResult;

// make sure a message goes through to exit the modal loop
if (m_nFlags & WF_CONTINUEMODAL)
{
  m_nFlags &= ~WF_CONTINUEMODAL;
  PostMessage(WM_NULL);
}
}




  NOTE: PostMessage(NULL)是有用的,。如果消息隊(duì)列中沒有消息的話,可能消息循環(huán)中的ContinueModal()不會(huì)馬上執(zhí)行,,發(fā)送一個(gè)空消息是激發(fā)消息循環(huán)馬上工作,。

  下面說(shuō)一下CWnd::RunModalLoop函數(shù)中的消息循環(huán)究竟干了些什么事情:
1,第一個(gè)內(nèi)循環(huán)。首先從消息隊(duì)列中查詢消息,,如果對(duì)話框空閑,,而且消息隊(duì)列中沒有消息,他做三件事情,,大家應(yīng)到都能從字面上明白什么意思,。最重要的是發(fā)送WM_KICKIDLE消息。為什么呢,?第一部分講到了,,非對(duì)話框程序用OnIdle來(lái)更新用戶界面(UI),比如工具欄,,狀態(tài)欄,。那么,如果對(duì)話框中也有工具欄和狀態(tài)欄呢,,在哪里更新(網(wǎng)上有很多這樣的程序),?可以處理WM_KICKIDLE消息:

LRESULT CDlg_5Dlg::OnKickIdle(WPARAM w,LPARAM l)
{
      //調(diào)用CWnd::UpdateDialogControls更新用戶界面
     UpdateDialogControls(this, TRUE);
     return 0;
}



  NOTE: CWnd::UpdateDialog函數(shù)發(fā)送CN_UPDATE_COMMAND_UI消息給所有的用戶界面對(duì)話框控件。

  2,第二個(gè)內(nèi)循環(huán),。最重要的還是PumpMessage派送消息到目標(biāo)窗口,。其他的,像第二個(gè)if語(yǔ)句,,0x118消息好像是WM_SYSTIMER消息(系統(tǒng)用來(lái)通知光標(biāo)跳動(dòng)的一個(gè)消息),。也就是說(shuō),如果消息為WM_SYSTIMER或者WM_SYSKEYDOWN,并且空閑顯示標(biāo)志為真的話,,就顯示窗口并通知窗口立刻重繪,。

  總之,對(duì)話框的消息循環(huán)機(jī)制和非對(duì)話框(比如SDI,MDI)還是類似的,,僅僅側(cè)重點(diǎn)不同,。模式對(duì)話框是模式顯示,自然有他的特點(diǎn),。下面部分討論一下模式對(duì)話框和非模式對(duì)話框的區(qū)別,。因?yàn)槟J綄?duì)話框有自己的特殊消息循環(huán),;而非模式對(duì)話框,共用程序的消息循環(huán),,和普通的窗口已經(jīng)沒有什么大的區(qū)別了,。

  第三部分:模式對(duì)話框和非模式對(duì)話框的區(qū)別

  這個(gè)話題已經(jīng)有很多人討論,我說(shuō)說(shuō)我所理解的意思,。
在MFC框架中,,一個(gè)對(duì)話框?qū)ο驞oModal一下就能產(chǎn)生一個(gè)模式對(duì)話框,Create一下就能產(chǎn)生一個(gè)非模式對(duì)話框,。實(shí)際上,,無(wú)論是模式對(duì)話框還是非模式對(duì)話框,在MFC內(nèi)部都是調(diào)用::CreateDialogIndirect(***)函數(shù)來(lái)創(chuàng)建非模式對(duì)話框,。只是模式對(duì)話框作了更多的工作,,包括使父窗口無(wú)效,然后進(jìn)入自己的消息循環(huán)等等,。::CreateDialogIndirect(***)函數(shù)最終調(diào)用CreateWindowEx函數(shù)通知系統(tǒng)創(chuàng)建窗體并返回句柄,,他內(nèi)部沒有實(shí)現(xiàn)自己的消息循環(huán)。
非模式對(duì)話框創(chuàng)建之后立即返回,,并且和主程序共用一個(gè)消息循環(huán),。非模式對(duì)話框要等對(duì)話框結(jié)束之后才返回,自己有消息循環(huán),。比如下面的代碼:

CMyDlg* pdlg = new CMyDlg;
pdlg ->Create(IDD_DIALOG1);
pdlg->ShowWindow(SW_SHOW);
MessageBox("abc");


  非模式對(duì)話框和消息框MessageBox幾乎是同時(shí)彈出來(lái),。而如果將Create改成DoModal,那么,,只能彈出模式對(duì)話框,,在關(guān)閉了對(duì)話框之后(模式對(duì)話框自己的消息循環(huán)結(jié)束),消息框才彈出來(lái),。
  NOTE:可以在模式對(duì)話框中調(diào)用GetParent()->EnableWindow(true);這樣,,主窗口的菜單,工具欄又激活了,,能用了,。MFC使用非模式對(duì)話框來(lái)模擬模式對(duì)話框,而在win32 SDK程序中,,模式對(duì)話框激發(fā)他的父窗口Enable操作是沒有效果的,。

  關(guān)于消息循環(huán)總結(jié):

  1,我們站在一個(gè)什么高度看消息循環(huán),?消息循環(huán)其實(shí)沒有什么深?yuàn)W的道理,。如果一個(gè)郵遞員要不斷在一個(gè)城市中送信,我們要求他做什么,?要求他來(lái)回跑,,但他一次只能在一個(gè)地方出現(xiàn)。如果我們的應(yīng)用程序只有一個(gè)線程的話,,我們要他不斷地為窗口傳遞消息,,我們?cè)趺醋觯吭谝粋€(gè)循環(huán)中不斷的檢測(cè)消息,,并將他發(fā)送到適當(dāng)?shù)拇翱?。窗口可以有很多個(gè),但消息循環(huán)只有一個(gè),,而且每時(shí)每刻最多只有一個(gè)地方在執(zhí)行代碼,。為什么? 看第二點(diǎn),。

  2,,因?yàn)槭菃尉€程的(程序進(jìn)程啟動(dòng)的時(shí)候,只有而且有一個(gè)線程,,我們稱他為主線程),所以就像郵遞員一樣,,每次只能在某一個(gè)地方干活。什么意思呢,?舉個(gè)例子,,用::DiapatchMessage派送消息,在窗口處理過(guò)程(WinProc,窗口函數(shù))返回之前,,他是阻塞的,不會(huì)立即返回,,也就是消息循環(huán)此時(shí)不能再?gòu)南㈥?duì)列中讀取消息,直到::DispatchMessage返回,。如果你在窗口函數(shù)中執(zhí)行一個(gè)死循環(huán)操作,,就算你用PostQuitMessage函數(shù)退出,程序也會(huì)down掉,。

while(1)
{
    PostQuitMessage(0); //程序照樣down.
}



  所以,,當(dāng)窗口函數(shù)處理沒有返回的時(shí)候,消息循環(huán)是不會(huì)從消息隊(duì)列中讀取消息的,。這也是為什么在模式對(duì)話框中要自己用無(wú)限循環(huán)來(lái)繼續(xù)消息循環(huán),,因?yàn)檫@個(gè)無(wú)限循環(huán)阻塞了原來(lái)的消息循環(huán),所以,,在這個(gè)無(wú)限循環(huán)中要用GetMessage,PeekMessage,DispatchMessage來(lái)從消息隊(duì)列中讀取消息并派送消息了,。要不然程序就不會(huì)響應(yīng)了,這不是我們所希望的,。

  所以說(shuō),,消息循環(huán)放在程序的什么的地方都基本上是過(guò)的去的,比如放在DLL里面,。但是,,最好在任何時(shí)候,,只有一個(gè)消息循環(huán)在工作(其他的都被阻塞了)。然后,,我們要作好的一件事情,,就是怎么從消息循環(huán)中退出!當(dāng)然用WM_QUIT是可以拉~(PostThreadMessage也是個(gè)好主意),,這個(gè)消息循環(huán)退出后,,可能程序退出,也可能會(huì)激活另外一個(gè)被阻塞的消息循環(huán),,程序繼續(xù)運(yùn)行,。這要看你怎么想,怎么去做,。最后一個(gè)消息循環(huán)結(jié)束的時(shí)候,,也許就是程序快結(jié)束的時(shí)候,因?yàn)橹骶€程的執(zhí)行代碼也快要完了(除非BT的再作個(gè)死循環(huán)),。


  NOTE: 讓windows系統(tǒng)知道創(chuàng)建一個(gè)線程的唯一方法是調(diào)用API CreatThread函數(shù)(__beginthreadex之類的都要在內(nèi)部調(diào)用他創(chuàng)建新線程),。好像windows核心編程說(shuō),在win2000下,,系統(tǒng)用CreateRemoteThread函數(shù)來(lái)創(chuàng)建線程,,CreateThread在內(nèi)部調(diào)用CreateRemoteThread。不過(guò)這不是爭(zhēng)論的焦點(diǎn),,至少win98下CreateRemoteThread并不能正常工作,,還是CreateThread主持大局。

  3,,在整個(gè)消息循環(huán)的機(jī)制中,,還必須談到窗口函數(shù)的可重入性。什么意思,?就是窗口函數(shù)(他是個(gè)回調(diào)函數(shù))的代碼什么時(shí)候都可以被系統(tǒng)(調(diào)用者一般是user32模塊)調(diào)用,。比如在窗口過(guò)程中,向自己的窗口SendMessage(***);那么執(zhí)行過(guò)程是怎樣的,?

  我們知道,,SendMessage是要等到消息發(fā)送并被目標(biāo)窗口執(zhí)行完之后才返回的。那么窗口在處理消息,,然后又等待剛才發(fā)送到本窗口的消息被處理后之后(SendMessage返回)才繼續(xù)往下執(zhí)行,,程序不就互相死鎖了嗎?

  其實(shí)是不會(huì)的,。windows設(shè)計(jì)一套適合SendMessage的算法,,他判斷如果發(fā)送的消息是屬于本線程創(chuàng)建的窗口的,那么直接由user32模塊調(diào)用窗口函數(shù)(可能就有窗口重入),,并將消息的處理結(jié)果結(jié)果返回,。這樣做體現(xiàn)了窗口重入,。上面的例子,我們調(diào)用SendMessage(***)發(fā)送消息到本窗口,,那么窗口過(guò)程再次被調(diào)用,,處理完消息之后將結(jié)果返回,,然后SendMessage之后的程序接著執(zhí)行,。對(duì)于非隊(duì)列消息,如果沒有窗口重入,,不知道會(huì)是什么樣子,。

  NOTE: 由于窗口的可重入性。在win32 SDK程序中應(yīng)盡量少用全局變量和靜態(tài)變量,,因?yàn)樵诖翱诤瘮?shù)執(zhí)行過(guò)程中可能窗口重入,,如果重入后將這些變量改了,但你的程序在窗口重入返回之后繼續(xù)執(zhí)行,,可能就是使用已經(jīng)改變的全局或靜態(tài)變量,。在MFC中(所有窗口的窗口函數(shù)基本上都是AfxWndProc),按照類的思想進(jìn)行了組織,,一般變量都是類中的,,好管理的多。

  4,MFC中窗口類(比如C**View,CFrameWnd等)中的MessageBox函數(shù),,以及AfxMessageBox函數(shù)都是阻塞原有的消息循環(huán)的,。由消息框內(nèi)部的一個(gè)消息循環(huán)來(lái)從消息隊(duì)列中讀取消息,并派送消息(和模式對(duì)話框類似),。實(shí)際上,,這些消息函數(shù)最終調(diào)用的是::MessageBox,它在消息框內(nèi)部實(shí)現(xiàn)了一個(gè)消息循環(huán)(原有的主程序消息循環(huán)被阻塞了),。論壇中碰到過(guò)幾次關(guān)于計(jì)時(shí)器和消息框的問(wèn)題,,看下面的代碼:

void CTest_recalclayoutView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
MessageBox("abc");
while(1); //設(shè)計(jì)一個(gè)死循環(huán)
CView::OnTimer(nIDEvent);
}



  咱讓OnTimer大約5秒鐘彈出一個(gè)消息框。那么,,消息框不斷的被彈出來(lái),,只要消息框不被關(guān)閉,那么程序就不會(huì)進(jìn)入死循環(huán),。實(shí)際上,,每次彈出對(duì)話框,都是最上層的那個(gè)消息框掌握著消息循環(huán),,其他的消息循環(huán)被阻塞了,。只要不關(guān)閉最上面的消息框,while(1);就得不到執(zhí)行,。如果點(diǎn)了關(guān)閉,,程序就進(jìn)入了死循環(huán),,只能用ctrl+alt+del來(lái)解決問(wèn)題了。

  5,,消息循環(huán)在很多地方都有應(yīng)用,。比如應(yīng)用在線程池中。一個(gè)線程的執(zhí)行周期一般在線程函數(shù)返回之后結(jié)束,,那么怎么延長(zhǎng)線程的生命周期呢,?一種方法就是按照消息循環(huán)的思想,在線程中加入消息循環(huán),,不斷地從線程隊(duì)列讀取消息,,并處理消息,線程的生命周期就保持著直到這個(gè)消息循環(huán)的退出,。

  NOTE:只要線程有界面元素或者調(diào)用GetMessage,或者有線程消息發(fā)送過(guò)來(lái),,系統(tǒng)就會(huì)為線程創(chuàng)建一個(gè)消息隊(duì)列。

  6,在單線程程序中,,如果要執(zhí)行一個(gè)長(zhǎng)時(shí)間的復(fù)雜操作而且界面要有相應(yīng)的話,,可以考慮用自己的消息泵。比如,,可以將一個(gè)阻塞等待操作放在一個(gè)循環(huán)中,,并將超時(shí)值設(shè)置得比較小,然后每個(gè)等待的片段中用消息泵繼續(xù)消息循環(huán),,使界面能夠響應(yīng)用戶操作,。等等之類,都可以應(yīng)用消息泵(調(diào)用一個(gè)類似這樣的函數(shù)):

BOOL CChildView::PeekAndPump()
{
MSG msg;
while(::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
  if(!AfxGetApp()->PumpMessage())
  {
   ::PostQuitMessage(0);
   return false;
  }
}
return true;
}



   其實(shí),,用多線程也能解決復(fù)雜運(yùn)算時(shí)的界面問(wèn)題,,但是沒有這么方便,而且一般要加入線程通信和同步,,考慮的事情更多一點(diǎn),。
  綜上所述,MFC消息循環(huán)就那么回事,,主要思想還是和SDK中差不多,。這種思想主要的特點(diǎn)表現(xiàn)在迎合MFC整個(gè)框架上,為整個(gè)框架服務(wù),,為應(yīng)用和功能服務(wù),。這是我的理解。呵呵~

  寫的比較匆忙,,如有錯(cuò)誤,,請(qǐng)指正。

  



    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多