MFC 就把有着相当固定行为之 WinMain 内部动作包装在 CWinApp 中,把有着相当固定行为之 WndProc 内部动作包装在 CFrameWnd 中。也就是说:
CWinApp 代表程序本体
CFrameWnd 代表一个框架窗口(Frame Window)
CwinApp——取代 WinMain 的地位
CWinApp 的派生对象被称为application object,可以想见,CWinApp 本身就代表一个程序本体。一个程序的本体是什么?回想第1章的SDK 程序,与程序本身有关而不与窗口有关的数据或动作有些什么?系统传进来的四个WinMain 参 数 算 不 算 ?InitApplication 和InitInstance 算不算?消息循环算不算?都算,是的,以下是MFC 4.x 的CWinApp声明(节录自AFXWIN.H):
几乎可以说CWinApp 用来取代WinMain在SDK程序中的地位。这并不是说MFC程序没有WinMain(稍后我会解释),而是说传统上 SDK 程序的 WinMain 所完成的工作现在由CWinApp 的三个函数完成:
virtual BOOL InitApplication();
virtual BOOL InitInstance();
virtual int Run();
WinMain 只是扮演役使它们的角色
CFrameWnd-取代 WndProc 的地位
CFrameWnd 主要用来掌握一个窗口,几乎你可以说它是用来取代SDK程序中的窗口函数的地位
在程序启动之初,由于theApp是全局的,CWinApp将首先调用构造函数,内部就是设置一些线程的this指针。
然后启动_tWinMain函数,继而调用AfxWinMain
AfxWinMain主要实现:
CWinApp* pApp = AfxGetApp();
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();
其实就相当于调用:
CMyWinApp::InitApplication();
CMyWinApp::InitInstance();
CMyWinApp::Run();
因而导至调用:
CWinApp::InitApplication();//因为CMyWinApp并没有改写InitApplication
CMyWinApp::InitInstance(); //因为 CMyWinApp改写了InitInstance
CWinApp::Run(); //因为 CMyWinApp并没有改写 Run
AfxWinMain流程:
AfxWinInit->AFX 内部初始化动作,主要是创建一个线程,该线程会设置窗口钩子
CWinApp::InitApplication:做些内部管理动作
CDialog::DoMdal(或者CFrameWnd的Create):注册创建窗口
Run():内部即是消息循环
来龙去脉总整理
前面各节的目的就是如何将表面上看来不知所以然的MFC程序对映到我们在SDK程序设计中学习到的消息流动观念,从而清楚地掌握MFC程序的诞生与死亡。让我对MFC程序的来龙去脉再做一次总整理。
程序的诞生:
Application object 产生,内存于是获得配置,初值亦设立了。
AfxWinMain 执行AfxWinInit,后者又调用AfxInitThread,把消息队列尽量加大到96。
AfxWinMain执行InitApplication。这是CWinApp 的虚函数,但我们通常不改写它。
AfxWinMain执行InitInstance。这是CWinApp的虚函数,我们必须改写它。
CMyWinApp::InitInstance 'new' 了一个CMyFrameWnd 对象。
CMyFrameWnd 构造函数调用Create,产生主窗口。我们在Create 参数中指定的窗口类是NULL, 于是MFC根据窗口种类 , 自行为我们注册一个名为"AfxFrameOrView42d" 的窗口类。
回到InitInstance中继续执行ShowWindow,显示窗口。
执行UpdateWindow,于是发出WM_PAINT。
回到AfxWinMain,执行Run,进入消息循环。
程序开始运作:
程序获得WM_PAINT 消息(藉由CWinApp::Run 中的::GetMessage 循环)。
WM_PAINT经由::DispatchMessage送到窗口函数CWnd::DefWindowProc 中。
CWnd::DefWindowProc 将消息循环过消息映射表格(Message Map)。
循环过程中发现有吻合项目,于是调用项目中对应的函数。此函数是应用程序利用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏设立起来的。
标准消息的处理例程亦有标准命名,例如WM_PAINT必然由OnPaint处理。
如果你的MFC 程序也想处理idle time,只要改写CWinApp 派生类的 OnIdle 函数即可。这个函数的类型如下:
virtual BOOL OnIdle(LONG lCount);
lCount是系统传进来的一个值,表示自从上次有消息进来,到现在,OnIdle 已经被调用了多少次