作者:liguisen
blog:http://blog.csdn.net/liguisen/
上一篇我们已经分析了MFC的简单执行流程:
Test1.cpp:
#include .....
CTest1App theApp;/*第1步*/
BOOL CTest1App::InitInstance()
{/*第2步*/
CTest1Dlg dlg;/*第3步*/
dlg.DoModal();/*第6步*/
return FALSE;/*第7步*/
}/*第8步*/
Test1Dlg.cpp:
#include .....
CTest1Dlg::CTest1Dlg(CWnd* pParent )
: CDialog(CTest1Dlg::IDD, pParent)
{/*第4步*/
}/*第5步*/
在我进一步分析的时候,发现MFC基于Dialog based的工程竟然是一个胎死腹中的怪胎!证据就是在InitInstance()函数中永远return FALSE,永远初始化失败!这导致我无法继续分析一个较为完整的MFC流程,例如theApp的构造,线程的Run等等。现在暂且简单分析一下这个对话框工程,在下一篇基于SDI的工程里面再好好分析吧!
Come on!
注://zuilang开头的是我加的注释。
现在进一步分析,如上一篇那样加上断点,现在你应该有经验了,知道那些地方可以加断点。好,F5开始(对了,环境是vc6),一直到第4步,然后,换F11(单步执行,碰到子函数进入内部),哈哈,进入MFC源码DLGCORE.CPP(core是什么意思?核心!再次强调英文的重要性)。
CDialog::CDialog(UINT nIDTemplate, CWnd* pParentWnd)
{//光标停在这里了
AFX_ZERO_INIT_OBJECT(CWnd);
m_pParentWnd = pParentWnd;
m_lpszTemplateName = MAKEINTRESOURCE(nIDTemplate);
m_nIDHelp = nIDTemplate;
}
上面这段代码应该不难看懂,就是CDialog的构造函数,再次说明了我们的对话框父类是CDialog,并且也知道了构造函数在什么时候执行。
再按F11,进入WINCORE.CPP:
CWnd::CWnd()
{//光标停在这里
AFX_ZERO_INIT_OBJECT(CCmdTarget);
}
说明CDialog的父类是CWnd。
继续F11,进入CMDTAGE.CPP:
CCmdTarget::CCmdTarget()
{//光标停在这里
// capture module state where object was constructed
#ifdef _AFXDLL
m_pModuleState = AfxGetModuleState();
ASSERT(m_pModuleState != NULL);
#endif
// initialize state
#ifndef _AFX_NO_OLE_SUPPORT
m_dwRef = 1;
m_pOuterUnknown = NULL;
m_xInnerUnknown = 0;
m_xDispatch.m_vtbl = 0;
m_bResultExpected = TRUE;
m_xConnPtContainer.m_vtbl = 0;
#endif
}
呵呵,MFC是不是开始复杂了?就此停住,你有兴趣可以继续深入进去。我们现在使用F10一直到第6步。
再次使用F11,重新进入DLGCORE.cpp,这里不准备详细解释每一句代码,其他问题你可以自己研究(例如:对话框面板资源是如何和程序联系在一起的?你若觉得吃力,除了我注释的代码,其它代码完全可以当它是透明的)
int CDialog::DoModal()
{//光标停在这里
// can be constructed with a resource template or InitModalIndirect
ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL ||
m_lpDialogTemplate != NULL);
// load resource as necessary
LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;
HGLOBAL hDialogTemplate = m_hDialogTemplate;
HINSTANCE hInst = AfxGetResourceHandle();
if (m_lpszTemplateName != NULL)
{
hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
hDialogTemplate = LoadResource(hInst, hResource);
}
if (hDialogTemplate != NULL)
lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);
// return -1 in case of failure to load the dialog template resource
if (lpDialogTemplate == NULL)
return -1;
// disable parent (before creating dialog)
HWND hWndParent = PreModal();
AfxUnhookWindowCreate();
BOOL bEnableParent = FALSE;
if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
{
::EnableWindow(hWndParent, FALSE);
bEnableParent = TRUE;
}
TRY
{
// create modeless dialog//zuilang:创建非模态(或者非模式,总之是modeless)对话框,你也许觉得很奇怪,明明是模态对话框啊,其实可以这么理解,模态对话框是非模态对话框的一种特殊形式。
AfxHookWindowCreate(this);
if (CreateDlgIndirect(lpDialogTemplate,
CWnd::FromHandle(hWndParent), hInst))
{
if (m_nFlags & WF_CONTINUEMODAL)
{
// enter modal loop //zuilang:模态对话框在这里得到体现
DWORD dwFlags = MLF_SHOWONIDLE;
if (GetStyle() & DS_NOIDLEMSG)
dwFlags |= MLF_NOIDLEMSG;
VERIFY(RunModalLoop(dwFlags) == m_nModalResult);//zuilang:进入模态对话框的循环,按F10到这里后换F11,然后进入WINCORE.CPP,请在这一句后先看本文下面标有“开始循环”的地方。回到这里来^_^,F10直到程序退出。
}
// hide the window before enabling the parent, etc.
if (m_hWnd != NULL)
SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|
SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
}
}
CATCH_ALL(e)
{
DELETE_EXCEPTION(e);
m_nModalResult = -1;
}
END_CATCH_ALL
if (bEnableParent)
::EnableWindow(hWndParent, TRUE);
if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
::SetActiveWindow(hWndParent);
// destroy modal window
DestroyWindow();
PostModal();
// unlock/free resources as necessary
if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)
UnlockResource(hDialogTemplate);
if (m_lpszTemplateName != NULL)
FreeResource(hDialogTemplate);
return m_nModalResult;
}
开始循环:
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 (;;)//zuilang:终于进入窗体的无限循环结构了(到这里你需要windows程序的相关背景知识--消息循环),看上面的英文就可以知道,这是用来处理消息的,即使对于有一定MFC经验的初学者,相信到了这里也会有豁然开朗的感觉。
{
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
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;//zuilang:到了这里,F10是再也出不去了,因为会不断的有消息循环,我们没有机会到对话框上退出程序,在下面ExitModal处加个断点,然后按F5继续运行。
// 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);//zuilang:这里加断点,到这里后按F10继续,一直回到RunModalLoop函数
return m_nModalResult;
}
至此,程序结束,我们发现,基于Dialog based的工程不过是在整个程序实例初始化的过程当中显示了一个窗体而已,这个事实告诉我们:我们可以在这个初始化过程当中显示多个窗体:
BOOL CTest1App::InitInstance()
{
CTest1Dlg dlg;
dlg.DoModal();
CTest1Dlg dlg2;
dlg2.DoModal();
return FALSE;
}
并且,这些窗体(在这里是dlg2)可以是其他对话框类(尽管我在这里演示的仍然是CTest1Dlg类),因此,我们应该可以想到,这个对话框类可以用来做其他事情,例如用户登录对话框。而我,确实就是这么做的。
基于Dialog based的工程暂时就分析到这里,现在,最起码你应该对派生、继承、构造函数等有一个感性认识,现在去翻一翻c++的基础书,相信你的印象会更加深刻。暂时不考虑给它增肥,保留这个工程,等我分析了基于SDI的工程后再来增肥。