📄 深入剖析mfc中windows消息处理、运行机制.htm
字号:
<BR>我首先简要谈一下SDI,然后会花更多文字描述模式对话框。
<BR>对于SDI窗口,你的应用程序类的InitInstance()大约如下: <BR>BOOL
CEx06aApp::InitInstance() <BR>{ …………… <BR>CSingleDocTemplate*
pDocTemplate; <BR>pDocTemplate = new CSingleDocTemplate(
<BR>IDR_MAINFRAME, <BR>RUNTIME_CLASS(CEx06aDoc),
<BR>RUNTIME_CLASS(CMainFrame), // main SDI frame window
<BR>RUNTIME_CLASS(CEx06aView));
<BR>AddDocTemplate(pDocTemplate); <BR>CCommandLineInfo
cmdInfo; <BR>ParseCommandLine(cmdInfo); <BR>if
(!ProcessShellCommand(cmdInfo)) <BR>return FALSE;
<BR>m_pMainWnd->ShowWindow(SW_SHOW);
<BR>m_pMainWnd->UpdateWindow(); <BR>return TRUE; <BR>}
<BR>完成一些如动态生成相关文档、视,显示主框架窗口、处理参数行信息等工作。这些都是显示在你工程中的“明码”。我们现在把断点设置到return
TRUE;一句,跟入MFC源码中,看看到底MFC内部做了什么。
<BR>程序进入SRC\WinMain.cpp,下一个大动作应是: <BR>nReturnCode =
pThread->Run(); <BR>各位看官注意了,重点来了。F11进入 <BR>int
CWinApp::Run() <BR>{ <BR>if (m_pMainWnd == NULL &&
AfxOleGetUserCtrl()) <BR>{ <BR>// Not launched /Embedding or
/Automation, but has no main window! <BR>TRACE0("Warning:
m_pMainWnd is NULL in CWinApp::Run - quitting
application.\n"); <BR>AfxPostQuitMessage(0); <BR>} <BR>return
CWinThread::Run(); <BR>} <BR>再次F11进入: <BR>int
CWinThread::Run() <BR>{ <BR>ASSERT_VALID(this); <BR><BR>// for
tracking the idle time state <BR>BOOL bIdle = TRUE; <BR>LONG
lIdleCount = 0; <BR><BR>// acquire and dispatch messages until
a WM_QUIT message is received. <BR>for (;;) <BR>{ <BR>//
phase1: check to see if we can do idle work <BR>while (bIdle
&& <BR>!::PeekMessage(&m_msgCur, NULL, NULL, NULL,
PM_NOREMOVE)) <BR>{ <BR>// call OnIdle while in bIdle state
<BR>if (!OnIdle(lIdleCount++)) <BR>bIdle = FALSE; // assume
"no idle" state <BR>} <BR><BR>// phase2: pump messages while
available <BR>do <BR>{ <BR>// pump message, but quit on
WM_QUIT <BR>if (!PumpMessage()) <BR>return ExitInstance();
<BR><BR>// reset "no idle" state after pumping "normal"
message <BR>if (IsIdleMessage(&m_msgCur)) <BR>{ <BR>bIdle
= TRUE; <BR>lIdleCount = 0; <BR>} <BR><BR>} while
(::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
<BR>} <BR><BR>ASSERT(FALSE); // not reachable <BR>}
<BR><BR>BOOL CWinThread::IsIdleMessage(MSG* pMsg) <BR>{ <BR>//
Return FALSE if the message just dispatched should _not_
<BR>// cause OnIdle to be run. Messages which do not usually
<BR>// affect the state of the user interface and happen very
<BR>// often are checked for. <BR><BR>// redundant
WM_MOUSEMOVE and WM_NCMOUSEMOVE <BR>if (pMsg->message ==
WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE) <BR>{
<BR>// mouse move at same position as last mouse move? <BR>if
(m_ptCursorLast == pMsg->pt && pMsg->message ==
m_nMsgLast) <BR>return FALSE; <BR><BR>m_ptCursorLast =
pMsg->pt; // remember for next time <BR>m_nMsgLast =
pMsg->message; <BR>return TRUE; <BR>} <BR><BR>// WM_PAINT
and WM_SYSTIMER (caret blink) <BR>return pMsg->message !=
WM_PAINT && pMsg->message != 0x0118; <BR>}
<BR>这是SDI处理消息的中心机构,但请注意,它觉对不是核心!
<BR>分析一下,在无限循环FOR内部又出现一个WHILE循环 <BR>while (bIdle &&
<BR>!::PeekMessage(&m_msgCur, NULL, NULL, NULL,
PM_NOREMOVE)) <BR>{ <BR>// call OnIdle while in bIdle state
<BR>if (!OnIdle(lIdleCount++)) <BR>bIdle = FALSE; // assume
"no idle" state <BR>}
<BR>这段代码是当你程序进程的消息队列中没有消息时,会调用OnIdle做一些后备工作,
<BR>临时对象在这里被删除。当然它是虚函数。其中的PeekMessage,是查看消息队列,如果有消息返回TRUE,如果没有消息返回FALSE,这里指定PM_NOREMOVE,是指查看过后不移走消息队列中刚刚被查看到的消息,也就是说这里的PeekMessage只起到一个检测作用,显然返回FALSE时[即没有消息],才会进入循环内部,执行OnIdle,当然了,你的OnIdle返回FLASE,会让程序不再执行OnIdle。你可能要问:
<BR>当bidle=0或消息队例中有消息时,程序又执行到哪了呢? <BR>do <BR>{ <BR>// pump
message, but quit on WM_QUIT <BR>if (!PumpMessage())
<BR>return ExitInstance(); <BR><BR>// reset "no idle" state
after pumping "normal" message <BR>if
(IsIdleMessage(&m_msgCur)) <BR>{ <BR>bIdle = TRUE;
<BR>lIdleCount = 0; <BR>} <BR><BR>} while
(::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
<BR><BR>看啊,又进入一个循环! <BR>其中有个重要的函数,PumpMessage,内容如下: <BR>BOOL
CWinThread::PumpMessage() <BR>{ <BR>ASSERT_VALID(this);
<BR><BR>if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
<BR>{ <BR>#ifdef _DEBUG <BR>if (afxTraceFlags &
traceAppMsg) <BR>TRACE0("CWinThread::PumpMessage - Received
WM_QUIT.\n"); <BR>m_nDisablePumpCount++; // application must
die <BR>// Note: prevents calling message loop things in
’ExitInstance’ <BR>// will never be decremented <BR>#endif
<BR>return FALSE; <BR>} <BR><BR>#ifdef _DEBUG <BR>if
(m_nDisablePumpCount != 0) <BR>{ <BR>TRACE0("Error:
CWinThread::PumpMessage called when not permitted.\n");
<BR>ASSERT(FALSE); <BR>} <BR>#endif <BR><BR>#ifdef _DEBUG
<BR>if (afxTraceFlags & traceAppMsg)
<BR>_AfxTraceMsg(_T("PumpMessage"), &m_msgCur); <BR>#endif
<BR><BR>// process this message <BR><BR>if (m_msgCur.message
!= WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
<BR>{ <BR>::TranslateMessage(&m_msgCur);
<BR>::DispatchMessage(&m_msgCur); <BR>} <BR>return TRUE;
<BR>} <BR>如你所想,这才是MFC消息处理的核心基地[也是我个人认为的]。
<BR>GetMessage不同于PeekMessae,它是不得到消息不罢体,PeekMessage如果发现消息队列中没有消息会返回0,而GetMessage如果发现没有消息,等,直到有了消息,而且,GetMessage不同于PeekMessage,它会将消息移走[当然,PeekMessage也可以做到这点]。我想当你读了这个函数后,你应明白PreTranslateMessage函数的用法了吧[我比较喜欢在程序中充分利用这个函数]。
<BR>::TranslateMessage(&m_msgCur);
<BR>::DispatchMessage(&m_msgCur);
<BR>将消息发送到窗口的处理函数[它是由窗口类指定的],之后的动作一直到你的程序做出反映的过程,你可以在《深入》一书中得到完美的解释。我们还是通过reurn
TRUE;回到CWinThread::Run()中的Do{}while;循环。然后还是对IDLE的处理,即便刚才你的ONIDLE返回了FALSE,在这里你看到,你的程序还是有机会执行它的。然后又是利用PeekMessage检测消息队列:
<BR>如果有消息[这个消息不被移动的原因是因为它要为PumpMessage内的GetMessage所利用。]再次进入PumpMessage[叫它“消息泵”吧]。
<BR>如果没有消息,退出DO循环,但它还在FOR内部,所以又执行第一个While循环。
<BR>这是CwinThread::Run的一个执行过程。
<BR>不用担心退不出for(;;)如果你的消息队列中有一条WM_QUIT,会使GetMessage返回0,然后PumpMessage返回0而RUN()内部:
<BR>if (!PumpMessage()) <BR>return ExitInstance();。
<BR><BR>SDI就说到这,下面我来谈一下模式对话框。我分2种情况讨论:
<BR>一当你的工程以模式对话框为基础时[没父窗口,或为桌面]。
<BR>与SDI不同处在于,在应用程序类的InItInstance内部: <BR>BOOL
CComboBoxApp::InitInstance() <BR>{
<BR>AfxEnableControlContainer(); <BR>// Standard
initialization <BR>// If you are not using these features and
wish to reduce the size <BR>// of your final executable, you
should remove from the following <BR>// the specific
initialization routines you do not need. <BR>#ifdef _AFXDLL
<BR>Enable3dControls(); // Call this when using MFC in a
shared DLL <BR>#else <BR>Enable3dControlsStatic(); // Call
this when linking to MFC statically <BR>#endif
<BR>this->m_nCmdShow = SW_HIDE; <BR>CComboBoxDlg dlg;
<BR>m_pMainWnd = &dlg; <BR>int nResponse = dlg.DoModal();
<BR>if (nResponse == IDOK) <BR>{ <BR>// TODO: Place code here
to handle when the dialog is <BR>// dismissed with OK <BR>}
<BR>else if (nResponse == IDCANCEL) <BR>{ <BR>// TODO: Place
code here to handle when the dialog is <BR>// dismissed with
Cancel <BR>} <BR>// Since the dialog has been closed, return
FALSE so that we exit the <BR>// application, rather than
start the application’s message pump. <BR>return FALSE; <BR>}
<BR><BR>int nResponse =
dlg.DoModal();一句使你的整个程序都在DoModal()内部进行。而且,你退出DoMal()时[你一定结束了你的对话框],InitInstance返回的是False,我们知道,这样,CwinThread::Run是不会执行的。
<BR>但对话框程序是在哪里进行消息处理的呢。
<BR>原来,dlg.DoModal()内部会调用CwinThread::RunModalLoop,它起到的作用和RUN()是一样的[当然内部有细小差别,请参考MSDN]!!!
<BR><BR>第二种情况,你是在SDI[或其它]程序中调用Dlg.DoModal() 产生了一模式对话框[有父窗口].
<BR><BR>这又是如何运作的呢? <BR>建了这样一个工程做为例子。
<BR>SDI,在View中处理LBUTTONDOWN: <BR>MyDLg.DoModal();
<BR>MyDLg内有按钮,以惫后用.
<BR><BR>没有显示模式对话框前,消息处理一直在Cthread::Run()中进行.
<BR>你单击后,程序执行点进入DoModal()内部的RunModalLoop,这又是一个消息处理机制.
<BR>不过DoModal()中调用RunModalLoop,前会Disable掉它的父窗口.从RunModalLoop中出来后,再
Enable它.
<BR>模式对话框和非模式对话框都是通过调用CreateDialogIndirect()产生创建对话框.那它和非模式对话框区别是什么造成的呢?
<BR>1 模式对话框将父窗口DISABLE掉.
<BR>我原以为被Disable的窗口是不接收消息的.但后来我马上发现我是错的.但,为什么你对被Disable的窗口进行KeyBorad,Mouse动作时,窗口没反映呢,我想,这可能是操作系统从中搞的鬼.我在本文一开始,就写出操作系统向窗口发送消息的过程,我想当它发现目标窗口处理Disabled状态时,不会将消息发送给它,但这不能说窗口不接收消息,其它程序[或它本身]发送给它的消息还是可以接收并处理的.
<BR>2 模式对话框本身有消息处理机制 RunModalLoop. <BR><BR>对以上两点加以实验.
<BR>我在我的刚才建的项目中的模式对话框中加上一个BUTTON,其中加入如下代码: <BR>OnButton1()
<BR>{ <BR>GetParaent()->EnableWindow(1); <BR>}
<BR>单击,后我们发现,此时它已经不再表现为”模态”,我试着点击菜单,还是会作出正常反映.
<BR>我想这此消息[对于父窗口的,如:菜单动作]的处理也应是在模式对话框中的RunModalLoop中进行处理的吧[这点我不能确定].
<BR><BR></TD></TR>
<TR vAlign=center align=right>
<TD vAlign=bottom bgColor=#ffffff><INPUT style="COLOR: #000000; BACKGROUND-COLOR: #ffffff" onclick=window.close() type=button value=关闭窗口><BR><BR></TD></TR>
<TR vAlign=center align=right>
<TD vAlign=center align=middle bgColor=#ffffff>
<TABLE id=AutoNumber20 style="BORDER-COLLAPSE: collapse"
borderColor=#111111 cellSpacing=0 cellPadding=0 width="99%"
align=center border=0>
<TBODY>
<TR>
<TD background=深入剖析MFC中Windows消息处理、运行机制.files/bg_dot.gif
colSpan=10 height=1></TD></TR></TBODY></TABLE></TD></TR>
<TR vAlign=center align=middle>
<TD bgColor=#ffffff><BR><FONT
color=red><B>特别声明:</B></FONT>文章版权归原作者所有, 未经允许请勿转载, 如有任何问题请<A
href="mailto:webmaster@vczx.com">联系我们</A>.
<BR><BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR><TR><TD
align="center" valign="middle">
<CENTER>
<TABLE id=AutoNumber20 style="BORDER-COLLAPSE: collapse" borderColor=#111111
cellSpacing=0 cellPadding=0 width="100%" align=center border=0>
<TBODY>
<TR>
<TD bgColor=#666666 colSpan=10 height=1></TD></TR></TBODY></TABLE>
<TABLE id=AutoNumber20 style="BORDER-COLLAPSE: collapse" borderColor=#111111
cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD width="100%"></TD></TR></TBODY></TABLE>
<TABLE borderColor=#799ccc cellSpacing=0 cellPadding=0 width="100%" align=center
bgColor=#799ccc border=0>
<TBODY>
<TR bgColor=#dedede>
<TD vAlign=center align=middle><BR>| <A class=d
href="http://www.vczx.com/"><FONT color=#000000>本站首页</FONT></A> | <A
class=d href="http://www.vczx.com/navigation.php" target=_blank><FONT
color=#000000>本站地图</FONT></A> | <A class=d
href="http://www.vczx.com/aboutus.php" target=_blank><FONT
color=#000000>关于本站</FONT></A> | <A class=d
href="http://www.vczx.com/copyright.php" target=_blank><FONT
color=#000000>版权声明</FONT></A> | <A class=d
href="http://www.vczx.com/link.php" target=_blank><FONT
color=#000000>友情链接</FONT></A> | <A class=d
href="http://www.vczx.com/ad.php" target=_blank><FONT
color=#000000>广告投放</FONT></A> | <A class=d
href="mailto:webmaster@vczx.com"><FONT color=#000000>联系我们</FONT></A>
|</FONT><BR><BR><B><FONT color=#ff0000>特别声明:</FONT><FONT
color=#636363></B>如本站所发布内容触犯了您的版权,请<A
href="mailto:webmaster@vczx.com"><B>来信</B></A>告诉我们,我们会立即删除。</FONT><BR><FONT
face=宋体 color=#636363>Copyright © 2003-2004 <B><FONT face=Verdana
color=#000000>vczx</FONT><FONT face=Verdana color=#ce0000>.com</FONT></B>
Inc. All rights reserved.</FONT> </TD></TR>
<TR bgColor=#dedede>
<TD vAlign=center align=middle><FONT face=宋体 color=#636363>版权所有 ©
2003-2004 vc在线 本站由loose_went制作维护</FONT><BR>
<SCRIPT language=JavaScript1.2> <!-- var correctwidth=1024 var correctheight=768 if (screen.width!=correctwidth||screen.height!=correctheight) document.write("<font color=#999999>本站最佳分辨率"+correctwidth+"*"+correctheight+",您当前的分辨率是"+screen.width+"*"+screen.height+".<br>设置合适的分辨率才能取得最佳的显示效果!</font>") //--> </SCRIPT>
</TD></TR></TBODY></TABLE></CENTER></TD></TR></TABLE></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -