MFC框架主线结构剖析复习过程_第1页
MFC框架主线结构剖析复习过程_第2页
MFC框架主线结构剖析复习过程_第3页
MFC框架主线结构剖析复习过程_第4页
MFC框架主线结构剖析复习过程_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Good is good, but better carries it.精益求精,善益求善。MFC框架主线结构剖析-MFC框架主线结构剖析MFC就把有着相当固定行为的WinMain内部动作包装在CWinApp中,把有着相当固定行为的WndProc内部动作包装在CFrameWnd中。也就是说:CWinApp代表程序本体CFrameWnd代表一个主框窗口(FrameWindow)但虽然我说,WinMain内部动作和WndProc内部动作都有着相当程度的固定行为,它们毕竟需要面对不同应用程序而有某种变化。所以,你必须以这两个类别为基础,衍生自己的类别,并改写其中一部份成员函数。classCMyWin

2、App:publicCWinApp.;classCMyFrameWnd:publicCFrameWnd.;本章对衍生类别的命名规则是:在基础类别名称的前面加上My。CWinApp取代WinMain的地位CWinApp的衍生对象被称为applicationobject,可以想见,CWinApp本身就代表一个程式本体。以下是MFC4.x的CWinApp声明(节录自AFXWIN.H):classCWinApp:publicCWinThread/Attributes/Startupargs(donotchange)HINSTANCEm_hInstance;HINSTANCEm_hPrevInstanc

3、e;LPTSTRm_lpCmdLine;intm_nCmdShow;/Runningargs(canbechangedinInitInstance)CWnd*m_pMainWnd;/mainwindow(optional)LPCTSTRm_pszAppName;/humanreadablenameLPCTSTRm_pszRegistryKey;/usedforregistryentriespublic:/setinconstructortooverridedefaultLPCTSTRm_pszExeName;/executablename(nospaces)LPCTSTRm_pszHelpFi

4、lePath;/defaultbasedonmodulepathLPCTSTRm_pszProfileName;/defaultbasedonappnamepublic:/hooksforyourinitializationcodevirtualBOOLInitApplication();/overridesforimplementationvirtualBOOLInitInstance();virtualintExitInstance();virtualintRun();virtualBOOLOnIdle(LONGlCount);.几乎可以说CWinApp用来取代WinMain在SDK程序中

5、的地位。这并不是说MFC程序没有WinMain(稍后我会解释),而是说传统上SDK程序的WinMain所完成的工作现在由CWinApp的三个函数完成:virtualBOOLInitApplication();virtualBOOLInitInstance();virtualintRun();WinMain只是扮演役使它们的角色。但从MFC4.x开始,m_pMainWnd已经被移往CWinThread中了(它是CWinApp的父类别)。以下内容节录自MFC4.x的AFXWIN.H:classCWinThread:publicCCmdTarget/Attributes/mainwindow(usu

6、allysameAfxGetApp()-m_pMainCWnd*m_pMainWnd;CWnd*m_pActiveWnd;/activemainwindow(maynotbem_pMainWnd)/onlyvalidwhilerunningHANDLEm_hThread;/thisthreadsHANDLEDWORDm_nThreadID;/thisthreadsIDintGetThreadPriority();BOOLSetThreadPriority(intnPriority);/OperationsDWORDSuspendThread();DWORDResumeThread();/Ove

7、rridables/threadinitializationvirtualBOOLInitInstance();/runningandidleprocessingvirtualintRun();virtualBOOLPreTranslateMessage(MSG*pMsg);virtualBOOLPumpMessage();/lowlevelmessagepumpvirtualBOOLOnIdle(LONGlCount);/returnTRUEifmoreidleprocessingpublic:/validafterconstructionAFX_THREADPROCm_pfnThreadP

8、roc;.;CFrameWnd取代WndProc的地位CFrameWnd主要用来掌握一个窗口,几乎你可以说它是用来取代SDK程序中的窗口函式的地位。传统的SDK窗口函数写法是:longFARPASCALWndProc(HWNDhWnd,UNITmsg,WORDwParam,LONGlParam)switch(msg)caseWM_COMMAND:switch(wParam)caseIDM_ABOUT:OnAbout(hWnd,wParam,lParam);break;break;caseWM_PAINT:OnPaint(hWnd,wParam,lParam);break;default:Def

9、WindowProc(hWnd,msg,wParam,lParam);MFC程序有新的作法,我们在Hello程序中也为CMyFrameWnd准备了两个消息处理例程,声明如下:classCMyFrameWnd:publicCFrameWndpublic:CMyFrameWnd();afx_msgvoidOnPaint();afx_msgvoidOnAbout();DECLARE_MESSAGE_MAP();这种写法非常奇特,原因是MFC内建了一个所谓的MessageMap机制,会把消息自动送到与消息对映之特定函数去;消息与处理函数之间的对映关系由程序员指定。DECLARE_MESSAGE_MAP

10、另搭配其它宏,就可以很便利地将消息与其处理函数关联在一起:BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)ON_WM_PAINT()ON_COMMAND(IDM_ABOUT,OnAbout)END_MESSAGE_MAP()稍后我就来探讨这些神秘的宏。引爆器Applicationobject上图的theApp就是Hello程序的applicationobject,每一个MFC应用程序都有一个,而且也只有这么一个。当你执行Hello,这个全域对象产生,于是构造式执行起来。我们并没有定义CMyWinApp构造式;至于其父类别CWinApp的构造式内容摘要如下(摘录自

11、APPCORE.CPP):CWinApp:CWinApp(LPCTSTRlpszAppName)m_pszAppName=lpszAppName;/initializeCWinThreadstateAFX_MODULE_THREAD_STATE*pThreadState=AfxGetModuleThreadState();pThreadState-m_pCurrentWinThread=this;m_hThread=:GetCurrentThread();m_nThreadID=:GetCurrentThreadId();/initializeCWinAppstateAFX_MODULE_ST

12、ATE*pModuleState=AfxGetModuleState();pModuleState-m_pCurrentWinApp=this;/innon-runningstateuntilWinMainm_hInstance=NULL;m_pszHelpFilePath=NULL;m_pszProfileName=NULL;m_pszRegistryKey=NULL;m_pszExeName=NULL;m_lpCmdLine=NULL;m_pCmdInfo=NULL;.CWinApp之中的成员变量将因为theApp这个全域对象的诞生而获得配置与初值。隐晦不明的WinMaintheApp配置

13、完成后,WinMain登场。我们并未撰写WinMain程序代码,这是MFC早已准备好并由联结器直接加到应用程序代码中的。_tWinMain函数是为了支持Unicode而准备的一个宏。/inAPPMODUL.CPPexternCintWINAPI_tWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPTSTRlpCmdLine,intnCmdShow)/callshared/exportedWinMainreturnAfxWinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow);详细查看AfxWin

14、Main的代码:/inWINMAIN.CPP#0001/#0002/StandardWinMainimplementation#0003/CanbereplacedaslongasAfxWinInitiscalledfirst#0004#0005intAFXAPIAfxWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,#0006LPTSTRlpCmdLine,intnCmdShow)#0007#0008ASSERT(hPrevInstance=NULL);#0009#0010intnReturnCode=-1;#0011CWinApp*pApp

15、=AfxGetApp();#0012#0013/AFXinternalinitialization#0014if(!AfxWinInit(hInstance,hPrevInstance,lpCmdLine,nCmdShow)#0015gotoInitFailure;#0016#0017/Appglobalinitializations(rare)#0018ASSERT_VALID(pApp);#0019if(!pApp-InitApplication()#0020gotoInitFailure;#0021ASSERT_VALID(pApp);#0022#0023/Performspecific

16、initializations#0024if(!pApp-InitInstance()#0025#0026if(pApp-m_pMainWnd!=NULL)#0027#0028TRACE0(Warning:Destroyingnon-NULLm_pMainWndn);#0029pApp-m_pMainWnd-DestroyWindow();#0030#0031nReturnCode=pApp-ExitInstance();#0032gotoInitFailure;#0033#0034ASSERT_VALID(pApp);#0035#0036nReturnCode=pApp-Run();#003

17、7ASSERT_VALID(pApp);#0038#0039InitFailure:#0040#0041AfxWinTerm();#0042returnnReturnCode;#0043稍加整理去芜存菁,就可以看到这个程序进入点主要做些什么事:intAFXAPIAfxWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPTSTRlpCmdLine,intnCmdShow)intnReturnCode=-1;CWinApp*pApp=AfxGetApp();AfxWinInit(hInstance,hPrevInstance,lpCmdLine,

18、nCmdShow);pApp-InitApplication();pApp-InitInstance();nReturnCode=pApp-Run();AfxWinTerm();returnnReturnCode;其中,AfxGetApp是一个全域函数,定义于AFXWIN1.INL中:_AFXWIN_INLINECWinApp*AFXAPIAfxGetApp()returnafxCurrentWinApp;而afxCurrentWinApp又定义于AFXWIN.H中:#defineafxCurrentWinAppAfxGetModuleState()-m_pCurrentWinApp再根据稍早

19、所述CWinApp:CWinApp中的动作,我们于是知道,AfxGetApp其实就是取得CMyWinApp对象指针。所以,AfxWinMain中这样的动作:CWinApp*pApp=AfxGetApp();pApp-InitApplication();pApp-InitInstance();nReturnCode=pApp-Run();其实就相当于调用:CMyWinApp:InitApplication();CMyWinApp:InitInstance();CMyWinApp:Run();因而导至调用:CWinApp:InitApplication();/因为CMyWinApp并没有改写Ini

20、tApplicationCMyWinApp:InitInstance();/因为CMyWinApp改写了InitInstanceCWinApp:Run();/因为CMyWinApp并没有改写RunAfxWinInitAFX内部初始化动作AfxWinInit是继CWinApp构造式之后的第一个动作:BOOLAFXAPIAfxWinInit(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPTSTRlpCmdLine,intnCmdShow)ASSERT(hPrevInstance=NULL);/setresourcehandlesAFX_MODULE_STA

21、TE*pState=AfxGetModuleState();pState-m_hCurrentInstanceHandle=hInstance;pState-m_hCurrentResourceHandle=hInstance;/fillintheinitialstatefortheapplicationCWinApp*pApp=AfxGetApp();if(pApp!=NULL)/Windowsspecificinitialization(notdoneifnoCWinApp)pApp-m_hInstance=hInstance;pApp-m_hPrevInstance=hPrevInsta

22、nce;pApp-m_lpCmdLine=lpCmdLine;pApp-m_nCmdShow=nCmdShow;pApp-SetCurrentHandles();/initializethreadspecificdata(formainthread)if(!afxContextIsDLL)AfxInitThread();returnTRUE;CWinApp:InitApplicationAfxWinInit之后的动作是pApp-InitApplication。稍早我对象(也就是本例的theApp),所以,当程序调用:pApp-InitApplication();相当于调用:CMyWinApp:

23、InitApplication();但是你要知道,CMyWinApp继承自CWinApp,而InitApplication又是CWinApp的一个虚拟函数;我们并没有改写它(大部份情况下不需改写它),所以上述动作相当于调用:CWinApp:InitApplication();此函数之源代码出现在APPCORE.CPP中:BOOLCwinApp:InitApplication()if(CDocManager:pStaticDocManager!=NULL)if(m_pDocManager=NULL)m_pDocManager=CDocManager:pStaticDocManager;CDocM

24、anager:pStaticDocManager=NULL;if(m_pDocManager!=NULL)m_pDocManager-AddDocTemplate(NULL);elseCDocManager:bStaticInit=FALSE;returnTRUE;这些动作都是MFC为了内部管理而做的。CMyWinApp:InitInstance继InitApplication之后,AfxWinMain调用pApp-InitInstance。稍早我说过了,pApp指向CMyWinApp对象(也就是本例的theApp),所以,当程序调用:pApp-InitInstance();相当于调用:CMy

25、WinApp:InitInstance();但是你要知道,CMyWinApp继承自CWinApp,而InitInstance又是CWinApp的一个虚拟函数。由于我们改写了它,所以上述动作的的确确就是调用我们自己(CMyWinApp)的这个InitInstance函数。我们将在该处展开我们的主窗口生命。注意:应用程序一定要改写虚拟函数InitInstance,因为它在CWinApp中只是个空函数,没有任何内建(预设)动作。CFrameWnd:Create产生主窗口(并先注册窗口类别)CMyWinApp:InitInstance一开始new了一个CMyFrameWnd对象,准备用作主框窗口的C+

26、对象。new会引发构造式:CMyFrameWnd:CMyFrameWndCreate(NULL,HelloMFC,WS_OVERLAPPEDWINDOW,rectDefault,NULL,MainMenu);其中Create是CFrameWnd的成员函数,它将产生一个窗口。但,使用哪一个窗口类别呢?(这里所谓的窗口类别是由RegisterClass所注册的一份数据结构,不是C+类别。)根据CFrameWnd:Create的规格:BOOLCreate(LPCTSTRlpszClassName,LPCTSTRlpszWindowName,DWORDdwStyle=WS_OVERLAPPEDWIND

27、OW,constRECT&rect=rectDefault,CWnd*pParentWnd=NULL,LPCTSTRlpszMenuName=NULL,DWORDdwExStyle=0,CCreateContext*pContext=NULL);八个参数中的后六个参数都有默认值,只有前两个参数必须指定。第一个参数lpszClassName指定WNDCLASS窗口类别,我们放置NULL究竟代表什么意思?意思是要以MFC内建的窗口类别产生一个标准的外框窗口。但,此时此刻Hello程序中根本不存在任何窗口类别呀!噢,Create函数在产生窗口之前会引发窗口类别的注册动作,稍后再解释。第二个参数lps

28、zWindowName指定窗口标题,本例指定HelloMFC。第三个参数dwStyle指定窗口风格,预设是WS_OVERLAPPEDWINDOW,也正是最常用的一种,它被定义为(在WINDOWS.H之中):#defineWS_OVERLAPPEDWINDOW(WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX)因此如果你不想要窗口右上角的极大极小钮,就得这么做:Create(NULL,HelloMFC,WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THIC

29、KFRAMErectDefault,NULL,MainMenu);除了上述标准的窗口风格,另有所谓的扩充风格,可以在Create的第七个参数dwExStyle指定之。扩充风格唯有以:CreateWindowEx(而非:CreateWindow)函数才能完成。事实上稍后你就会发现,CFrameWnd:Create最终调用的正是:CreateWindowEx。Create的第四个参数rect指定窗口的位置与大小。默认值rectDefault是CFrameWnd的一个static成员变量,告诉Windows以预设方式指定窗口位置与大小,就好象在SDK程序中以CW_USEDEFAULT指定给Creat

30、eWindow函数一样。如果你很有主见,希望窗口在特定位置有特定大小,可以这么做:Create(NULL,HelloMFC,WS_OVERLAPPEDWINDOW,CRect(40,60,240,460),/起始位置(40,60),寬200,高400)NULL,MainMenu);第五个参数pParentWnd指定父窗口。对于一个top-level窗口而言,此值应为NULL,表示没有父窗口(其实是有的,父窗口就是desktop窗口)。第六个参数lpszMenuName指定菜单。本例使用一份在RC中准备好的菜单“MainMenu”。第八个参数pContext是一个指向CCreateContext

31、结构的指针,framework利用它,在具备Document/View架构的程序中初始化外框窗口。本例不具备Document/View架构,所以不必指定pContext参数,默认值为NULL。前面提过,CFrameWnd:Create在产生窗口之前,会先引发窗口类别的注册动作。让我再扮一次MFC向导,带你寻幽访胜。你会看到MFC为我们注册的窗口类别名称,及注册动作。/WINFRM.CPPBOOLCFrameWnd:Create(LPCTSTRlpszClassName,LPCTSTRlpszWindowName,DWORDdwStyle,constRECT&rect,CWnd*pParentW

32、nd,LPCTSTRlpszMenuName,DWORDdwExStyle,CCreateContext*pContext)HMENUhMenu=NULL;if(lpszMenuName!=NULL)/loadinamenuthatwillgetdestroyedwhenwindowgetsdestroyedHINSTANCEhInst=AfxFindResourceHandle(lpszMenuName,RT_MENU);hMenu=:LoadMenu(hInst,lpszMenuName);m_strTitle=lpszWindowName;/savetitleforlaterCreate

33、Ex(dwExStyle,lpszClassName,lpszWindowName,dwStyle,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,pParentWnd-GetSafeHwnd(),hMenu,(LPVOID)pContext);returnTRUE;函数中调用CreateEx。注意,CWnd有成员函数CreateEx,但其衍生类别CFrameWnd并无改写,所以这里虽然调用的是CFrameWnd:CreateEx,其实乃是从父类别继承下来的CWnd:CreateEx。/WINCORE.CPPBOOLCW

34、nd:CreateEx(DWORDdwExStyle,LPCTSTRlpszClassName,LPCTSTRlpszWindowName,DWORDdwStyle,intx,inty,intnWidth,intnHeight,HWNDhWndParent,HMENUnIDorHMenu,LPVOIDlpParam)/allowmodificationofseveralcommoncreateparametersCREATESTRUCTcs;cs.dwExStyle=dwExStyle;cs.lpszClass=lpszClassName;cs.lpszName=lpszWindowName;

35、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;PreCreateWindow(cs);AfxHookWindowCreate(this);/HWNDhWnd=:CreateWindowEx(cs.dwExStyle,cs.lpszClass,cs.lpszName,cs.style,cs.x,cs.y,cs.cx,

36、cs.cy,cs.hwndParent,cs.hMenu,cs.hInstance,cs.lpCreateParams);.函数中调用的PreCreateWindow是虚拟函数,CWnd和CFrameWnd之中都有定义。由于this指针所指对象的缘故,这里应该调用的是CFrameWnd:PreCreateWindow(虚拟函数常见的那种行为模式)。/WINFRM.CPPCFrameWndsecondphasecreationBOOLCFrameWnd:PreCreateWindow(CREATESTRUCT&cs)if(cs.lpszClass=NULL)AfxDeferRegisterCla

37、ss(AFX_WNDFRAMEORVIEW_REG);cs.lpszClass=_afxWndFrameOrView;/COLOR_WINDOWbackground.其中AfxDeferRegisterClass是一个定义于AFXIMPL.H中的宏。/AFXIMPL.H#defineAfxDeferRegisterClass(fClass)(afxRegisteredClasses&fClass)?TRUE:AfxEndDeferRegisterClass(fClass)这个宏表示,如果变量afxRegisteredClasses的值显示系统已经注册了fClass这种视窗类别,MFC就啥也不做

38、;否则就调用AfxEndDeferRegisterClass(fClass),准备注册之。让我们再回顾AfxEndDeferRegisterClass的动作。它调用两个函数完成实际的窗口类别注册动作,一个是RegisterWithIcon,一个是AfxRegisterClass:staticBOOLAFXAPIRegisterWithIcon(WNDCLASS*pWndCls,LPCTSTRlpszClassName,UINTnIDIcon)pWndCls-lpszClassName=lpszClassName;HINSTANCEhInst=AfxFindResourceHandle(MAKE

39、INTRESOURCE(nIDIcon),RT_GROUP_ICON);if(pWndCls-hIcon=:LoadIcon(hInst,MAKEINTRESOURCE(nIDIcon)=NULL)/usedefaulticonpWndCls-hIcon=:LoadIcon(NULL,IDI_APPLICATION);returnAfxRegisterClass(pWndCls);BOOLAFXAPIAfxRegisterClass(WNDCLASS*lpWndClass)WNDCLASSwndcls;If(GetClassInfo(lpWndClass-hInstance,lpWndClas

40、s-lpszClassName,&wndcls)/classalreadyregisteredreturnTRUE;:RegisterClass(lpWndClass);.returnTRUE;注意,不同类别的PreCreateWindow成员函数都是在窗口产生之前一刻被调用,准备用来注册窗口类别。如果我们指定的窗口类别是NULL,那么就使用系统预设类别。从CWnd及其各个衍生类别的PreCreateWindow成员函数可以看出,整个Framework针对不同功能的窗口使用了不同的窗口类别。题外话:回忆SDK程序,指定图标和光标形状实为RegisterClass的责任而非CreateWindo

41、w的责任!MFC程序的RegisterClass动作并非由程序员自己来做,因此似乎难以改变图标。不过,MFC还是开放了一个窗口,我们可以在HELLO.RC这么设定图标:AFX_IDI_STD_FRAMEICONDISCARDABLEHELLO.ICO鼠标光标的设定就比较麻烦了。要改变光标形状,我们必须调用AfxRegisterWndClass(其中有Cursor参数)注册自己的窗口类别;然后再将其传回值(一个字符串)做为Create的第一个参数。奇怪的窗口类别名称Afx:b:14ae:6:3e8f原来是ApplicationFramework玩了一些把戏,它把这些窗口类别名称转换为Afx:x:

42、y:z:w的型式,成为独一无二的窗口类别名称:x:窗口风格(windowstyle)的hex值y:窗口鼠标光标的hex值z:窗口背景颜色的hex值w:窗口图标(icon)的hex值如果你要使用原来的(MFC预设的)那些个窗口类别,但又希望拥有自己定义的一个有意义的类别名称,你可以改写PreCreateWindow虚拟函数(因为Create和LoadFrame的内部都会调用它),在其中先利用API函数GetClassInfo获得该类别的一个副本,更改其类别结构中的lpszClassName字段(甚至更改其hIcon字段),再以AfxRegisterClass重新注册之,例如:#0000#defi

43、neMY_CLASSNAMEMyClassName#0001#0002BOOLCMainFrame:PreCreateWindow(CREATESTRUCT&cs)#0003#0004staticLPCSTRclassName=NULL;#0005#0006if(!CFrameWnd:PreCreateWindow(cs)#0007returnFALSE;#0008#0009if(className=NULL)#0010/One-timeclassregistration#0011/Theonlypurposeistomaketheclassnamesomething#0012/meaning

44、fulinsteadofAfx:0 x4d:27:32:hup1hup:hike!#0013/#0014WNDCLASSwndcls;#0015:GetClassInfo(AfxGetInstanceHandle(),cs.lpszClass,&wndcls);#0016wndcls.lpszClassName=MY_CLASSNAME;#0017wndcls.hIcon=AfxGetApp()-LoadIcon(IDR_MAINFRAME);#0018VERIFY(AfxRegisterClass(&wndcls);#0019className=TRACEWND_CLASSNAME;#002

45、0#0021cs.lpszClass=className;#0022#0023returnTRUE;#0024窗口显示与更新CMyFrameWnd:CMyFrameWnd结束后,窗口已经诞生出来;程序流程又回到CMyWinApp:InitInstance,于是调用ShowWindow函数令窗口显示出来并调用UpdateWindow函数令Hello程序送出WM_PAINT消息。MFC程序是不是也像SDK程序一样,有一个GetMessage/DispatchMesage循环?是否每个窗口也都有一个窗口函数,并以某种方式进行消息的判断与处理?两者都是肯定的。CWinApp:Run-程序生命的活水源头

46、Hello程序进行到这里,窗口类别注册好了,窗口诞生并显示出来了,UpdateWindow被调用,使得消息队列中出现了一个WM_PAINT消息,等待被处理。现在,执行的脚步到达pApp-Run。稍早我说过了,pApp指向CMyWinApp对象(也就是本例的theApp),所以,当程序调用:pApp-Run();相当于调用:CMyWinApp:Run();要知道,CMyWinApp继承自CWinApp,而Run又是CWinApp的一个虚拟函数。我们并没有改写它(大部份情况下不需改写它),所以上述动作相当于调用:CWinApp:Run();/APPCORE.CPPintCWinApp:Run()i

47、f(m_pMainWnd=NULL&AfxOleGetUserCtrl()/Notlaunched/Embeddingor/Automation,buthasnomainwindow!TRACE0(Warning:m_pMainWndisNULLinCWinApp:Run-quittingapplication.n);AfxPostQuitMessage(0);returnCWinThread:Run();CWinThread定义于THRDCORE.CPP中:intCWinThread:Run()/fortrackingtheidletimestateBOOLbIdle=TRUE;LONGlI

48、dleCount=0;/acquireanddispatchmessagesuntilaWM_QUITmessageisreceived.for(;)/phase1:checktoseeifwecandoidleworkwhile(bIdle&!:PeekMessage(&m_msgCur,NULL,NULL,NULL,PM_NOREMOVE)/callOnIdlewhileinbIdlestateif(!OnIdle(lIdleCount+)bIdle=FALSE;/assumenoidlestate/phase2:pumpmessageswhileavailabledo/pumpmessa

49、ge,butquitonWM_QUITif(!PumpMessage()returnExitInstance();/resetnoidlestateafterpumpingnormalmessageif(IsIdleMessage(&m_msgCur)bIdle=TRUE;lIdleCount=0;while(:PeekMessage(&m_msgCur,NULL,NULL,NULL,PM_NOREMOVE);ASSERT(FALSE);/notreachableBOOLCWinThread:PumpMessage()if(!:GetMessage(&m_msgCur,NULL,NULL,NU

50、LL)returnFALSE;/processthismessageif(m_msgCur.message!=WM_KICKIDLE&!PreTranslateMessage(&m_msgCur):TranslateMessage(&m_msgCur);:DispatchMessage(&m_msgCur);returnTRUE;获得的消息如何交给适当的例程去处理呢?SDK程序的作法是调用DispatchMessage,把消息丢给窗口函数;MFC也是如此。但我们并未在Hello程序中提供任何窗口函数,是的,窗口函数事实上由MFC提供。回头看看前面AfxEndDeferRegisterClass

51、源代码,它在注册四种窗口类别之前已经指定窗口函数为:wndcls.lpfnWndProc=DefWindowProc;注意,虽然窗口函数被指定为DefWindowProc成员函数,但事实上消息并不是被唧往该处,而是一个名为AfxWndProc的全域函数去。这其中牵扯到MFC暗中做了大挪移的手脚(利用hook和subclassing)。把消息与处理函数串接在一起:MessageMap机制消息映射(MessageMap)的雏形首先,定义一个MSGMAP_ENTRY结构和一个dim宏:structMSGMAP_ENTRYUINTnMessage;LONG(*pfn)(HWND,UINT,WPARAM

52、,LPARAM);#definedim(x)(sizeof(x)/sizeof(x0)请注意MSGMAP_ENTRY的第二元素pfn是一个函数指针,我准备以此指针所指之函数处理nMessage消息。接下来,组织两个数组_messageEntries和_commandEntries,把程序中欲处理的消息以及消息处理例程的关联性建立起来:structMSGMAP_ENTRY_messageEntries=WM_CREATE,OnCreate,WM_PAINT,OnPaint,WM_SIZE,OnSize,WM_COMMAND,OnCommand,WM_SETFOCUS,OnSetFocus,WM_

53、CLOSE,OnClose,WM_DESTROY,OnDestroy,;这是消息这是消息处理例程/Command-ID与处理例程之对照表格structMSGMAP_ENTRY_commandEntries=IDM_ABOUT,OnAbout,IDM_FILEOPEN,OnFileOpen,IDM_SAVEAS,OnSaveAs,;这是WM_COMMAND命令项这是命令处理例程于是窗口函数可以这么设计:/窗口函数LRESULTCALLBACKWndProc(HWNDhWnd,UINTmessage,WPARAMwParam,LPARAMlParam)inti;for(i=0;idim(_messageEntries);i+)/消息对照表if(message=_messageEntriesi.nMessage)return(*_messageEntriesi.pfn)(hWnd,message,wParam,lParam);return(DefWindowProc(hWnd,message,wParam,lParam);/OnCommand-专门处理WM_COMMANDLONGOnComma

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论