Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1407177
  • 博文数量: 430
  • 博客积分: 9995
  • 博客等级: 中将
  • 技术积分: 4388
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-24 18:04
文章存档

2013年(1)

2008年(2)

2007年(14)

2006年(413)

分类:

2006-06-12 16:29:51

第二部分: 对话框程序的消息循环机制

  基于对话框的MFC工程和上面的消息循环机制不一样。实际上MFC的对话框工程程序就是模式对话框。他和上面讲到的非对话框程序的不同之处,主要在于应用程序对象的InitInstance()不一样。

//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; //定义一个对话框对象
 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;
}

   NOTE: InitInstance函数返回FALSE,由最上面程序启动流程可以看出,CWinThread::Run是不会得到执行的。也就是说,上面第一部分 说的消息循环在对话框中是不能执行的。实际上,对话框也有消息循环,她的消息循环在CDialog::DoModal()虚函数中的一个 RunModalLoop函数中。

  这个函数的实现体在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(消息泵)的实现和上面讲的差不多。都是派送消息到窗口。
   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));
 } //无限循环

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

  先说说怎么退出这个无限循环,在代码中:

if (!ContinueModal())
goto ExitModal;

  决定是否退出循环,消息循环函数返回也就是快要结束结束程序了。

BOOL CWnd::ContinueModal()
{
return m_nFlags & WF_CONTINUEMODAL;
}

  NOTE: CWnd::ContinueModal()函数检查对话框是否继续模式。返回TRUE,表示现在是模式的;返回FALSE,表示对话框已经不是模式(将要结束)。

   如果要结束对话框,在内部最终会调用函数CWnd::EndModalLoop,它取消m_nFlags的模式标志(消息循环中的 ContinueModal函数将返回FALSE,消息循环将结束,程序将退出);然后激发消息循环读取消息。也就是说,结束模式对话框是一个标志,改变 这个标志就可以了。他的代码是:

//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)是有用的。如果消息队列中没有消息的话,可能消息循环中的ContinueModal()不会马上执行,发送一个空消息是激发消息循环马上工作。

  下面说一下CWnd::RunModalLoop函数中的消息循环究竟干了些什么事情:

   1,第一个内循环。首先从消息队列中查询消息,如果对话框空闲,而且消息队列中没有消息,他做三件事情,大家应到都能从字面上明白什么意思。最重要的是 发送WM_KICKIDLE消息。为什么呢?第一部分讲到了,非对话框程序用OnIdle来更新用户界面(UI),比如工具栏,状态栏。那么,如果对话框 中也有工具栏和状态栏呢,在哪里更新(网上有很多这样的程序)?可以处理WM_KICKIDLE消息:

LRESULT CDlg_5Dlg::OnKickIdle(WPARAM w,LPARAM l)
{
//调用CWnd::UpdateDialogControls更新用户界面
UpdateDialogControls(this, TRUE);
return 0;
}

  NOTE: CWnd::UpdateDialog函数发送CN_UPDATE_COMMAND_UI消息给所有的用户界面对话框控件。

   2,第二个内循环。最重要的还是PumpMessage派送消息到目标窗口。其他的,像第二个if语句,0x118消息好像是WM_SYSTIMER消 息(系统用来通知光标跳动的一个消息)。也就是说,如果消息为WM_SYSTIMER或者WM_SYSKEYDOWN,并且空闲显示标志为真的话,就显示 窗口并通知窗口立刻重绘。

  总之,对话框的消息循环机制和非对话框(比如SDI,MDI)还是类似的,仅仅侧重点不同。模式对话框是模 式显示,自然有他的特点。下面部分讨论一下模式对话框和非模式对话框的区别。因为模式对话框有自己的特殊消息循环;而非模式对话框,共用程序的消息循环, 和普通的窗口已经没有什么大的区别了。
阅读(507) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~