查看完整版本: MFC消息循环

Beverly 2008-10-15 17:35

MFC消息循环

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;
}
页: [1]
查看完整版本: MFC消息循环