VC深入详解讲解.docx
- 文档编号:20135996
- 上传时间:2023-04-25
- 格式:DOCX
- 页数:231
- 大小:2.32MB
VC深入详解讲解.docx
《VC深入详解讲解.docx》由会员分享,可在线阅读,更多相关《VC深入详解讲解.docx(231页珍藏版)》请在冰豆网上搜索。
VC深入详解讲解
第三章MFC框架剖析
3.2基于MFC的程序框架剖析
MFCAppWizard帮助我们生成的代码,单击左边工作区窗格中的ClassView(类视图)标签页,可以看到如图的5个类:
3.2.1MFC程序中的WinMain函数
Win32程序的一个主线:
1、进入WinMain函数;2、设计窗口类;3、注册窗口类;4、产生窗口;5、注册窗口;6、显示窗口;7、更新窗口;8、消息循环,将消息路由到窗口过程函数中去处理。
1.theApp全局对象
在程序入口main函数加载时,系统已经为全局变量或者全局对象分配了存储空间,并为他们赋初值。
如何查看变量的值?
1、将鼠标移动到该变量上停留片刻,VC++会弹出一个小窗口显示变量的值;2、点击View,在下拉菜单中选择variables即可。
为什么要定义一个全局对象theApp,让它在WinMain之前执行?
其作用是什么?
应用程序的实例是由实例句柄(WinMain函数的参数hInstance)来标识的。
对于MFC程序,通过产生一个应用程序类的对象来唯一标识应用程序的实例。
每一个MFC仅有一个从应用程序(CWinApp)派生的类。
每一个MFC仅有一个该派生类的实例化对象,也就是theApp全局对象。
在一个子类在构造函数前会先调用其父类的构造函数。
TheApp对象的构造函数CTestApp在调用之前,会调用其父类CWinApp的构造函数。
2.AfxWinMain函数
当程序调用了CWinApp类的构造函数,并执行了CTestApp的构造函数,且产生了TheApp对象之后,接下来就是进入了WinMain函数。
实际上,WinMain函数是通过.AfxWinMain函数来完成他的功能的。
intAFXAPIAfxWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,intnCmdShow)
{
ASSERT(hPrevInstance==NULL);
intnReturnCode=-1;
①CWinThread*pThread=AfxGetThread();
CWinApp*pApp=AfxGetApp();
//注:
实质上上述2个指针是一致的,AfxaGetApp是一个全局函数,定义于AFXWIN1.INL中。
//AFXinternalinitialization
if(!
AfxWinInit(hInstance,hPrevInstance,lpCmdLine,nCmdShow))
gotoInitFailure;
//Appglobalinitializations(rare)
②if(pApp!
=NULL&&!
pApp->InitApplication())
gotoInitFailure;
//Performspecificinitializations
③if(!
pThread->InitInstance())
{
if(pThread->m_pMainWnd!
=NULL)
{
TRACE0("Warning:
Destroyingnon-NULLm_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode=pThread->ExitInstance();
gotoInitFailure;
}
④nReturnCode=pThread->Run();
InitFailure:
#ifdef_DEBUG
//CheckformissingAfxLockTempMapcalls
if(AfxGetModuleThreadState()->m_nTempMapLock!
=0)
{
TRACE1("Warning:
Tempmaplockcountnon-zero(%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
returnnReturnCode;
}
3.InitInstance函数
pApp首先调用InitInstance函数,该函数完成MFC内部管理方面的工作,接着,调用PThread的InitInstance函数。
在Test程序中,可以发现从CWinApp派生的应用程序类CTestApp也有一个InitInstance函数,其申明如下:
virtualBOOLInitInstance();
这是一个虚函数,根据多态性原理,AfxWinMain函数这里实际上调用的是子类CTestApp的InitInstance函数。
3.2.2MFC框架窗口
1.设计和注册窗口
窗口类的注册是由AfxEndDeferRegisterClass函数完成。
AfxEndDeferRegisterClass函数先判断窗口的类型,然后赋予其相应的类名(wndcls.lpszClassName变量)。
之后就调用AfxRegisterClass函数注册窗口类。
BOOLAFXAPIAfxEndDeferRegisterClass(LONGfToRegister)
{
…………
//commoninitialization
WNDCLASSwndcls;
memset(&wndcls,0,sizeof(WNDCLASS));//startwithNULLdefaults
wndcls.lpfnWndProc=DefWindowProc;
wndcls.hInstance=AfxGetInstanceHandle();
wndcls.hCursor=afxData.hcurArrow;
INITCOMMONCONTROLSEXinit;
init.dwSize=sizeof(init);
//worktoregisterclassesasspecifiedbyfToRegister,populatefRegisteredClassesaswego
if(fToRegister&AFX_WND_REG)
{
//Childwindows-nobrush,noicon,safestdefaultclassstyles
wndcls.style=CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
wndcls.lpszClassName=_afxWnd;
if(AfxRegisterClass(&wndcls))
fRegisteredClasses|=AFX_WND_REG;
}
if(fToRegister&AFX_WNDOLECONTROL_REG)
{
//OLEControlwindows-useparentDCforspeed
wndcls.style|=CS_PARENTDC|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
wndcls.lpszClassName=_afxWndOleControl;
if(AfxRegisterClass(&wndcls))
fRegisteredClasses|=AFX_WNDOLECONTROL_REG;
}
if(fToRegister&AFX_WNDCONTROLBAR_REG)
{
//Controlbarwindows
wndcls.style=0;//controlbarsdon'thandledoubleclick
wndcls.lpszClassName=_afxWndControlBar;
wndcls.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1);
if(AfxRegisterClass(&wndcls))
fRegisteredClasses|=AFX_WNDCONTROLBAR_REG;
}
if(fToRegister&AFX_WNDMDIFRAME_REG)
{
//MDIFramewindow(alsousedforsplitterwindow)
wndcls.style=CS_DBLCLKS;
wndcls.hbrBackground=NULL;
if(_AfxRegisterWithIcon(&wndcls,_afxWndMDIFrame,AFX_IDI_STD_MDIFRAME))
fRegisteredClasses|=AFX_WNDMDIFRAME_REG;
}
if(fToRegister&AFX_WNDFRAMEORVIEW_REG)
{
//SDIFrameorMDIChildwindowsorviews-normalcolors
wndcls.style=CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
wndcls.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
if(_AfxRegisterWithIcon(&wndcls,_afxWndFrameOrView,AFX_IDI_STD_FRAME))
fRegisteredClasses|=AFX_WNDFRAMEORVIEW_REG;
}
………
}
AfxRegisterClass函数首先获取窗口类的信息。
如该窗口已经注册,则直接返回一个真值;如果未注册,就调用RegisterClass函数注册该窗口类。
BOOLAFXAPIAfxRegisterClass(WNDCLASS*lpWndClass)
{
WNDCLASSwndcls;
if(GetClassInfo(lpWndClass->hInstance,lpWndClass->lpszClassName,
&wndcls))
{
//classalreadyregistered
returnTRUE;
}
if(!
:
:
RegisterClass(lpWndClass))
{
TRACE1("Can'tregisterwindowclassnamed%s\n",
lpWndClass->lpszClassName);
returnFALSE;
}
if(afxContextIsDLL)
{
AfxLockGlobals(CRIT_REGCLASSLIST);
TRY
{
//classregisteredsuccessfully,addtoregisteredlist
AFX_MODULE_STATE*pModuleState=AfxGetModuleState();
LPTSTRlpszUnregisterList=pModuleState->m_szUnregisterList;
//thebufferisoffixedsize--ensurethatitdoesnotoverflow
ASSERT(lstrlen(lpszUnregisterList)+1+
lstrlen(lpWndClass->lpszClassName)+1<
_countof(pModuleState->m_szUnregisterList));
//appendclassname+newlinetom_szUnregisterList
lstrcat(lpszUnregisterList,lpWndClass->lpszClassName);
TCHARszTemp[2];
szTemp[0]='\n';
szTemp[1]='\0';
lstrcat(lpszUnregisterList,szTemp);
}
CATCH_ALL(e)
{
AfxUnlockGlobals(CRIT_REGCLASSLIST);
THROW_LAST();
//Note:
DELETE_EXCEPTIONnotrequired.
}
END_CATCH_ALL
AfxUnlockGlobals(CRIT_REGCLASSLIST);
}
returnTRUE;
}
我们所创建的这个MFC应用程序Test,实际上有2个窗口。
其中一个是CMainFrame类的对象所代表的应用程序框架窗口。
该类有一个PreCreateWindow函数,这是在窗口产生之前被调用的。
PreCreateWindow函数调用了AfxDeferRegisterClass函数,实际上,这是有个宏定义,#defineAfxDeferRegisterClass()AfxEndDeferRegisterClass(),其功能就是注册窗口类。
基本的执行流程是WinMain函数之后,窗口产生之前注册窗口类的。
BOOLCFrameWnd:
:
PreCreateWindow(CREATESTRUCT&cs)
{
if(cs.lpszClass==NULL)
{
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
cs.lpszClass=_afxWndFrameOrView;//COLOR_WINDOWbackground
}
if((cs.style&FWS_ADDTOTITLE)&&afxData.bWin4)
cs.style|=FWS_PREFIXTITLE;
if(afxData.bWin4)
cs.dwExStyle|=WS_EX_CLIENTEDGE;
returnTRUE;
}
2.创建窗口
在MFC程序中,窗口的创建功能是由CWnd类的CreateEx函数实现的。
BOOLCWnd:
:
CreateEx(DWORDdwExStyle,LPCTSTRlpszClassName,
LPCTSTRlpszWindowName,DWORDdwStyle,
intx,inty,intnWidth,intnHeight,
HWNDhWndParent,HMENUnIDorHMenu,LPVOIDlpParam)
{
//allowmodificationofseveralcommoncreateparameters
CREATESTRUCTcs;
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();
returnFALSE;
}
AfxHookWindowCreate(this);
HWNDhWnd=:
:
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);
………….
}
CFrameWnd类的Create函数的申明也在AFXWin.h中,实现代码如下:
BOOLCFrameWnd:
:
Create(LPCTSTRlpszClassName,
LPCTSTRlpszWindowName,
DWORDdwStyle,
constRECT&rect,
CWnd*pParentWnd,
LPCTSTRlpszMenuName,
DWORDdwExStyle,
CCreateContext*pContext)
{
……..
if(!
CreateEx(dwExStyle,lpszClassName,lpszWindowName,dwStyle,
rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,
pParentWnd->GetSafeHwnd(),hMenu,(LPVOID)pContext))
{
TRACE0("Warning:
failedtocreateCFrameWnd.\n");
if(hMenu!
=NULL)
DestroyMenu(hMenu);
returnFALSE;
}
returnTRUE;
}
3.显示窗口和更新窗口
在Test程序的应用程序类(CTestApp)中有一个名为m_pMainWnd的成员变量,该变量是一个CWnd类型的指针,它保存了应用程序框架窗口对象的指针。
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow;
3.2.3消息循环
利用CWinThread类的Run函数完成这一任务。
intCWinThread:
:
Run()
{
ASSERT_VALID(this);
//fortrackingtheidletimestate
BOOLbIdle=TRUE;
LONGlIdleCount=0;
//acquireanddispatchmessagesuntilaWM_QUITmessageisreceived.
for(;;)
{
//phase1:
checktoseeifwecandoidlework
while(bIdle&&
!
:
:
PeekMessage(&m_msgCur,NULL,NULL,NULL,PM_NOREMOVE))
{
//callOnIdlewhileinbIdlestate
if(!
OnIdle(lIdleCount++))
bIdle=FALSE;//assume"noidle"state
}
//phase2:
pumpmessageswhileavailable
do
{
//pumpmessage,butquitonWM_QUIT
if(!
PumpMessage())
returnExitInstance();
//reset"noidle"stateafterpumping"normal"message
if(IsIdleMessage(&m_msgCur))
{
bIdle=TRUE;
lIdleCount=0;
}
}while(:
:
PeekMessage(&m_msgCur,NULL,NULL,NULL,PM_NOREMOVE));
}
ASSERT(FALSE);//notreachable
}
该循环在收到一个WM_QUIT消息时退出。
BOOLCWinThread:
:
PumpMessage()
{
ASSERT_VALID(this);
if(!
:
:
GetMessage(&m_msgCur,NULL,NULL,NULL))
{……………
returnFALSE;
}
…………….
//processthismessage
if(m_msgCur.message!
=WM_KICKIDLE&&!
PreTranslateMessage(&m_msgCur))
{
:
:
TranslateMessage(&m_msgCur);
:
:
DispatchMessage(&m_msgCur);
}
returnTRUE;
}
3.2.4窗口过程函数
在AfxEndDeferRegisterClass,里面有这样一句:
wndcls.lpfnWndProc=DefWindowProc;其作用实际上就是设置窗口过程函数,这里指定的是一个默认的窗口过程:
DefWindowProc。
实际上,MFC程序不是把所有的消息都交给DefWindowProc来处理,而是采用了消息映射机制来处理各种消息。
整个MFC基本运行过程如下:
■首先利用全局应用程序对象theApp启动应用程序。
产生了这个全局对象,基类CWinApp中的this指针才能指向这个对象。
■调用全局应用程序对象的构造函数,从而就会先调用其基类CWinApp的构造函数。
后者完成程序的一些初始化工作,并将应用程序对象的指针保存起来。
■进入WinMain函数,在AfxWinMain函数中可以获取子类(对于Test程序来说,就是CtestApp类)的指针,利用此指针调用虚函数:
InitInstance,根据多态性原理,实际上调用的是子类(CtestApp)的InitInstance函数。
期间会多次调用CreateEx函数,因为一个单文档MFC应用程序有多个窗口,包括框架窗口、工具条、状态条等等。
■进入消息循环。
MFC程序实际上采用的是消息机制来处理各种消息。
当收到WM_QUIT消息时,程序结束。
3.2.5文档/视类结构
创建的MFC程序处了煮框架窗口外,还有一个窗口是视类窗口,对应的类是CView类,CView类也派生于CWnd类。
主框架窗口是整个应用程序外框所包括部分,即图中粗框以内的内容;而视类窗口只是主框架窗口中空白的地方。
这些类之间的派生关系如下:
CtestDo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- VC 深入详解讲解 深入 详解 讲解