MFC应用程序的初始化过程
来源:互联网 发布:c语言中char的取值范围 编辑:程序博客网 时间:2024/09/21 09:04
一个MFC应用程序的生命周期
(一)程序的进入点
MFC作为Win32 API的一种封装,它的程序进入点自然是WinMain。但是,这个WinMain也被封装起来,用户是看不到的,只是在编译器进行连接时会被自动连接。
下面我们就来寻找一下MFC程序被隐藏了的WinMain。搜索MFC的源文件,可以发现MFC的WinMain定义在 appmodul.cpp中。 此文件可以在VC的MFC src文件夹中找到
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
__in LPTSTR lpCmdLine, int nCmdShow)
...{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
__in LPTSTR lpCmdLine, int nCmdShow)
...{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
这里的名字虽然是_tWinMain但是我们使用“转到定义”菜单项跳转,会发现实际上这是一个宏:
#define _tWinMain wWinMain
作为测试我们在VS中新建一个SDI MFC工程,起名为Test,VC会自动生成五个类
CAboutDlg CMainFrame CTestApp CTestDoc CTestView
在appmodul.cpp中的WinMain中加入一个断点,然后运行程序。这时我们会发现程序在_tWinMain的断点停住,说明_tWinMain正是MFC程序包裹之下的WinMain
(二) WinMain的工作与生命周期
继续先前追溯,我们注意到,在Test.cpp中定义有一个全局的对象CTestApp theApp;而每一个MFC应用程序都有这样唯一的一个从CWinApp继承来的应用程序对象。由于这个对象是全局的,将在WinMain进入之前进行构造和初始化。
我们继续看看_tWinMain都进行了什么工作。在_tWinMain中只调用了 AfxWinMain函数,它 定义在WINMAIN.CPP中,部分代码如下:
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
CWinApp* pApp = AfxGetApp();
/*pThread和pApp实际是相同的,因为CWinApp继承自
CWinThread,它们最终都指向CTestApp */
CWinThread,它们最终都指向CTestApp */
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare) CTestApp.InitApplication 被调用
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations CTestApp.InitInstance被调用 初始化、显示窗口
if (!pThread->InitInstance())
...{
if (pThread->m_pMainWnd != NULL)
...{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd ");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run(); // 开始消息循环
我们看到,一开始定义的全局变量在这里发挥了作用,通过AfxGetThread函数程序获得了一个指向CWinApp对象的指针。这时便可以利用这个指针执行一些通用的初始化工作。
首先,MFC通过函数AfxEndDeferRegisterClass注册窗口类,这个函数在wincore.cpp中定义,在InitInstance函数中被调用。
MFC在注册窗口完成后,CMainFrame中的PreCreateWindow被调用。注意,要先手动调用其父类的PreCreateWindow。在PreCreateWindow返回之后窗口将被创建。窗口的Create函数在ProcessShellCommand函数中调用。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
...{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
return TRUE;
}
...{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
return TRUE;
}
综合《深入浅出MFC》第三章的内容,总结一下MFC程序的生命周期:
构造全局对象CWinApp => WinMain中通过AfxGetApp()得到指向该全局对象的指针 pApp => 调用
pApp->InitApplication() => 调用pApp->InitInstance() 在InitInstance中注册、显示窗口 => 调用run()函数开始消息循环
其中,CWinApp中有一个成员变量m_pMainWnd,在InitInstance中会new一个CWnd类给这个指针,然后会register并Create这个窗口。在Create中会先调用preCreateWindow函数来根据客户需求设置窗口参数。
再细节一点,在InitInstance中先调用AfxEndDeferRegisterClass注册窗口,然后再调用preCreateWindow。根据孙鑫的视频所讲,一般的顺序应是在preCreateWindow里注册窗口,但是MFC为了一些需要先注册的窗口再调用preCreateWindow。当然这是不影响结果的。
如果我们在preCreateWindow和AfxEndDeferRegisterClass中加入断点调试,会发现这两个函数被调用了很多次。这是因为包括toolbar,statebar在内的很多类都是CWnd的派生类,他们的产生和窗口的产生本质是一样的,都需要先调用preCreateWindow、AfxEndDeferRegisterClass等函数。作为区分,我们可以在断点处观察堆栈中的lpClassName参数,会发现它们指向了不同的类名。另外,CSDN上还有个解答详见:
http://topic.csdn.net/t/20031124/00/2487008.html
preCreateWindow的参数cs结构体和createWindow的参数类型完全一样,也就是说我们可以在preCreateWindow函数中通过修改cs来影响最终窗口的创建。
例如在preCreateWindow中设置cs.cx = 0 cs.cy = 0,则创建出的窗口大小是0
在窗口创建完成后,通过pThread->run()开始消息循环,一个MFC程序的初始化工作就基本完成了。
关于更详细和深入的讨论可以参考我转载的文章——《温故而知新,学习MFC框架如何创建的过程》
- MFC应用程序的初始化过程
- MFC的初始化过程
- MFC程序的初始化过程
- MFC之MFC的初始化过程
- RTEMS 应用程序的初始化过程分析
- MFC应用程序创建窗口的过程
- MFC应用程序创建窗口的过程
- MFC应用程序创建窗口的过程
- MFC应用程序的一些过程顺序
- MFC应用程序创建窗口的过程
- 深入浅出MFC:MFC的程序的初始化过程
- MFC初始化过程
- MFC 初始化详细过程
- MFC 应用程序初始化与消息传递的 生死因果
- MFC的初始化过程相关类及其成员。
- 探索MFC中单文档中m_pMainWnd的初始化过程
- 深入浅出MFC学习笔记:MFC六大关键技术仿真之MFC程序的初始化过程
- MFC学习笔记三——MFC程序的初始化过程
- 在QTP9.2中打开QTP9.5的脚本
- 民政部相关抗灾单位表示接受IBM捐助Sahana减灾系统
- JS调用C#后台代码---JS实现DataGrid“全选”、“反选”、调用后台代码批量删除数据
- 在AS里创建可视组件》关于AS组件
- hypertable原理
- MFC应用程序的初始化过程
- 第二人生的源码分析(六十九)使用LLXmlTree类来分析XML配置文件
- 涉及页面提交过程中的一些怪异问题记录
- MS为何叫座不叫好,还下面有阻力上面有压力
- about Trace32
- 【转】用户权限管理经典篇
- Eclipse的properties插件
- 永远的记忆--汶川地震
- 项目经理工作内容及指引