原文出自:http://blog.csdn.net/yuvmen/article/details/5877271
了解MFC程序的启动过程,对于初学者来讲,了学习MFC很有帮助;对于不常用VC的人来说,过一段时间就会忘记。还是来记下来,方便以后查阅。
1、创建Application object对象theApp
程序一开始生产一个(且只有一个)Application object对象theApp,也即一个CWinApp对象,这个全局对象一产生,便执行其构造函数,因为并没有定义CMyWinApp构造函数,所以即执行CWinApp类的构造函数。该函数定义于APPCORE.CPP第75行,你可以自己搜出来啃一啃,因此,CWinApp之中的成员变量将因为theApp这个全局对象的诞生而获得配置与初值。
2、WinMain登场
用SDK编程序时,程序的入口点是WinMain函数,而在MFC程序里我们并没有看到WinMain函数,哦!~ 原来她是被隐藏在MFC代码里面了。当theApp配置完成后,WinMain登场,慢!细看程序,并没连到WinMain函数的代码啊!这个我也不知道,MFC早已准备好并由链接器直接加到应用程序代码中了,原来她在APPMODUL.CPP里面,好,我们就认为当theApp配置完成后,程序就转到APPMODUL.CPP来了。那执行什么呢?看看下面从APPMODUL.CPP摘出来的代码:
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
_tWinMain函数的“_t”是为了支持Unicode而准备的一个宏。
_tWinMain函数返回值是AfxWinMain函数的返回值,AfxWinMain函数定义于WINMAIN.CPP第21行,稍加整理,去芜存菁,就可以看到这个“程序进入点”主要做些什么事:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
int nReturnCode = -1;
CWinApp* pApp = AfxGetApp();
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
pApp->InitApplication();
pApp->InitInstance()
nReturnCode = pApp->Run();
AfxWinTerm();
return nReturnCode;
}
AfxGetApp()函数是取得CMyWinApp对象指针,故上面函数第6至8行相当于调用:
CMyWinApp::InitApplication();
CMyWinApp::InitInstance()
CMyWinApp::Run();
因而导致调用:
CWinApp::InitApplication(); //因为 CMyWinApp 并没有改写 InitApplication
CMyWinApp::InitInstance() //因为 CMyWinApp 改写了 InitInstance
CWinApp::Run(); //因为 CMyWinApp 并没有改写 Run
用过SDK写程序的朋友,现在可能会发出会心的微笑。
3、AfxWinInit——AFX内部初始化操作
AfxWinInit是继CWinApp构造函数之后的第一个操作,主要做的是AFX内部初始化操作,该函数定义于APPINIT.CPP第24行,这里就不掏出来了,你自己搜出来啃吧!
4、执行CWinApp::InitApplication
AfxWinInit之后的操作是pApp->InitApplication,我们已知道pApp指向CMyWinApp对象,当调用:
pApp->InitApplication();
相当于调用:
CMyWinApp::InitApplication();
但是你要知道,CMyWinApp继承自CWinApp,而InitApplication又是CWinApp的一个虚拟函数,我们并没有改写它(大部分情况下不需改写它),所以上述操作相当于调用:
CWinApp::InitApplication();
此函数定义于APPCORE.CPP第125行,你自己搜出来看吧!我就不搬出来了,里面的操作都是MFC为了内部管理而做的(其实我也看不懂,知道有这回事就好了)。
5、执行CWinApp::InitInstance
继InitApplication函数之后,AfxWinMain调用pApp->InitInstance。当程序调用:
pApp->InitInstance();
相当于调用:
CMyWinApp::InitInstance();
但是你要知道,CMyWinApp继承自CWinApp,而InitInstance又是CWinApp的一个虚拟函数。由于我们改写了它,所以上述操作就是调用我们自己(CMyWinApp)的这个InitInstance函数。
6、CFrameWnd::Create产生主窗口(并先注册窗口类)
现在已经来到CWinApp::InitInstance了,该函数先new一个CMyFrameWnd对象,从而产生主窗口。在创建CMyFrameWnd对之前,要先执行构造函数CMyFrameWnd::CMyFrameWnd(),该函数用Create函数产生窗口:
CMyFrameWnd::CMyFrameWnd()
{
Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, "MainMenu");
}
其中Create是CFrameWnd的成员函数,它将产生一个窗口,用过SDK编程序的朋友都知道,要创建主窗口时要先注册一个窗口类,规定窗口的属性等,但,这里使用哪一个窗口类呢?Create函数第一个参数(其它参数请参考MSDN或《深出浅出MFC》详解)指定窗口类设为NULL又是什么意思啊?意思是要以MFC内建的空中类产生一个标准的外框窗口,但,我们的程序一般都没有注册任何窗口类呀!噢,Create函数在产生窗口之前会引发窗口类的注册操作。
让我们先挖出Create函数都做了些什么操作,Create函数定义于WINFRM.CPP的第538行(在此我就不把代码Copy过来了,你自己打开出来看吧),函数在562行调用CreateEx函数,由于CreateEx是CWnd的成员函数,而CFrameWnd是从CWnd继而来,故将调用CWnd::CreateEx。此函数定义于WINCORE.CPP第665行,下面是部分代码:
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
// allow modification of several common create parameters
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hWndParent;
cs.hMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle();
cs.lpCreateParams = lpParam;
if(PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this);
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
...
}
用过SDK编程序的朋友,看到上面代码应该有一点感觉了吧,函数中调用的PreCreateWindows是虚拟函数,在CWnd和CFrameWnd之中都有定义。由于this指针所指对象的缘故,这里应该调用的是CFrameWnd::PreCreateWindow。该函数定义于WINFRM.CPP第521行,以下是部分代码:
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
}
...
}
其中AfxDeferRegisterClass是一个定义于AFXIMPL.H中的宏。该宏如下:
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
注:这里有宏和《深入浅出MFC》的不一样,以上代码是从Visual C++ 6.0摘取。
AfxEndDeferRegisterClass定义于WINCORE.CPP第3619行,该函数很复杂,主要是注册五个窗口类(哇!终于看到窗口类了,怎么用5个呢?我还不清楚),不同类的PreCreateWindow成员函数都是在窗口产生之前一刻被调用,准备用来注册窗口类。如果我们指定的窗口类是NULL,那么就使用系统默认类。从CWnd及其各个派生类的PreCreateWindow成员函数可以看出,整个Framework针对不同功能的窗口使用了哪些窗口类。
7、窗口显示与更新
CMyFrameWnd::CMyFrameWnd结束后,窗口已经诞生出来;程序流程又回到CMyWinApp::InitInstance,于是调用ShowWindow函数令窗口显示出来,并调用UpdateWindow函数令程序送出WM_PAINT消息。在SDK程序中,消息是通过窗口函数来处理,而现在窗口函数在哪里、又如何送到窗口函数手中呢?那要从CWinApp::Run说起了。
8、执行CWinApp::Run——程序生命的活水源头
在执行完CMyWinApp::InitInstance函数后,程序的脚步到了AfxWinMain函数的pApp->Run了,现在我们已知道这将执行CWinApp::Run函数,该函数定义于APPCORE.CPP第391行,下面是程序代码:
int CWinApp::Run()
{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
{
// Not launched /Embedding or /Automation, but has no main window!
TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application./n");
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
函数调用CWinThread::Run函数,该函数定义于THRDCORE.CPP第456行,在这里我就不Copy出来了。函数在第480行调用了PumpMessage函数,该函数定义于THRDCORE.CPP第810行,整理后的部分代码如下:
BOOL CWinThread::PumpMessage()
{
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
{
return FALSE;
}
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
该函数主要操作是将消息由::DispatchMessage送到窗口函数(CWnd::DefWindowProc)中,但程序一般没有提供任何窗口函数,但在AfxEndDeferRegisterClass中,在注册五种窗口类之前已经指定窗口函数为:
wndcls.lpfnWndProc = DefWindowProc;
虽然窗口函数被指定为DefWindowProc成员函数(CWnd::DefWindowProc),但事实上消息并不是被唧往该处,而是一个名为AfxWndProc的全局函数去。
9、把消息与处理函数连接在一起——Message Map机制
到此,主窗口已经产生,等待的就是各种消息了,然后调用相应的处理函数,然而消息和处理函数怎样连接在一起呢?MFC采用了Message Map机制(消息映射机制),提供给应用程序使用的“很方便的接口”的两组宏,其原理我还不大清楚,在这里也无法讲解,主要用法是:先在类声明中结合DECLARE_MESSAGE_MAP()给出处理函数,如:
class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd();
afx_msg void OnPaint(); // for WM_PAINT
afx_msg void OnAbout(); // for WM_COMMAND (IDM_ABOUT)
DECLARE_MESSAGE_MAP()
}
再在相应的.CPP文件的任何位置(当然不能在函数之内)使用BEBIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏把相应的消息加入去,如:
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_COMMAND(IDM_ABOUT, OnAbout)
ON_WM_PAINT()
END_MESSAGE_MAP()
总结一下:
(1) 调用CXXApp构造函数构造全局对象theApp: CXApp theApp;
(2) 调用Winmain函数完成初始化工作: 通过宏_tWinMain
(3) 初始化工作包括: 窗口类注册、窗口产生、显示和更新、消息循环等等
① 注册窗口类:AfxEndDeferRegisterClass() //相当于SDK里面的RegisterClass()函数
② 创建窗口:CMainFrame::PreCreateWindow() //允许我们修改窗口属性的地方
CFrameWnd::Create()
③ 消息循环:PumpMessage()
在程序运行时CWinApp将创建一个CFrameWnd框架窗口实例,而框架窗口将创建文档模板,然后有文档模板创建文档实例和视实例,并将两者关联。
相关推荐
6、MFC程序具有一个CTestApp类的全局对象theApp,在MFC程序运行时,程序执行的顺序为:theApp全局对象定义 处->CTestApp构造函数->WinMain函数 7、对于普通的VC++控制台程序,无论全局变量还是全局对象,程序运行时...
3.2.2 构造函数与析构函数 3.2.3 静态成员 3.2.4 this指针 3.2.5 友元函数 3.3 类的继承机制 3.3.1 基类与派生类概念 3.3.2 单一派生 3.3.3 多重继承 3.3.4 虚基类 3.4 多态性与虚函数 3.5 抽象类与纯虚函数 3.6 C++...
3.2.2 构造函数与析构函数 3.2.3 静态成员 3.2.4 this指针 3.2.5 友元函数 3.3 类的继承机制 3.3.1 基类与派生类概念 3.3.2 单一派生 3.3.3 多重继承 3.3.4 虚基类 3.4 多态性与虚函数 3.5 抽象类与纯虚函数 3.6 C++...
11.4.2 MFC程序的组织方式 11.5使用Windows Forms 11.6小结 第12章 使用MFC编写Windows程序 12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序...
11.4.2 MFC程序的组织方式 11.5使用Windows Forms 11.6小结 第12章 使用MFC编写Windows程序 12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序...
11.4.2 MFC程序的组织方式 11.5使用Windows Forms 11.6小结 第12章 使用MFC编写Windows程序 12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序...
11.4.2 MFC程序的组织方式 11.5使用Windows Forms 11.6小结 第12章 使用MFC编写Windows程序 12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序...
11.4.2 MFC程序的组织方式 11.5使用Windows Forms 11.6小结 第12章 使用MFC编写Windows程序 12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序...
11.4.2 MFC程序的组织方式 11.5使用Windows Forms 11.6小结 第12章 使用MFC编写Windows程序 12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序...
Ivor Horton是世界著名的计算机图书作家,主要从事与编程相关的顾问及撰写工作,曾帮助无数程序员步入编程的殿堂。他曾在IBM工作多年,能使用多种语言进行编程(在多种机器上使用汇编语言和高级语言),设计和实现了...
8.1.3 析构函数与动态内存分配 366 8.2 实现复制构造函数 369 8.3 在变量之间共享内存 370 8.3.1 定义联合 371 8.3.2 匿名联合 372 8.3.3 类和结构中的联合 372 8.4 运算符重载 373 8.4.1 实现重载的运算符 ...
10.3 构造函数和析构函数 .119 10.4 小 结 .122 第十一章 方 法 .124 11.1 方法的声明.124 11.2 方法中的参数.125 11.3 静态和非静态的方法.129 11.4 方法的重载.130 11.5 操作符重载.134 11.6 小 ...
我们创建一个MFC应用程序,对话框模式,照上面的说明,设置工程包含我们上面得到的oSIP的相关开发库及SDK的一些开发库,并且由于默认LIBC的冲突,需要排除MSVCRT[D]开发库(其中D代表Debug模式...