科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件深入剖析MFC中Windows消息处理机制

深入剖析MFC中Windows消息处理机制

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础.

作者:mahongxi 来源:vczx 2007年10月19日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
当bidle=0或消息队例中有消息时,程序又执行到哪了呢?

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));

  看啊,又进入一个循环!

  其中有个重要的函数,PumpMessage,内容如下:

BOOL CWinThread::PumpMessage()
{
 ASSERT_VALID(this);

 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;
}

  如你所想,这才是MFC消息处理的核心基地[也是我个人认为的]。

  GetMessage不同于PeekMessae,它是不得到消息不罢体,PeekMessage如果发现消息队列中没有消息会返回0,而GetMessage如果发现没有消息,等,直到有了消息,而且,GetMessage不同于PeekMessage,它会将消息移走[当然,PeekMessage也可以做到这点]。我想当你读了这个函数后,你应明白PreTranslateMessage函数的用法了吧[我比较喜欢在程序中充分利用这个函数]。

::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);

  将消息发送到窗口的处理函数[它是由窗口类指定的],之后的动作一直到你的程序做出反映的过程,你可以在《深入》一书中得到完美的解释。我们还是通过reurn TRUE;回到CWinThread::Run()中的Do{}while;循环。然后还是对IDLE的处理,即便刚才你的ONIDLE返回了FALSE,在这里你看到,你的程序还是有机会执行它的。然后又是利用PeekMessage检测消息队列:

  如果有消息[这个消息不被移动的原因是因为它要为PumpMessage内的GetMessage所利用。]再次进入PumpMessage[叫它“消息泵”吧]。

  如果没有消息,退出DO循环,但它还在FOR内部,所以又执行第一个While循环。

  这是CwinThread::Run的一个执行过程。

  不用担心退不出for(;;)如果你的消息队列中有一条WM_QUIT,会使GetMessage返回0,然后PumpMessage返回0而RUN()内部:

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

  SDI就说到这,下面我来谈一下模式对话框。我分2种情况讨论:

  一当你的工程以模式对话框为基础时[没父窗口,或为桌面]。

  与SDI不同处在于,在应用程序类的InItInstance内部:

BOOL CComboBoxApp::InitInstance()
{
 AfxEnableControlContainer();
 // Standard initialization
 // If you are not using these features and wish to reduce the size
 // of your final executable, you should remove from the following
 // the specific initialization routines you do not need.
 #ifdef _AFXDLL
  Enable3dControls(); // Call this when using MFC in a shared DLL
 #else
  Enable3dControlsStatic(); // Call this when linking to MFC statically
 #endif
 this->m_nCmdShow = SW_HIDE;
 CComboBoxDlg 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;
}

  int nResponse = dlg.DoModal();一句使你的整个程序都在DoModal()内部进行。而且,你退出DoMal()时[你一定结束了你的对话框],InitInstance返回的是False,我们知道,这样,CwinThread::Run是不会执行的。

  但对话框程序是在哪里进行消息处理的呢。

  原来,dlg.DoModal()内部会调用CwinThread::RunModalLoop,它起到的作用和RUN()是一样的[当然内部有细小差别,请参考MSDN]!!!

  第二种情况,你是在SDI[或其它]程序中调用Dlg.DoModal() 产生了一模式对话框[有父窗口].

  这又是如何运作的呢?

  建了这样一个工程做为例子。

  SDI,在View中处理LBUTTONDOWN:

MyDLg.DoModal();

  MyDLg内有按钮,以惫后用.

  没有显示模式对话框前,消息处理一直在Cthread::Run()中进行.

  你单击后,程序执行点进入DoModal()内部的RunModalLoop,这又是一个消息处理机制.

  不过DoModal()中调用RunModalLoop,前会Disable掉它的父窗口.从RunModalLoop中出来后,再 Enable它.

  模式对话框和非模式对话框都是通过调用CreateDialogIndirect()产生创建对话框.那它和非模式对话框区别是什么造成的呢?

  1 模式对话框将父窗口DISABLE掉.

  我原以为被Disable的窗口是不接收消息的.但后来我马上发现我是错的.但,为什么你对被Disable的窗口进行KeyBorad,Mouse动作时,窗口没反映呢,我想,这可能是操作系统从中搞的鬼.我在本文一开始,就写出操作系统向窗口发送消息的过程,我想当它发现目标窗口处理Disabled状态时,不会将消息发送给它,但这不能说窗口不接收消息,其它程序[或它本身]发送给它的消息还是可以接收并处理的.

  2 模式对话框本身有消息处理机制 RunModalLoop.

  对以上两点加以实验.

  我在我的刚才建的项目中的模式对话框中加上一个BUTTON,其中加入如下代码:

OnButton1()
{
 GetParaent()->EnableWindow(1);
}

  单击,后我们发现,此时它已经不再表现为”模态”,我试着点击菜单,还是会作出正常反映.

  我想这此消息[对于父窗口的,如:菜单动作]的处理也应是在模式对话框中的RunModalLoop中进行处理的吧[这点我不能确定].

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章