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(消息泵)的實現和上面講的差不多。都是派送消息到窗口。
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);
消息分為隊列消息(進入線程的消息隊列)和非隊列消息(不進入線程的消息隊列)。對于隊列消息,最常見的是鼠標和鍵盤觸發的消息,例如 WM_MOUSERMOVE,WM_CHAR等消息;還有例如:WM_PAINT、WM_TIMER和WM_QUIT。當鼠標、鍵盤事件被觸發后,相應的鼠標或鍵盤驅動程序就會把這些事件轉換成相應的消息,然后輸送到系統消息隊列,由Windows系統負責把消息加入到相應線程的消息隊列中,于是就有了消息循環(從消息隊列中讀取并派送消息)。還有一種是非隊列消息,他繞過系統隊列和消息隊列,直接將消息發送到窗口過程。例如,當用戶激活一個窗口系統發送 WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。創建窗口時發送WM_CREATE消息。
BOOL CWinThread::PumpMessage()


{
ASSERT_VALID(this);
//如果是WM_QUIT就退出函數(return FALSE),這將導致程序結束.

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); //鍵轉換
::DispatchMessage(&m_msgCur); //派送消息
}
return TRUE;
}
BOOL CWinThread::PreTranslateMessage(MSG* pMsg)


{
ASSERT_VALID(this);

// 如果是線程消息,那么將會在消息映射表中找到消息入口,調用線程消息的處理函數
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
}
注:1.一般用PostThreadMessage函數發送線程之間的消息,他和窗口消息不同,需要指定線程id,消息激被系統放入到目標線程的消息隊列中;用 ON_THREAD_MESSAGE( message, memberFxn )宏可以映射線程消息和他的處理函數。這個宏必須在應用程序類(從CWinThread繼承)中,因為只有應用程序類才處理線程消息。如果你在別的類(比如視圖類)中用這個宏,線程消息的消息處理函數將得不到線程消息。
2.消息的目標窗口的PreTranslateMessage函數首先得到消息處理權,如果函數返回FALSE,那么他的父窗口將得到消息的處理權,直到主窗口;如果函數返回TRUE(表示消息已經被處理了),那么就不需要調用父類的PreTranslateMessage函數。這樣,保證了消息的目標窗口以及他的父窗口都可以有機會調用PreTranslateMessage,如果你想要消息不傳遞給父類進行處理的話,返回TRUE就行了
//對話框程序的消息循環
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++))
//發送WM_KICKIDLE消息,如果對話框中有狀態欄或是工具欄等,靠這個消息更新狀態,相當于OnIdle

{
// stop idle processing next time
bIdle = FALSE;
}
}

// phase2: pump messages while available
do

{
ASSERT(ContinueModal());

// pump message, but quit on WM_QUIT
//PumpMessage(消息泵)的實現和上面講的差不多。都是派送消息到窗口。
if (!AfxGetThread()->PumpMessage())

{
AfxPostQuitMessage(0);
return -1;
}

// 消息為WM_SYSTIMER或者WM_SYSKEYDOWN,并且空閑顯示標志為真的話,就顯示窗口并通知窗口立刻重繪。
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));
} //無限循環

ExitModal:
m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
return m_nModalResult;
}
posted on 2008-09-02 23:16
黑色天使 閱讀(638)
評論(0) 編輯 收藏 引用 所屬分類:
VC&MFC