青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

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

深入探討MFC消息循環(huán)和消息泵

作者:周焱

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

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

這里講述MFC的消息循環(huán),消息泵。先看看程序啟動時,怎么進(jìn)入消息循環(huán)的:
_tWinMain ->AfxWinMain ->AfxWinInit ->CWinThread::InitApplication ->CWinThread::InitInstance ->CWinThread::Run

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

第一部分:非對話框程序的消息循環(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));
}    //無限循環(huán),退出條件是收到WM_QUIT消息。

ASSERT(FALSE); // not reachable
}

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

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

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

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

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

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

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

2,第二個內(nèi)循環(huán)phase2。在這個循環(huán)內(nèi)先啟動消息泵(PumpMessage),如果不是WM_QUIT消息,消息泵將消息發(fā)送出去(::DispatchMessage)。消息的目的地是消息結(jié)構(gòu)中的hwnd字段所對應(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;
}

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

// 如果是線程消息,那么將會調(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
}

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

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

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

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

第三,如果消息的目標(biāo)窗口和主窗口沒有父子關(guān)系,那么再調(diào)用主 窗口的PreTranslateMessage函數(shù)。為什么這樣?由第二步知道,一個窗口的父窗口不是主窗口的話,盡管它的 PreTranslateMessage返回FALSE,主窗口也沒有機(jī)會調(diào)用PreTranslateMessage函數(shù)。我們知道,加速鍵的轉(zhuǎn)換一般 在框架窗口的PreTranslateMessage函數(shù)中。我找遍了MFC中關(guān)于加速鍵轉(zhuǎn)換的處理,只有CFrameWnd, CMDIFrameWnd,CMDIChildWnd等窗口類有。所以,第三步的意思是,如果消息的目標(biāo)窗口(他的父窗口不是主窗口,比如一個這樣的非模 式對話框)使消息的預(yù)處理繼續(xù)漫游的話(他的PreTranslateMessage返回FALSE),那么給一次機(jī)會給主窗口調(diào)用 PreTranslateMessage(萬一他是某個加速鍵消息呢?),這樣能夠保證在有非模式對話框的情況下還能保證主窗口的加速鍵好使。
我做了一個小例子,在對話框類的PreTranslateMessage中,返回FALSE。在主窗口顯示這個非模式對話框,在對話框擁有焦點(diǎn)的時候,仍然能夠激活主窗口的快捷鍵。

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

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

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

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

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

基于對話框的MFC工程和上面的消息循環(huán)機(jī)制不一樣。實(shí)際上MFC的對話框工程程序就是模式對話框。他和上面講到的非對話框程序的不同之處,主要在于應(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; //定義一個對話框?qū)ο?/font>
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal(); //對話框的消息循環(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,由最上面程序啟動流程可以看出,CWinThread::Run是不會得到執(zhí)行的。也就是說,上面第一部分 說的消息循環(huán)在對話框中是不能執(zhí)行的。實(shí)際上,對話框也有消息循環(huán),她的消息循環(huán)在CDialog::DoModal()虛函數(shù)中的一個 RunModalLoop函數(shù)中。

這個函數(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));
} //無限循環(huán)

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

先說說怎么退出這個無限循環(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ù)檢查對話框是否繼續(xù)模式。返回TRUE,表示現(xiàn)在是模式的;返回FALSE,表示對話框已經(jīng)不是模式(將要結(jié)束)。

如 果要結(jié)束對話框,在內(nèi)部最終會調(diào)用函數(shù)CWnd::EndModalLoop,它取消m_nFlags的模式標(biāo)志(消息循環(huán)中的 ContinueModal函數(shù)將返回FALSE,消息循環(huán)將結(jié)束,程序?qū)⑼顺?;然后激發(fā)消息循環(huán)讀取消息。也就是說,結(jié)束模式對話框是一個標(biāo)志,改變 這個標(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)是有用的。如果消息隊列中沒有消息的話,可能消息循環(huán)中的ContinueModal()不會馬上執(zhí)行,發(fā)送一個空消息是激發(fā)消息循環(huán)馬上工作。

下面說一下CWnd::RunModalLoop函數(shù)中的消息循環(huán)究竟干了些什么事情:
1, 第一個內(nèi)循環(huán)。首先從消息隊列中查詢消息,如果對話框空閑,而且消息隊列中沒有消息,他做三件事情,大家應(yīng)到都能從字面上明白什么意思。最重要的是發(fā)送 WM_KICKIDLE消息。為什么呢?第一部分講到了,非對話框程序用OnIdle來更新用戶界面(UI),比如工具欄,狀態(tài)欄。那么,如果對話框中也 有工具欄和狀態(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消息給所有的用戶界面對話框控件。

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

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

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

這個話題已經(jīng)有很多人討論,我說說我所理解的意思。
在MFC 框架中,一個對話框?qū)ο驞oModal一下就能產(chǎn)生一個模式對話框,Create一下就能產(chǎn)生一個非模式對話框。實(shí)際上,無論是模式對話框還是非模式對話 框,在MFC內(nèi)部都是調(diào)用::CreateDialogIndirect(***)函數(shù)來創(chuàng)建非模式對話框。只是模式對話框作了更多的工作,包括使父窗口 無效,然后進(jìn)入自己的消息循環(huán)等等。::CreateDialogIndirect(***)函數(shù)最終調(diào)用CreateWindowEx函數(shù)通知系統(tǒng)創(chuàng)建 窗體并返回句柄,他內(nèi)部沒有實(shí)現(xiàn)自己的消息循環(huán)。
非模式對話框創(chuàng)建之后立即返回,并且和主程序共用一個消息循環(huán)。非模式對話框要等對話框結(jié)束之后才返回,自己有消息循環(huán)。比如下面的代碼:
CMyDlg* pdlg = new CMyDlg;
pdlg ->Create(IDD_DIALOG1);
pdlg->ShowWindow(SW_SHOW);
MessageBox("abc");
非模式對話框和消息框MessageBox幾乎是同時彈出來。而如果將Create改成DoModal,那么,只能彈出模式對話框,在關(guān)閉了對話框之后(模式對話框自己的消息循環(huán)結(jié)束),消息框才彈出來。

NOTE: 可以在模式對話框中調(diào)用GetParent()->EnableWindow(true);這樣,主窗口的菜單,工具欄又激活了,能用了。MFC使 用非模式對話框來模擬模式對話框,而在win32 SDK程序中,模式對話框激發(fā)他的父窗口Enable操作是沒有效果的。

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


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

2,因?yàn)槭菃尉€程的(程序進(jìn)程 啟動的時候,只有而且有一個線程,我們稱他為主線程),所以就像郵遞員一樣,每次只能在某一個地方干活。什么意思呢?舉個例子,用:: DiapatchMessage派送消息,在窗口處理過程(WinProc,窗口函數(shù))返回之前,他是阻塞的,不會立即返回,也就是消息循環(huán)此時不能再從 消息隊列中讀取消息,直到::DispatchMessage返回。如果你在窗口函數(shù)中執(zhí)行一個死循環(huán)操作,就算你用PostQuitMessage函數(shù) 退出,程序也會down掉。
while(1)
{
    PostQuitMessage(0); //程序照樣down.
}
所 以,當(dāng)窗口函數(shù)處理沒有返回的時候,消息循環(huán)是不會從消息隊列中讀取消息的。這也是為什么在模式對話框中要自己用無限循環(huán)來繼續(xù)消息循環(huán),因?yàn)檫@個無限循 環(huán)阻塞了原來的消息循環(huán),所以,在這個無限循環(huán)中要用GetMessage,PeekMessage,DispatchMessage來從消息隊列中讀取 消息并派送消息了。要不然程序就不會響應(yīng)了,這不是我們所希望的。
所以說,消息循環(huán)放在程序的什么的地方都基本上是過的去的,比如放在DLL里 面。但是,最好在任何時候,只有一個消息循環(huán)在工作(其他的都被阻塞了)。然后,我們要作好的一件事情,就是怎么從消息循環(huán)中退出!當(dāng)然用WM_QUIT 是可以拉~(PostThreadMessage也是個好主意),這個消息循環(huán)退出后,可能程序退出,也可能會激活另外一個被阻塞的消息循環(huán),程序繼續(xù)運(yùn) 行。這要看你怎么想,怎么去做。最后一個消息循環(huán)結(jié)束的時候,也許就是程序快結(jié)束的時候,因?yàn)橹骶€程的執(zhí)行代碼也快要完了(除非BT的再作個死循環(huán))。

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

3,在整個消息循環(huán)的機(jī)制中,還必須談到窗口函數(shù)的可重入性。什么意思?就是窗口函數(shù)(他是個回調(diào)函數(shù))的代碼什么時候都可以被系統(tǒng)(調(diào)用者一般是user32模塊)調(diào)用。比如在窗口過程中,向自己的窗口SendMessage(***);那么執(zhí)行過程是怎樣的?
我們知道,SendMessage是要等到消息發(fā)送并被目標(biāo)窗口執(zhí)行完之后才返回的。那么窗口在處理消息,然后又等待剛才發(fā)送到本窗口的消息被處理后之后(SendMessage返回)才繼續(xù)往下執(zhí)行,程序不就互相死鎖了嗎?
其 實(shí)是不會的。windows設(shè)計一套適合SendMessage的算法,他判斷如果發(fā)送的消息是屬于本線程創(chuàng)建的窗口的,那么直接由user32模塊調(diào)用 窗口函數(shù)(可能就有窗口重入),并將消息的處理結(jié)果結(jié)果返回。這樣做體現(xiàn)了窗口重入。上面的例子,我們調(diào)用SendMessage(***)發(fā)送消息到本 窗口,那么窗口過程再次被調(diào)用,處理完消息之后將結(jié)果返回,然后SendMessage之后的程序接著執(zhí)行。對于非隊列消息,如果沒有窗口重入,不知道會 是什么樣子。

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

4,MFC中窗口類(比如C**View,CFrameWnd等)中的MessageBox函數(shù),以及 AfxMessageBox函數(shù)都是阻塞原有的消息循環(huán)的。由消息框內(nèi)部的一個消息循環(huán)來從消息隊列中讀取消息,并派送消息(和模式對話框類似)。實(shí)際 上,這些消息函數(shù)最終調(diào)用的是::MessageBox,它在消息框內(nèi)部實(shí)現(xiàn)了一個消息循環(huán)(原有的主程序消息循環(huán)被阻塞了)。論壇中碰到過幾次關(guān)于計時 器和消息框的問題,看下面的代碼:
void CTest_recalclayoutView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
MessageBox("abc");
while(1); //設(shè)計一個死循環(huán)
CView::OnTimer(nIDEvent);
}
咱 讓OnTimer大約5秒鐘彈出一個消息框。那么,消息框不斷的被彈出來,只要消息框不被關(guān)閉,那么程序就不會進(jìn)入死循環(huán)。實(shí)際上,每次彈出對話框,都是 最上層的那個消息框掌握著消息循環(huán),其他的消息循環(huán)被阻塞了。只要不關(guān)閉最上面的消息框,while(1);就得不到執(zhí)行。如果點(diǎn)了關(guān)閉,程序就進(jìn)入了死 循環(huán),只能用ctrl+alt+del來解決問題了。

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

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

 

6, 在單線程程序中,如果要執(zhí)行一個長時間的復(fù)雜操作而且界面要有相應(yīng)的話,可以考慮用自己的消息泵。比如,可以將一個阻塞等待操作放在一個循環(huán)中,并將超時 值設(shè)置得比較小,然后每個等待的片段中用消息泵繼續(xù)消息循環(huán),使界面能夠響應(yīng)用戶操作。等等之類,都可以應(yīng)用消息泵(調(diào)用一個類似這樣的函數(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)算時的界面問題,但是沒有這么方便,而且一般要加入線程通信和同步,考慮的事情更多一點(diǎn)。

 

綜上所述,MFC消息循環(huán)就那么回事,主要思想還是和SDK中差不多。這種思想主要的特點(diǎn)表現(xiàn)在迎合MFC整個框架上,為整個框架服務(wù),為應(yīng)用和功能服務(wù)。這是我的理解。呵呵~

posted on 2007-12-17 20:28 李亞 閱讀(404) 評論(0)  編輯 收藏 引用 所屬分類: MFC/VC

<2007年12月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

導(dǎo)航

統(tǒng)計

  • 隨筆 - 32
  • 文章 - 0
  • 評論 - 5
  • 引用 - 0

公告

這世界并不會在意你的自尊,這世界指望你在自我感覺良好之前先要有所成就!

常用鏈接

留言簿(3)

隨筆分類(32)

隨筆檔案(32)

相冊

最新隨筆

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
              欧美国内亚洲| 亚洲视频在线二区| 136国产福利精品导航网址应用 | 亚洲欧美日韩国产综合| 国产欧美在线观看| 久久精品五月| 亚洲免费成人av| 久久电影一区| 亚洲精品一区二区三区在线观看| 欧美亚日韩国产aⅴ精品中极品| 欧美亚洲日本国产| 亚洲欧洲美洲综合色网| 国产精品99久久久久久有的能看| 国产亚洲精品一区二区| 欧美成人a∨高清免费观看| 在线性视频日韩欧美| 美女网站久久| 午夜国产一区| 亚洲三级电影在线观看| 国产欧美激情| 欧美欧美天天天天操| 欧美一区二区在线视频| 亚洲人成啪啪网站| 裸体歌舞表演一区二区| 亚洲欧美日韩中文视频| 在线看欧美日韩| 国产模特精品视频久久久久 | 校园激情久久| 日韩系列在线| 亚洲第一精品电影| 久久精品亚洲一区二区| 中文精品视频| 亚洲激情欧美激情| 欧美怡红院视频一区二区三区| 亚洲国产精品激情在线观看| 久久av二区| 国产精品99久久久久久久久久久久| 极品少妇一区二区三区| 国产日韩精品在线观看| 欧美亚洲动漫精品| 欧美日韩不卡| 欧美成人午夜视频| 久久综合中文字幕| 久久久久久久999精品视频| 香蕉久久夜色精品国产使用方法| 一区二区三区视频在线观看| 亚洲精品国产精品国自产观看| 嫩草成人www欧美| 久久精品伊人| 久久国产黑丝| 欧美淫片网站| 香蕉乱码成人久久天堂爱免费 | 久久亚洲高清| 久久av免费一区| 欧美亚洲综合另类| 亚洲欧美另类中文字幕| 亚洲婷婷综合色高清在线| 日韩西西人体444www| 亚洲精品乱码久久久久久久久| 亚洲电影激情视频网站| 伊人成年综合电影网| 一区在线播放| 1000精品久久久久久久久| 影音先锋亚洲一区| 亚洲电影第1页| 91久久综合| 亚洲美女黄色片| aa成人免费视频| 中文在线不卡视频| 亚洲欧美日本国产有色| 欧美一级大片在线观看| 欧美一区二区三区视频在线| 欧美一区二区三区另类| 欧美在线视频免费观看| 久久精品一本| 欧美成ee人免费视频| 亚洲国产高清在线| 亚洲精品系列| 亚洲一区二区高清| 欧美一级欧美一级在线播放| 欧美一区二区播放| 久久夜色精品国产欧美乱| 美日韩精品免费| 欧美日韩国产a| 国产精品午夜在线观看| 激情综合中文娱乐网| 亚洲精品一区中文| 亚洲欧美国产高清va在线播| 久久大香伊蕉在人线观看热2| 久久综合综合久久综合| 亚洲国产天堂网精品网站| 一区二区av| 久久av一区二区三区亚洲| 久久精品国产久精国产思思 | 午夜久久影院| 久久久久久久一区二区三区| 欧美国产激情| 国产精品影音先锋| 在线免费一区三区| 中国成人亚色综合网站| 久久精品成人一区二区三区| 欧美激情精品久久久久久久变态 | 欧美激情1区| 99在线精品视频在线观看| 香蕉免费一区二区三区在线观看| 久久亚洲不卡| 国产精品a久久久久久| 韩国欧美国产1区| 一区二区三区导航| 久久婷婷国产综合精品青草| 亚洲三级免费| 久久久精品2019中文字幕神马| 久久女同互慰一区二区三区| 欧美在线一区二区| 亚洲高清成人| 亚洲欧美视频一区| 欧美激情二区三区| 国产女人精品视频| 日韩五码在线| 久久婷婷国产综合尤物精品 | 久久综合激情| 一区二区不卡在线视频 午夜欧美不卡在| 欧美在线精品一区| 欧美网站在线观看| 尤物99国产成人精品视频| 亚洲一区在线直播| 欧美大片在线看免费观看| 亚洲在线一区二区三区| 欧美激情一区二区在线| 一区二区视频在线观看| 欧美亚洲日本一区| 日韩午夜中文字幕| 欧美激情精品久久久久久黑人| 韩国欧美一区| 欧美在线一区二区| 亚洲天堂av电影| 欧美日韩精品三区| 亚洲三级影院| 牛牛国产精品| 久久国产精品久久久久久久久久| 欧美午夜精品久久久久久久| 亚洲日韩成人| 欧美激情视频给我| 久久精品最新地址| 国产欧美日韩视频一区二区三区| 亚洲天堂第二页| 亚洲三级色网| 欧美精品一区在线发布| 亚洲人成网站在线观看播放| 久久先锋资源| 久久久99免费视频| 国内精品视频一区| 久久精品国产清高在天天线| 亚洲影院污污.| 国产精品一级在线| 西西人体一区二区| 亚洲免费网址| 国产欧美高清| 欧美在线免费一级片| 亚洲欧美激情视频| 国产欧美视频一区二区| 欧美一区二区三区电影在线观看| 亚洲一区999| 国产精品私房写真福利视频| 亚洲欧美国产精品va在线观看| 中文在线资源观看视频网站免费不卡| 欧美日韩一区二区三区四区在线观看 | 欧美日韩一区二区三区视频| 一区二区欧美日韩| 一区二区三欧美| 国产精品久久99| 亚洲欧美日韩人成在线播放| 亚洲午夜精品17c| 国产麻豆日韩欧美久久| 久久久久国产一区二区三区四区 | 久久爱另类一区二区小说| 国产综合自拍| 欧美大片免费观看| 欧美高清视频在线播放| 日韩视频免费| 一区二区三区精品视频在线观看| 国产精品mv在线观看| 欧美一区二区福利在线| 欧美诱惑福利视频| 亚洲国产日韩欧美在线动漫| 亚洲黄色在线| 国产精品高清网站| 欧美中文字幕在线播放| 久久青青草综合| av成人福利| 午夜精品一区二区三区在线| 红桃视频国产一区| 91久久国产综合久久蜜月精品 | 欧美亚洲在线播放| 欧美影院成人| 亚洲国产精品毛片| 亚洲精品色婷婷福利天堂| 欧美午夜久久| 欧美一区二区三区四区夜夜大片 | 欧美中文在线观看|