1. 无论是用win32 SDK编写的应用程序还是MFC应用程序,程序的入口都是WinMain;在MFC中程序的入口函数为:_tWinMain,这个函数被微软隐藏在源文件文件:AppModule.cpp中。
为了能找到MFC的运行流程的脉络,我们可以让程序处于调试模式,单步跟踪程序的运行状况。下面首先列举出将要跟踪的函数和类及其所在的源文件中。
函数————————————————>所在源文件
—————————————>
AfxWinMain—————————————>WinMain.cpp
CWinApp类—————————————>AppCode.cpp
CreateEx——————————————>WinCode.cpp(调用API函数CreateWindowEx,进行窗口创建,他是CreateWindow的扩展函数,同是API函数)
AfxEndDeferRegisterClass——————>WinCode.cpp(注册窗口类)
AfxDeferRegisterClass————————>AfxIMPL.h(因为这个函数是一个宏,他是在这个头文件中定义的,他实际上就是AfxEndDeferRegisterClass)
Create————————————————>WinFrm.cpp(调用CreateEx函数)
CFrameWnd::PreCreateWindow————>WinFrm.cpp(是虚函数。窗体创建前,在子类的PreCreateWindow中可以修改窗体的样式。在窗口创建前做一些准备工作。)
CFrameWnd::LoadFrame———————>WinFrm.cpp(调用Create函数,创建窗口)
这些文件的位置在:VC++环境安装位置\Microsoft Visual Studio\VC98\MFC\SRC,进入后可点击搜索——>“文件包含一个词或一个词组”,输入函数类容....
2. 首先创建SDI程序(单文档程序)作为测试程序。每一个MFC程序都有且仅有一个全局变量theApp,他表示当前应用程序模块。
整个MFC程序的执行顺序大致为:
首先是全局构造
CObject构造函数à CCmdTarget àCWinThreadàCWinAppà theApp构造函数
然后进入WinMain函数
WinMainàAfxWinMainàAfxWinInitàtheApp.InitApplicationàtheApp.InitInstance
接着执行线程过程。
theApp.Run()
最后清理
AfxWinTerm
我们首先进入文件AppModule.cpp,找到_tWinMain,函数定义为:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
发现就一个函数:AfxWinMain,我们在进入这个函数。他的主要定义如下:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
9
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
10
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
11
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
12
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
13
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
14
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
15
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
16
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
17
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
18
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
19
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
20
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
21
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
22
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
23
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
24
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
25
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
26
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
27
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
28
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
29
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
30
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
31
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
32
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
33
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
34
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
35
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
36
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
37
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
38
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
39
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
40
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
41
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
42
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
43
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
44
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
45
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
在AfxWinMain中,AfxWinInit和pApp->InitApplication(),主要做的事情是:初始化应用程序内部的环境,完成MFC自己内部的一些设置等。这个函数和外部没多大干系。应用程序的很多工作都是在pThread->InitInstance()中完成,类比上节课的知识,他主要做的事情是:
a.设计窗口类:WNDCLASS
b.注册窗口类:RegisterClass
c.创建窗口类:CreateWindow
d.显示窗口类:ShowWindow
e.更新窗口类:UpdateWindow
然后又到nReturnCode = pThread->Run();这是一个消息循环,处理系统消息。
有上面可以看出,在AfxWinMain中的执行流程和Win32下的流程差不多(第一节课讲的内容)。
3. 我们首先看pThread->InitInstance(),这是由虚函数,而pThread指向的是theApp,所以其处他所调用的是子类C***App类中的函数,C***App的InitInstance函数我们可以修改他,先看看他的Code大体是什么样子的:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
{
AfxEnableControlContainer()
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
SetRegistryKey(_T( " Local AppWizard-Generated Applications "));
LoadStdProfileSettings(); // Load standard INI file options (including MRU)
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
// 其处是将Doc类,CMainFrame,CView三个类用文档模板类关联到一起,在装载到代表当前应用程序的全局对象theApp中。
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
/*这个函数CWinApp::ProcessShellCommand主要完成的工作为:
a.设计窗口类:WNDCLAS
b.注册窗口类:RegisterClass
c.创建窗口类:CreateWindow
他不是一个虚函数,而且在CWinThread中没有这个成员函数,所以在其处调用的ProcessShellCommand一定是早已经在CwinApp中定义好的函数,他所应该完成的工作都已经定制好的了。
*/
// The one and only window has been initialized, so show and update it.
m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,调用API函数ShowWindow
m_pMainWnd->UpdateWindow();//更新窗口,调用API函数UpdateWindow
return TRUE;
}
说明:
对于上面代码中的函数ProcessShellCommand完成的功能,我个人分析是:
a.设计窗口类:WNDCLAS
b.注册窗口类:RegisterClass
c.创建窗口类:CreateWindow
他不是一个虚函数,而且在CWinThread中没有这个成员函数,所以在其处调用的ProcessShellCommand一定是早已经在CwinApp中定义好的函数,他所应该完成的工作都已经定制好的了。
这是我个人的观点,老师在视频中并没有说明。因为这三个工作肯定在C***App::InitInstance()中完成,通过跟踪,发现只可能在这里面完成的。
那么ProcessShellCommand是如何完成这个功能的呢? 首先可以肯定的调用线路是:CFrameWnd::LoadFrame()——>Cwnd::Create()——>Cwnd::CreateEx()——>::CreateWindowEx();其中“——>”表示调用关系。可见创建窗口主要是在CFrameWnd::LoadFrame()中完成的。
另外,我根据老师说明,从函数ProcessShellCommand进入进行跟踪,发现如下的一条线路通向CFrameWnd::LoadFrame(),在其列举出仅供参考,线路如下(其中“——>”表示调用关系):
ProcessShellCommand()——>OnCmd()——>AfxDispatchCmdMsg()——>pfn_Command()——>OFileNew()——>OpenDocumentFile()——>CreateNewFrame()——>LoadFrame()
在跟踪过程中,我发现系统多次调用PreCreateWindow、CreateEx、AfxEndDeferRegisterClass ,但是那好像都是都是注册工具栏、状态栏等的(实际上工具栏、状态栏等都是窗口),
没有发现注册主窗口,可能是跟丢了...
那现在看看CreateEx这个函数:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
9
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
10
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
11
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
12
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
13
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
14
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
15
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
16
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
17
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
18
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
19
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
20
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
21
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
22
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
23
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
24
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
25
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
26
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
27
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
28
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
29
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
30
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
31
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b99501c0ab8f77006783dde3ca54c4e3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b99501c0ab8f77006783dde3ca54c4e3.gif)
32
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
33
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
34
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
35
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
36
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
37
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
38
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
39
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
注意上面PreCreateWindow的功能。
我们在看看AfxEndDeferRegisterClass的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
{
// mask off all classes that are already registered
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
LONG fRegisteredClasses = 0;
// common initialization
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults
wndcls.lpfnWndProc = DefWindowProc; // 是窗口类的窗口过程为系统的默认窗口过程。在MFC中,对于外部用户,已不再使用win32中的消息循环了,用户正真接触到的是消息响应机制。
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hCursor = afxData.hcurArrow;
INITCOMMONCONTROLSEX init;
init.dwSize = sizeof(init);
// work to register classes as specified by fToRegister, populate fRegisteredClasses as we go
........
{
// Control bar windows
wndcls.style = 0; // control bars don't handle double click
wndcls.lpszClassName = _afxWndControlBar;
wndcls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
if (AfxRegisterClass(&wndcls)) // 注册窗口
fRegisteredClasses |= AFX_WNDCONTROLBAR_REG;
}
if (fToRegister & AFX_WNDCOMMCTL_TAB_REG)
{
init.dwICC = ICC_TAB_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TAB_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_PROGRESS_REG)
{
init.dwICC = ICC_PROGRESS_CLASS;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_PROGRESS_REG);
}
.......
}
接下来的类容就是一些零散的笔记了
1. 在Win32 SDK 或者MFC中,函数后面加"Ex"的一般表示为另一个函数的扩展函数,如CreateWindowEx为CreateWindow的扩展函数.
2. 在MFC中定义了一下用于应用程序框架类的全局函数,这些全局函数都是以AFX开头的,在程序中都可以调用.
3.一般我们看到的"一个窗口",实际上他可能有多个窗口组成,如单文档程序,里面的就有两个窗口:CMainFrame和CView.
4. AfxGetApp和AfxGetThread 返回的都是theApp对象.
5. 在MFC程序中当调用的API函数或者全局函数和MFC中的成员函数或者成员变量在名字上有冲突时,可以在API函数或者全局函数前面加上"::",表示他们是API函数或者全局函数;
6. 当一个窗口销毁时,CWnd对象不一定立即被销毁,因为销毁窗口(DestroyWindow())只是将窗口句柄置为NULL,而CWnd对象只是包含了一个窗口句柄,CWnd对象的成员句柄不知为空后,Cwnd对象可能还会存在。