《深入浅出MFC》这本书,对于VC程序员来说,应该算是如雷贯耳。此书对于MFC程序的来龙去脉阐述颇为精彩,但也许是出于描述的方便,在讲到“主窗口如何产生”这一话题时,侯老师将我们平常在VC下看到的CMyApp::InitInstance()修改了一下,如下所示:
//the original codes
BOOL CSellManagerApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControls()。否则,将无法创建窗口。
InitCommonControls();
CWinApp::InitInstance();
// 初始化 OLE 库
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU)
// 注册应用程序的文档模板。文档模板
// 将用作文档、框架窗口和视图之间的连接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CSellManagerDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CSellManagerView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 分析标准外壳命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 调度在命令行中指定的命令。如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
m_pMainWnd->UpdateWindow();
// 仅当存在后缀时才调用 DragAcceptFiles,
// 在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生
return TRUE;
}
//codes in <distectting the MFC>
BOOL CMyWinApp::InitInstance()
{
m_pMainWnd = new CMyFrameWnd();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
在侯老师的代码中有:“m_pMainWnd=new CMyFrameWnd(); ”。显然这也是很符合逻辑的,一个程序肯定是先创建好主窗口,然后才会处理其他的消息。于是,我就试图在FrameWork生成的代码中找类似的代码,显然上文中是没有任何的线索。我采取了这么一个办法。在InitInstance的开头加入:bool bIsNull=false;然后在原来的每行代码后添加:
if(NULL==m_pMainWnd)
bIsNull=true;
于是,如上的代码变成如下:
bool bIsNull=true;
InitCommonControls();
if(NULL==m_pMainWnd)
bIsNull=false;
CWinApp::InitInstance();
if(NULL==m_pMainWnd)
bIsNull=false;
//
...
//end
通过调试,我发现在执行“ProcessShellCommand(cmdInfo)”后,bIsNull发生了变化。于是设置断点,进行跟踪调试,最终发现在这个函数的执行过程存在着类似“m_pMainWnd=new CMyFrameWnd(); ”的代码。
现在,我来描述一下ProcessShellCommand:首先判断运行时系统传递的命令行参数,当我们双击应用程序(如"sellmanager.exe"),那么就是得到的消息是FileNew。对于FileNew,将是一连串函数调用,过程如下:
CWinApp::OnFileNew()->CDocManager::OnFileNew()->CSingleDocTemplate::OpenDocumentFile().在OpenDocumentFile中,程序判断m_pMainWnd是否为空,如果为空将创建CMainWnd对象,然后调用该对象的LoadFrame,由于我在程序中没有重写该函数,也就是调用CFrameWnd::LoadFrame,在LoadFrame中,将会看到:
if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,
pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))
{
return FALSE; // will self destruct on failure normally
}
对主窗口的诞生过程有了了解之后,就对编写程序有一定帮助。比如,我们做了一个管理软件,这中软件大多数是有登录对话框的,等用户输入正确的密码后,才会进入我们界面。那么,这个登录对话框要放在代码中的哪个位置呢?显然,可以把他放在InitInstance中,并将它放在ProcessShellCommand之前。为什么呢?因为,此时主窗口还尚未创建。