讲述MFC AppWizard的原理与MFC程序框架的剖析。AppWizard是一个源代码生成工具,是计算机辅助程序设计工具,WinMain在MFC程序中是 如何从源程序中被隐藏的,theApp全局变量是如何被分配的,MFC框架中的几个类的作用与相互关系,MFC框架窗口是如何产生和销毁的,对窗口类的 PreCreateWindow和OnCreate两个函数的着重分析,Windows窗口与C++中的CWnd类的关系。
1,寻找WinMain入口:
在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。
WinMain在APPMODUL.CPP中实现:
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
注意:(#define _tWinMain WinMain)
2,对于全局对象或全局变量来说,在程序运行即WinMain函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。
所以:CTestApp theApp; -> CTestApp ::CTestApp(){} -> _tWinMain(){}
说
明:每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所实例化的对象,表示应用程序本身。在WIN32程
序当中,表示应用程序是通过WinMain入口函数来表示的(通过一个应用程序的一个实例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一
个应用程序对象,用它来唯一的表示了应用程序。
3,通过构造应用程序对象过程中调用基类CWinApp的构造函数,在CWinApp的构造函数中对程序包括运行时一些初始化工作完成了。
CWinApp类在APPCORE.CPP中实现:
CWinApp::CWinApp(LPCTSTR lpszAppName){…} //带参数,而CTestApp构造函数没有显式向父类传参,难道CWinApp()有默认参数?见下:
(在CWinApp类定义中, CWinApp(LPCTSTR lpszAppName = NULL); )
注意:CWinApp()函数中:
pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this
(this指向的是派生类CTestApp对象,即theApp)
调
试:CWinApp::CWinApp();->CTestApp theApp;(->CTestApp
::CTestApp())->CWinApp::CWinApp()->CTestApp
::CTestApp()->_tWinMain(){}
4,_tWinMain函数中通过调用AfxWinMain()函数来完成
它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,
我们可根据这些类函数来设计自己的应用程序)。
AfxWinMain()函数在WINMAIN.CPP中:
在AfxWinMain()函数中:
(1)CWinApp* pApp = AfxGetApp();
说明:pApp存储的是指向WinApp派生类对象(theApp)的指针。
//_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
// { return afxCurrentWinApp; }
(2)调用pApp->InitApplication():MFC类的一些内部初始化管理。
(3)调用pThread->InitInstance()
说明:pThread也指向theApp,由于基类中
virtual BOOL
InitInstance()定义为虚函数,所以调用pThread->InitInstance()的时候,调用的是派生类CTestApp的
InitInstance()函数。
(4)nReturnCode = pThread->Run();
说明:pThread->Run()完成了消息循环。
5,注册窗口类:AfxEndDeferRegisterClass();
AfxEndDeferRegisterClass()在WINCORE.CPP中实现:
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){…}
说明:
设计窗口类:在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。
调
试:
CWinApp::CWinApp();->CTestApp theApp;(->CTestApp
::CTestApp())->CWinApp::CWinApp()->CTestApp::CTestApp()->
_tWinMain(){}//进入程序
->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance() //父类InitInstance虚函数;
->CTestApp::InitInstance() //子类实现函数;
->AfxEndDeferRegisterClass(LONG fToRegister) //注册所选择的窗口类(出于文档管理,注册提前,正常的应在PreCreateWindow中进行注册)//之后进入创建窗口阶段(以下再不做调试)
6,CMainFrame的PreCreateWindow(): //主要是注册窗口类,以及在创建窗口之前让用户有机会对style进行修改
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
//在创建窗口之前让用户有机会对style进行修改,即修改cs
return TRUE;
}
说明:
CFrameWnd::PreCreateWindow()函数所在文件:WINFRM.CPP
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
//判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册
cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
//把注册后的窗口类名赋给cs.lpszClass
}
if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
cs.style |= FWS_PREFIXTITLE;
if (afxData.bWin4)
cs.dwExStyle |= WS_EX_CLIENTEDGE;
return TRUE;
}
其中:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs); //PreCreateWindow()是个虚函数,如果子类有则调用子类的。
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW; //WINCORE.CPP文件中,定义为全局数组。
//#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS(”FrameOrView”)
7,创建窗口:
这里只讲解了框架窗口的创建,它的Create()函数在WINFRM.CPP中:
CFrameWnd::Create(…){
…
CreateEx(…);//从父类继承来的,调用CWnd::CreateEx().
…
}
CWnd::CreateEx()函数在WINCORE.CPP中:
BOOL CWnd::CreateEx(…){
…
if (!PreCreateWindow(cs))//虚函数,如果子类有调用子类的。
{
PostNcDestroy();
return FALSE;
}
…
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);
…
}
说
明:CreateWindowEx()函数参数与CREATESTRUCT结构体参数有一 一对应关系,使我们在创建窗口之前可以通过
PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。
8,显示和更新窗口:
CTestApp类,TestApp.cpp中
m_pMainWnd->ShowWindow(SW_SHOW); //显示窗口,m_pMainWnd指向框架窗口
m_pMainWnd->UpdateWindow(); //更新窗口
说明:
class CTestApp : public CWinApp{…}
class CWinApp : public CWinThread{…}
class CWinThread : public CCmdTarget
{
…
public:
CWnd* m_pMainWnd;
…
}
9,消息循环:
int AFXAPI AfxWinMain()
{ …
// Perform specific initializations
if (!pThread->InitInstance()){…}
//完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新。
nReturnCode = pThread->Run();
//继承基类Run()方法,调用CWinThread::Run()来完成消息循环
…
}
////////////////////////////////////////////////////////////////
CWinThread::Run()函数在THRDCORE.CPP中
int CWinThread::Run()
{ …
// phase2: pump messages while available
do//消息循环
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())//取消息并处理
return ExitInstance();
…
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
…
}
说明:
BOOL PeekMessage(,,,,)函数说明
The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.
/////////////////////////////////////////////////////////////
BOOL CWinThread::PumpMessage()
{
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息
{…}
…
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur); //进行消息(如键盘消息)转换
::DispatchMessage(&m_msgCur); //将消息路由给操作系统,由相应的消息响应函数来处理
}
return TRUE;
}
9,文档与视结构:
可以认为CView类窗口是CMainFrame类窗口的子窗口。
CDocument类是文档类。
DOC-VIEW结构将数据本身与它的显示分离开。
文档类:数据的存储,加载
视类:数据的显示,修改
10,文档类,视类,框架类的有机结合:
在CTestApp类CTestApp::InitInstance()函数中通过文档模板将文档类,视类,框架类组织在一起。
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);//增加到文档模板
11,窗口类,窗口对象,窗口:
窗口是屏幕上的一块儿矩形区域,是一种计算机资源;窗口类是封装了对窗口的一系列操作的类,比如注册窗口,创建窗口,显示窗口,销毁窗口等等;窗口对象是窗口类的一个实例。
1、课程内容:
讲述MFC AppWizard的原理与MFC程序框架的剖析。AppWizard是一个源代码生成工具,是计算机辅助程序设计工具,WinMain在MFC程序中是 如何从源程序中被隐藏的,theApp全局变量是如何被分配的,MFC框架中的几个类的作用与相互关系,MFC框架窗口是如何产生和销毁的,对窗口类的 PreCreateWidow和OnCreate两个函数的着重分析,Windows窗口与C++中的CWnd类的关系
在D:\Program Files\VisualSdudio\VC\atlmfc\src\mfc 目录下可以找到appmodul.cpp文件,WinMain函数就在这里。
2. 窗口类,窗口对象与窗口关系
窗口类中定义一个类型为HWND成员变量,用来保存与之相关的窗口句柄值,可以用一个窗口类的实例即窗口对象来对应一个创建的窗口(是一种资源),窗口对象与窗口之间的关系是:
C++窗口类对象与窗口并不是一回事,它们之间唯一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄.窗口销毁时,与之对应的C++窗口类对象销毁与否,要看其生命周期是否结束,但C++窗口类对象销毁时,与之相关的窗口将销毁,因为它们之间的纽带(m_hWnd)已经断了,因此这时要回收窗口资源.
窗口销毁时调用DestroyWindow函数,窗口类对象销毁即将m_hWnd变量设置为NULL.
3.MFC流程介绍:WinMain->创建窗口类->注册窗口类->创建窗口->显示窗口->消息循环
以项目为例,我的项目名称为MFC02;在vs的安装目录下可以查找mfc核心函数的定义,如我的mfc目录为 D:\Program Files\VisualSdudio\VC\atlmfc\src\mfc\
1.从CMFC02App类开始运行:
a、声明全局变量CMFC02App theApp:子类调用父类的构造函数,进入CWinApp::CWinApp(LPCTSTR lpszAppName = NULL)对变量进行初始化;
这个构造函数的定义在 D:\Program Files\VisualSdudio\VC\atlmfc\src\mfc\appcore.cpp 中
b、进入_tWinMain函数(实际就是WinMain,并立即进入AfxWinMain函数中):产生了theApp对象后,在AfxWinMain()中的指针CWinThread *pThread和CWinApp *pApp就有了内容(theApp变量)。CWinApp派生于CWinAppThread,所以两个指针都可以指向theApp。
c、注册窗口类:
pThread->Initinstance() 初始化工作和注册窗口类,创建空口,窗口显示和更新,因为CWinApp派生于CWinAppThread,所以相当于调用theApp的Initinstance()方法;
对于单文档来说,要涉及文档管理,在这个Initinstance方法中完成窗口类的注册:
AfxEndDeferRegisterClass 判断是否注册过窗口类,MFC已经定义好了窗口类,所以只需要注册就可以啦;
AfxRegisterClass 注册窗口类;
d、创建窗口:CMainFrame是CFrameWnd的子类,调用基类的PreCreateWindow函数完成窗口类的注册(一般情况),单文档应用程序除外(见c说明)
CFrameWnd的Create函数创建窗口,它会调用CWnd的CreateEx函数;
e、显示和更新窗口:
CMainFrame的指针可以调用ShowWindow和UpdateWindow函数;
f、消息循环:CWinThread::Run
pThread->Run实现消息循环;
g、消息处理函数:消息映射机制;
2.MFC大致流程:
CTestApp theApp;//构造全局对象
WinMain()
{
AfxWinMain();//调用下面的函数
}
AfxWinMain()
{
pThread->Initinstance(); //初始化工作和注册窗口类,窗口显示和更新,因为CWinApp派生于CWinAppThread,所以相当于调用theApp的Initinstance()方法
pThread->Run(); //消息循环
}
而在BOOL CTestApp::InitInstance()中的代码
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMFC02Doc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CMFC02View));
AddDocTemplate(pDocTemplate);
完成了将这三个类关联起来的工作。
3.如何在单文档文件中显示一个CButton的对象?
在CMainFrame::OnCreate()中定义一个CButton的对象btn;
然后调用btn.Create("维新",WS_DISABLED |WS_CHILD | WS_VISIBLE | BS_AUTO3STATE, CRect(0,0,300,100),/*GetParent(),*/this,123);
注意点:
(1).此处btn不能是局部变量,否则它的生命周期太短,将不能显示。
(2).在CBUTTON类的Create函数的第二个参数中加入WS_VISIBLE 参数才行。否则必须调用ShowWindow也可以在view的OnCreate消息响应函数中加入
(3).CButton类的定义头文件在afxwin.h中,而stdafx.h包含了afxwin.h,所以可以直接使用。因为MFC中的每一个类中都有#include "stdafx.h"的声明。
浙公网安备 33010602011771号