windows消息处理机制_第1页
windows消息处理机制_第2页
windows消息处理机制_第3页
windows消息处理机制_第4页
windows消息处理机制_第5页
已阅读5页,还剩26页未读 继续免费阅读

下载本文档

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

文档简介

windows消息处理机制消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做TMsg,它在Windows单元中是这样声明的:typeTMsg=packedrecordhwnd:HWND;//窗口句柄message:UINT;//消息常量标识符wParam:WPARAM;//32位消息的特定附加信息lParam:LPARAM;//32位消息的特定附加信息time:DWORD;//消息创建时的时间pt:TPoint;//消息创建时的鼠标位置end;消息中有什么?是否觉得一个消息记录中的信息像希腊语一样?如果是这样,那么看一看下面的解释hwnd32位的窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。message用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。wParam通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。lParam通常是一个指向内存中数据的指针。由于WParam、lParam和Pointer都是32位的,因此,它们之间可以相互转换。Windows的消息系统是由3个部分组成的:?消息队列。Windows能够为所有的应用程序维护一个消息队列。应用程序必须从消息队列中获取消息,然后分派给某个窗口。?消息循环。通过这个循环机制应用程序从消息队列中检索消息,再把它分派给适当的窗口,然后继续从消息队列中检索下一条消息,再分派给适当的窗口,依次进行。?窗口过程。每个窗口都有一个窗口过程来接收传递给窗口的消息,它的任务就是获取消息然后响应它。窗口过程是一个回调函数;处理了一个消息后,它通常要返回一个值给Windows。注意回调函数是程序中的一种函数,它是由Windows或外部模块调用的。一个消息从产生到被一个窗口响应,其中有5个步骤:1) 系统中发生了某个事件。2) Windows把这个事件翻译为消息,然后把它放到消息队列中。3) 应用程序从消息队列中接收到这个消息,把它存放在TMsg记录中。应用程序把消息传递给一个适当的窗口的窗口过程。窗口过程响应这个消息并进行处理。步骤3和4构成了应用程序的消息循环。消息循环往往是Windows应用程序的核心,因为消息循环使一个应用程序能够响应外部的事件。消息循环的任务就是从消息队列中检索消息,然后把消息传递给适当的窗口。如果消息队列中没有消息,Windows就允许其他应用程序处理它们的消息。Windows操作系统最大的特点就是其图形化的操作界面,其图形化界面是建立在其消息处理机制这个基础之上的。如果不理解Windows消息处理机制,肯定无法深入的理解Windows编程。可惜很多程序员对Windows消息只是略有所闻,对其使用知之甚少,更不了解其内部实现原理,本文试着一步一步向大家披露我理解的Windows消息机制。可以说,掌握了这一部分知识,就是掌握了Windows编程中的神兵利器,灵活运用它,将会极大的提高我们的编程能力。[编辑本段]Windows窗体是怎样展现在屏幕上的呢?众所周知,是通过API绘制实现的。Windows操作系统提供了一系列的API函数来实现界面的绘制功能,例如:2DrawText绘制文字2DrawEdge绘制边框2DrawIcon绘制图标2BitBlt绘制位图2Rectangle绘制矩形2…再复杂的程序界面都是通过这个函数来实现的。那什么时候调用这些函数呢?显然我们需要一个控制中心,用来进行“发号施令”,我们还需要一个命令传达机制,将命令即时的传达到目的地。这个控制中心,就是一个动力源就像一颗心脏,源源不断地将血液送往各处。这个命令传达机制就是Windows消息机制,Windows消息就好比是身体中的血液,它是命令传达的使者。Windows消息控制中心一般是三层结构,其顶端就是Windows内核。Windows内核维护着一个消息队列,第二级控制中心从这个消息队列中获取属于自己管辖的消息,后做出处理,有些消息直接处理掉,有些还要发送给下一级窗体(Window)或控件(Control)o第二级控制中心一般是各Windows应用程序的Application对象。第三级控制中心就是Windows窗体对象,每一个窗体都有一个默认的窗体过程,这个过程负责处理各种接收到的消息。如下图所示:<!--[if!vml]--><!--[endif]-->说明图(注:windows指windows操作系统;窗口:即windows窗口;窗体:包括窗口,以及有句柄的控件;control指控件,控件本身也可能是一个window,也可能不是;Application即应用程序,应用程序也可能不会用到Windows消息机制,这里我们专门讨论有消息循环的应用程序)消息是以固定的结构传送给应用程序的,结构如下:PublicTypeMSGhwndAsLongmessageAsLongwParamAsLonglParamAsLongtimeAsLongptAsPOINTAPIEndType其中hwnd是窗体的句柄,message是一个消息常量,用来表示消息的类型,wParam和IParam都是32位的附加信息,具体表示什么内容,要视消息的类型而定,time是消息发送的时间,pt是消息发送时鼠标所在的位置。Windows操作系统中包括以下几种消息:1、 标准Windows消息:这种消息以WM_打头。2、 通知消息通知消息是针对标准Windows控件的消息。这些控个包括:按钮(Button)、组合框(ComboBox)、编辑框(TextBox)、列表框(ListBox)、ListView控件、Treeview控件、工具条(Toolbar)、菜单(Menu)等。每种消息以不同的字符串打头。3、 自定义消息编程人员还可以自定义消息。二、 关于Windows句柄不是每个控件都能接收消息,转发消息和绘制自身,只有具有句柄(handle)的控件才能做到。有句柄的控件本质上都是一个窗体(window),它们可以独立存在,可以作为其它控件的容器,而没有句柄的控件,如Label,是不能独立存在的,只能作为窗口控件的子控件,它不能绘制自身,只能依靠父窗体将它绘制来。句柄的本质是一个系统自动维护的32位的数值,在整个操作系统的任一时刻,这个数值是唯一的。但该句柄代表的窗体释放后,句柄也会被释放,这个数值又可能被其它窗体使用。也就是说,句柄的数值是动态的,它本身只是一个唯一性标识,操作系统通过句柄来识别和查找它所代表的对象。然而,并非所有的句柄都是窗体的句柄,Windows系统中还中很多其它类型的句柄,如画布(hdc)句柄,画笔句柄,画刷句柄,应用程序句柄hlnstance)等。这种句柄是不能接收消息的。但不管是哪种句柄,都是系统中对象的唯一标识。本文只讨论窗体句柄。那为什么句柄使窗口具有了如此独特的特性呢?实际是都是由于消息的原因。由于有了句柄,窗体能够接收消息,也就知道了该什么时候绘制自己,绘制子控件,知道了鼠标在什么时候点击了窗口的哪个部分,从而作出相应的处理。句柄就好像是一个人的身份证,有了它,你就可以从事各种社会活动;否则的话,你要么是一个社会看不到的黑户,要么跟在别人后面,通过别人来证明你的存在。三、 消息的传送1、 从消息队列获取消息:可以通过PeekMessage或GetMessage函数从Windows消息队列中获取消息。Windows保存的消息队列是以线程(Thread)来分组的,也就是说每个线程都有自己的消息队列。2、 发送消息发送消息到指定窗体一般通过以下两个函数完成:SendMessage和PostMessage。两个函数的区别在于:PostMessage函数只是向线程消息队列中添加消息,如果添加成功,则返回True,否则返回False,消息是否被处理,或处理的结果,就不知道了。而SendMessage则有些不同,它并不是把消息加入到队列里,而是直接翻译消息和调用消息处理(线程向自己发送消息才是这样),直到消息处理完成后才返回。所以,如果我们希望发送的消息立即被执行,就应该调用SendMessage。还有一点,就是SendMessage发送的消息由于不会被加入到消息队列中(错:线程向其他线程发送消息也是追加到其他线程的发送消息队列的,即使这两个线程在同一个进程也是如此),所以通过PeekMessage或GetMessage是不能获取到由SendMessage发送的消息。另外,有些消息用PostMessage不会成功,比如wm_settext。所以不是所有的消息都能够用PostMessage的。还有一些其它的发送消息API函数,如PostThreadMessage,SendMessageCallback,SendMessageTimeout,SendNotifyMessage等。四、 消息循环与窗体过程消息循环是应用程序能够持续存在的根本原因。如果循环退出,则应用程序就结束了。我们来看一看Delphi中封装的消息循环是怎样的:第一步:程序开始运行(Run)Application.Initialize;//初始化Application.CreateForm(TForm1,Form1);//创建主窗体Application.Run;//开始运行,准备进行消息循环如果不创建主窗体,应用程序同样可以存在和运行。第二步:开始调用消息循环(HandleMessage)procedureTApplication.Run;beginFRunning:=True;tryAddExitProc(DoneApplication);ifFMainForm<>nilthenbegincaseCmdShowofSW_SHOWMINNOACTIVE:FMainForm.FWindowState:=wsMinimized;SW_SHOWMAXIMIZED:MainForm.WindowState:=wsMaximized;end;ifFShowMainFormthenifFMainForm.FWindowState=wsMinimizedthenMinimizeelseFMainForm.Visible:=True;Repeat//注:循环开始tryHandleMessage;exceptHandleException(Self);end;untilTerminated;//循环结束条件end;finallyFRunning:=False;end;end;第三步:消息循环中对消息的处理。procedureTApplication.HandleMessage;varMsg:TMsg;beginifnotProcessMessage(Msg)thenIdle(Msg);end;functionTApplication.ProcessMessage(varMsg:TMsg):Boolean;varHandled:Boolean;beginResult:=False;ifPeekMessage(Msg,0,0,0,PM_REMOVE)thenbeginResult:=True;ifMsg.Message<>WM_QUITthenbeginHandled:=False;ifAssigned(FOnMessage)thenFOnMessage(Msg,Handled);ifnotIsHintMsg(Msg)andnotHandledandnotIsMDIMsg(Msg)andnotIsKeyMsg(Msg)andnotIsDlgMsg(Msg)thenbeginTranslateMessage(Msg);DispatchMessage(Msg);end;endelseFTerminate:=True;end;end;窗体过程实际上是一个回调函数。所谓的回调函数,实际上就是由Windows操作系统或外部程序调用的函数。回调函数一般都有规定的参数格式,以地址方式传递给调用者。窗口过程中是Windows操作系统调用了,在一个窗口创建的时候,在分配窗体句柄的时候就需要传入回调函数地址。那为什么我们平时编程看不到这个回调函数呢?这是由于我们的编程工具已经为我们生成了默认的窗体过程,这个过程的要做的事情就是判断不同的消息类型然后做出不同的处理。例如可以为键盘或鼠标输入生成事件等。五、 消息与事件事件本质上是对消息的封装,是IDE编程环境为了简化编程而提供的有用的工具。这个封装是在窗体过程中实现的。每种IDE封装了许多Windows的消息,例如:事件消息OnActivateWM_ACTIVATEOnClickWM_XBUTTONDOWNOnCreateWM_CREATEOnDblClickWM_XBUTTONDBLCLICKOnKeyDownWM_KEYDOWNOnKeyPressWM_CHAROnKeyUpWIN_KEYUPOnPaintWM_PAINTOnResizeWM_SIZEOnTimerWM_TIMERMFC框架原理以及消息运行机制收藏(1)Windows程序内部运行机制1,windows程序设计是种事件驱动方式的程序设计,主要基于消息的。当用户需要完成某种功能时,需要调用OS某种支持,然后OS将用户的需要包装成消息,并投入到消息队列中,最后应用程序从消息队列中取走消息并进行响应。2,消息结构:typedefstructtagMSG{ //msgHWNDhwnd;//接收消息的窗口句柄。和哪个窗口相关联。UINTmessage;//消息标识。消息本身是什么。WPARAMwParam;//消息的附加信息。具体取决于消息本身。LPARAMlParam;DWORDtime;//消息投递时间。POINTpt;//消息投递时,光标在屏幕上的位置。}MSG;3,消息队列:每个应用程序OS都为它建立一个消息队列,消息队列是个先进先出的缓冲区,其中每个元素都是一个消息,OS将生成的每个消息按先后顺序放进消息队列中,应用程序总是取走当前消息队列中的第一条消息,应用程序取走消息后便知道用户的操作和程序的状态,然后对其处理即消息响应,消息响应通过编码实现。使用VC编程除了良好的C基础外还需要掌握两方面:一,消息本身。不同消息所代表的用户操作和应用程序的状态。二,对于某个特定的消息来说,要让OS执行某个特定的功能去响应消息。5,Window程序入口:intWINAPIWinMain(HINSTANCEhInstance,//当前事例句柄。HINSTANCEhPrevInstance,//先前事例句柄。LPSTRlpCmdLine,//命令行指针intnCmdShow//(窗口)显示的状态);说明:WinMain函数是Windows程序入口点函数,由OS调用,当OS启动应用程序的时候,winmain函数的参数由OS传递的。6,创建一个完整的窗口需要经过下面四个操作步骤:一,设计一个窗口类;如:WNDCLASSwndcls;二,注册窗口类;如:RegisterClass(&wndcls);三,创建窗口;如:CreateWindow(),CreateWindowEX();四,显示及更新窗口。如:ShowWindow(),UpdateWindow();说明:创建窗口的时候一定要基于已经注册的窗口类.7,Windows提供的窗口类:typedefstruct_WNDCLASS{UINTstyle; //窗口的类型WNDPROClpfnWndProc;//窗口过程函数指针(回调函数)int cbClsExtra;//窗口类附加字节,为该类窗口所共享。通常0。int cbWndExtra;//窗口附加字节。通常设为0。HANDLEhInstance;//当前应用程序事例句柄。HICONhIcon; //图标句柄LoadIcon();HCURSORhCursor;//光标句柄LoadCursor();HBRUSHhbrBackground;//画刷句柄(HBRUSH)GetStockObject();LPCTSTRlpszMenuName;//菜单名字LPCTSTRlpszClassName;//类的名字}WNDCLASS;窗口类注册:ATOMRegisterClass(CONSTWNDCLASS*lpWndClass//addressofstructurewithclass//data);9,创建窗口:HWNDCreateWindow(LPCTSTRlpClassName,//pointertoregisteredclassnameLPCTSTRlpWindowName,//pointertowindownameDWORDdwStyle,//windowstyleintx, //horizontalpositionofwindowinty, //verticalpositionofwindowintnWidth, //windowwidthintnHeight, //windowheightHWNDhWndParent,//handletoparentorownerwindowHMENUhMenu, //handletomenuorchild-windowidentifierHANDLEhInstance,//handletoapplicationinstanceLPVOIDlpParam//pointertowindow-creationdata);10,显示和更新窗口窗口:BOOLShowWindow(HWNDhWnd,//handletowindowintnCmdShow//showstateofwindow);BOOLUpdateWindow(HWNDhWnd//handleofwindow);11,消息循环:MSGmsg;while(GetMessage(&msg,...))//从消息队列中取出一条消息{TranslateMessage(&msg);//进行消息(如键盘消息)转换DispatchMessage(&msg);〃分派消息到窗口的回调函数处理,(OS调用窗口回调函数进行处理)。}其中://**TheGetMessagefunctionretrievesamessagefromthecallingthread'smessagequeueandplacesitinthespecifiedstructure.//**IfthefunctionretrievesamessageotherthanWM_QUIT,thereturnvalueisnonzero.IfthefunctionretrievestheWM_QUITmessage,thereturnvalueiszero.Ifthereisanerror,thereturnvalueis-1.BOOLGetMessage(LPMSGlpMsg,//addressofstructurewithmessageHWNDhWnd,//handleofwindowUINTwMsgFilterMin,//firstmessageUINTwMsgFilterMax//lastmessage);//TheTranslateMessagefunctiontranslatesvirtual-keymessagesintocharactermessages.Thecharactermessagesarepostedtothecallingthread'smessagequeue,tobereadthenexttimethethreadcallstheGetMessageorPeekMessagefunction.BOOLTranslateMessage(CONSTMSG*lpMsg//addressofstructurewithmessage//TheDispatchMessagefunctiondispatchesamessagetoawindowprocedure.LONGDispatchMessage(CONSTMSG*lpmsg//pointertostructurewithmessage);12,窗口过程函数(回调函数)原型:TheWindowProcfunctionisanapplication-definedfunctionthatprocessesmessagessenttoawindow.TheWNDPROCtypedefinesapointertothiscallbackfunction.WindowProcisaplaceholder(占位符)fortheapplication-definedfunctionname.LRESULTCALLBACKWindowProc(〃这里WindowProc是个代号名字。HWNDhwnd,//handletowindowUINTuMsg,//messageidentifierWPARAMwParam,//firstmessageparameterLPARAMlParam//secondmessageparameter);说明:两种函数调用约定(__stdcall和__cdecl):#defineCALLBACK__stdcall〃_stdcall标准调用预定,是PASCAL调用约定,象DELPHI使用的就是标准调用约定#defineWINAPIV__cdecl//__cdecl是C语言形式的调用约定。主要区别:函数参数传递顺序和对堆栈的清除上。问题:除了那些可变参数的函数调用外,其余的一般都是_stdcall约定。但C/C++编译默然的是—cdecl约定。所以如果在VC等环境中调用—stdcall约定的函数,必须要在函数声明的时加上—stdcall修饰符,以便对这个函数的调用是使用—stdcall约定(如使用DELPHI编写的DLL时候)。(VC中可通过这途径修改:project|settings..|c/c++|...)在窗口过程函数中通过一组switch语句来对消息进行处理如:LRESULTCALLBACKWindowProc(HWNDhwnd,UINTuMsg,WPARAMwParam,LPARAMlParam){switch(uMsg){caseWM_PAINT:break;case...break;caseWM_CLOSE://DestroyWindow(hwnd);〃销毁窗口,并发送WM_DESTROY消息。break;caseWM_DESTROY://PostQuitMessage(0);〃发送WM_QUIT消息到消息队列中,请求终止。〃GetMessage()取到WM_QUIT消息后,返回0退出消息循 //环,从而终止应用程序。break;default:returnDefWindowProc(hwnd,uMsg,wParam,lParam);//用缺省的窗口过程处理我们不感兴趣的消息(其它消息)。//这是必须的。}//switchreturn0;}//WindowProc13,DestroyWindow()函数和PostQuitMessage()函数原型://**TheDestroyWindowfunctiondestroysthespecifiedwindow.ThefunctionsendsWM_DESTROYandWM_NCDESTROYmessages。BOOLDestroyWindow(HWNDhWnd//handletowindowtodestroy);//**ThePostQuitMessagefunctionindicatestothesystemthatathreadhasmadearequesttoterminate(quit).ItistypicallyusedinresponsetoaWM_DESTROYmessage.//**ThePostQuitMessagefunctionpostsaWM_QUITmessagetothethread'smessagequeueandreturnsimmediately;thefunctionsimplyindicates(预示,通知)tothesystemthatthethreadisrequestingtoquitatsometimeinthefuture.WhenthethreadretrievestheWM_QUITmessagefromitsmessagequeue,itshouldexititsmessageloopandreturncontroltothesystem.VOIDPostQuitMessage(intnExitCode//exitcode14,关于DC句柄获取:a) 使用BeginPaint(),EndPaint()对。注意只能在响应WM_PAINT消息时使用。b) 使用GetDc(),ReleaseDC()对。注意他们不能在响应WM_PAINT中使用。(2)C++c语言中,结构体struct中不能包括函数的,而在C++中struct中可以包括函数。C++中结构体和类可以通用,区别主要表现在访问控制方面:struct中默认是public,而class中默认的是private。构造函数最重要的作用是创建对象的本身,C++中每个类可以拥有多个构造函数,但必须至少有一个构造函数,当一个类中没有显式提供任何构造函数,C++编辑器自动提供一个默认的不带参数的构造函数,这个默认的构造函数只负责构造对象,不做任何初始化工作。但在一个类中只要自己定义一个构造函数,不管带参不带参,编辑器不再提供默认的不带参的构造函数了。构造函数没有返回值。析构函数当一个对象生命周期结束时候被调用来回收对象占用的内存空间。一个类只需有一个析构函数。析构函数没有返回值也不的带参数。5,析构函数的作用与构造函数相反,对象超出起作用范围对应的内存空间被系统收回,或被程序用delete删除的时候,对象的析构函数被调用。6,函数的重载条件:函数的参数类型、个数不同,才能构成函数的重载。重载是发生在同一个类中。7,类是抽象的,不占用具体物理内存,只有对象是实例化的,是占用具体物理内存的。8,this指针是隐含指针,指向对象本身(this指针不是指向类的),代表了对象的地址。所有的对象调用的成员函数都是同一代码段,但每个对象都有自己的数据成员。当对象通过调用它的成员函数来访问它的数据成员的时候,成员函数除了接收实参外,还接收了对象的地址,这个地址被一个隐藏的形参this所获取,通过这个this指针可以访问对象的数据成员和成员函数。对象中public属性的成员在外部和子类中都可以被访问;protected属性的成员在外部不能被访问,在子类中是可以访问的;private属性在子类中和外部都不能被访问。类的继承访问特性:(public,protected,private)a) 基类中private属性成员,子类无论采用那种继承方式都不能访问。b) 采用public继承,基类中的public,protected属性的成员访问特性在子类中仍然保持一致。c) 采用protected继承,基类中的public,protected属性成员访问特性在子类中变为protected.d) 采用private继承,基类中的public,protected属性成员访问特性在子类中变为private.11,子类和基类的构造函数或析构函数调用顺序:当调用子类的构造函数时候先调用基类的构造函数(如果没有指明,则调用基类却省那个不带参数的构造函数;如果要指明则在子类构造函数名后加":基类名(参数)")。析构函数则相反,先调用子类析构函数,后调用基类的析构函数。12,函数的覆盖:函数的覆盖是发生在发生父类和子类之间的。(函数的重载是发生在同一个类中)当子类中重写了父类的某些成员函数后,子类中的成员函数覆盖了父类的对应同名成员函数13,用父类指针访问子类对象成员时候,只能访问子类从父类继承来的那部分。(这时候外部不可以访问父类中保护和私有的部分,子类中不可访问父类私有部分。)14,多态性:在基类的的成员函数前加virturl变成虚函数,当用子类对象调用该功能的成员函数时候,子类有的就调用子类的,子类没有的就调用基类的。当C++编译器在编译的时候,发现被调用的成员函数在基类中定义的是虚函数,这个时候C++就会采用迟绑定技术(latebinding),在运行的时候,依据对象的类型来确定调用的哪个函数,子类有调用子类的,子类没有的就调用基类的。如果基类中的成员函数不是虚函数,则这时候的绑定是早期绑定,在编译的时候就已经确定该调用哪个函数。15,纯虚函数:在类中定义时eg:virtualvoidfunc1()=0;纯虚函数没有函数体,含有纯虚函数的类叫做抽象类,抽象类不能实例化对象。当子类从抽象类的基类中派生出来时候,如果没有实现基类中的纯虚函数,则子类也是个抽象类,也不能实例化对象。纯虚函数被标名为不具体实现的虚成员函数,纯虚函数可以让类只具有操作的名称而不具有具体的操作的内容,让派生类在继承的时候再给出具体的定义。如果派生类没有给出基类的纯虚函数的具体定义的时候,派生类也为一个抽象类,也不能实例化对象。16,引用:变量的别名。引用需要在定义的时候用一变量或对象初始化自己。引用一旦在定义的时候初始化,就维系在一个特定的变量或对象上。引用不占用物理内存(与定义引用的目标共用同一内存)。指针变量需要占用物理内存,用来存储地址。(3)MFC程序框架的剖析1,寻找WinMain人口:在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。路径:MFC|SRC|APPMODUL.CPP:_tWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPTSTRlpCmdLine,intnCmdShow){//callshared/exportedWinMainreturnAfxWinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow);}注意:(#define_tWinMainWinMain)2,对于全局对象或全局变量来说,在程序运行即WINMAIN函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。所以:CTEApptheApp;->CTEApp::CTEApp(){}->_tWinMain(){}说明:每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。3,通过构造应用程序对象过程中调用基类CWinApp的构造函数,在CWinApp的构造函数中对程序包括运行时一些初始化工作完成了。CWinApp构造函数:MFC|SRC|APPCORE.CPPCWinApp::CWinApp(LPCTSTRlpszAppName){...}〃带参数,而CTEApp构造函数没有显式向父类传参,难道CWinApp()有默认参数?见下:(在CWinApp类定义中,CWinApp(LPCTSTRlpszAppName=NULL);)注意:CWinApp()函数中:pThreadState->m_pCurrentWinThread=this;pModuleState->m_pCurrentWinApp=this(this指向的是派生类CTEApp对象,即theApp)调试:CWinApp::CWinApp();->CTEApptheApp;(->CTEApp::CTEApp())->CWinApp::CWinApp()->CTEApp::CTEApp()->_tWinMain(){}_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。AfxWinMain()函数路径:MFC|SRC|WINMAIN.CPP:在AfxWinMain()函数中:CWinApp*pApp=AfxGetApp();说明:pApp存储的是指向WinApp派生类对象(theApp)的指针。//_AFXWIN_INLINECWinApp*AFXAPIAfxGetApp()//{returnafxCurrentWinApp;}调用pThread->InitInstance()说明:pThread也指向theApp,由于基类中virtualBOOLInitApplication()定义为虚函数,所以调用pThread->InitInstance()时候,调用的是派生类CTEApp的InitInstance()函数。nReturnCode=pThread->Run();说明:pThread->Run()完成了消息循环。注册窗口类:AfxEndDeferRegisterClass();AfxEndDeferRegisterClass()函数所在文件:MFC|SRC|APPCORE.CPPBOOLAFXAPIAfxEndDeferRegisterClass(LONGfToRegister){...}说明:设计窗口类:在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。调试:CWinApp::CWinApp();->CTEApptheApp;(->CTEApp::CTEApp())->CWinApp::CWinApp()->CTEApp::CTEApp()->_tWinMain(){}〃进入程序->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()〃父类InitInstance虚函数;->CTEApp::InitInstance()〃子类实现函数;->AfxEndDeferRegisterClass(LONGfToRegister)//注册所选择的窗口类(出于文档管理,注册提前,正常的应在PreCreateWindow中进行注册)//之后进入创建窗口阶段(以下再不做调试)6,PreCreateWindow()://主要是注册窗口类BOOLCMainFrame::PreCreateWindow(CREATESTRUCT&cs){if(!CFrameWnd::PreCreateWindow(cs))returnFALSE;returnTRUE;}说明:CFrameWnd::PreCreateWindow()函数所在文件:MFC|SRC|WINFRM.CPPBOOLCFrameWnd::PreCreateWindow(CREATESTRUCT&cs){if(cs.lpszClass==NULL){VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));//判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册cs.lpszClass=_afxWndFrameOrView;//COLOR_WINDOWbackground//把注册后的窗口类名赋给cs.lpszClass}if((cs.style&FWS_ADDTOTITLE)&&afxData.bWin4)cs.style|=FWS_PREFIXTITLE;if(afxData.bWin4)cs.dwExStyle|=WS_EX_CLIENTEDGE;returnTRUE;}其中:virtualBOOLPreCreateWindow(CREATESTRUCT&cs);//PreCreateWindow()是个虚函数,如果子类有则调用子类的。#defineVERIFY(f)ASSERT(f)#defineAfxDeferRegisterClass(fClass)AfxEndDeferRegisterClass(fClass)defineAFX_WNDFRAMEORVIEW_REG0x00008constTCHAR_afxWndFrameOrView[]=AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件中,定义为全局数组。//#defineAFX_WNDFRAMEORVIEWAFX_WNDCLASS("FrameOrView")7,创建窗口:Create()函数路径:MFC|SRC|WINFRM.CPP:CFrameWnd::Create(...){CreateEx(...);//从父类继承来的,调用CWnd::CreateEx().}CWnd::CreateEx()函数路径:MFC|SRC|WINCORE.CPPBOOLCWnd::CreateEx(...){if(!PreCreateWindow(cs))〃虚函数,如果子类有调用子类的。{PostNcDestroy();returnFALSE;}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);}说明:CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数,如果子类有调用子类的。HWNDCreateWindowEx(DWORDdwExStyle,LPCTSTRlpClassName,LPCTSTRlpWindowName,DWORDdwStyle,intx,inty,intnWidth,intnHeight,HWNDhWndParent,HMENUhMenu,HINSTANCEhInstance,LPVOIDlpParam);typedefstructtagCREATESTRUCT{//csLPVOIDlpCreateParams;HINSTANCEhInstance;HMENUhMenu;HWNDhwndParent;intcy;intcx;inty;intx;LONGstyle;LPCTSTRlpszName;LPCTSTRlpszClass;DWORDdwExStyle;}CREATESTRUCT;8,显示和更新窗口:CTEApp类,TEApp.cpp中m_pMainWnd->ShowWindow(SW_SHOW);〃显示窗口,m_pMainWnd指向框架窗口m_pMainWnd->UpdateWindow();//更新窗口说明:classCTEApp:publicCWinApp{...}classCWinApp:publicCWinThread{...}classCWinThread:publicCCmdTarget{public:CWnd*m_pMainWnd;}9,消息循环:intAFXAPIAfxWinMain(){...//Performspecificinitializationsif(!pThread->InitInstance()){...}//完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新nReturnCode=pThread->Run();//继承基类Run()方法,调用CWinThread::Run()来完成消息循环}////////////////////////////////////////////////////////////////CWinThread::Run()方法路径:MFC|SRC|THRDCORE.CPPintCWinThread::Run(){...//phase2:pumpmessageswhileavailabledo//消息循环{//pumpmessage,butquitonWM_QUITif(!PumpMessage())〃取消息并处理returnExitInstance();}while(::PeekMessage(&m_msgCur,NULL,NULL,NULL,PM_NOREMOVE));}说明:BOOLPeekMessage(”)函数说明ThePeekMessagefunctionchecksathreadmessagequeueforamessageandplacesthemessage(ifany)inthespecifiedstructure.Ifamessageisavailable,thereturnvalueisnonzero.Ifnomessagesareavailable,thereturnvalueiszero./////////////////////////////////////////////////////////////BOOLCWinThread::PumpMessage(){if(!::GetMessage(&m_msgCur,NULL,NULL,NULL))//取消息{...}//processthismessageif(m_msgCur.message!=WM_KICKIDLE&&!PreTranslateMessage(&m_msgCur)){::TranslateMessage(&m_msgCur);//进行消息(如键盘消息)转换::DispatchMessage(&m_msgCur);〃分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。)}returnTRUE;}9,文档与视结构:可以认为View类窗口是CMainFram类窗口的子窗口。DOCument类是文档类。DOC-VIEW结构将数据本身与它的显示分离开。文档类:数据的存储,加载视类:数据的显示,修改10,文档类,视类,框架类的有机结合:在CTEApp类CTEApp::lnitlnstance()函数中通过文档模板将文档类,视类,框架类的有机组织一起。CSingleDocTemplate*pDocTemplate;pDocTemplate=newCSingleDocTemplate(lDR_MAlNFRAME,RUNTlME_CLASS(CTEDoc),RUNTlME_CLASS(CMainFrame),//mainSDlframewindowRUNTlME_CLASS(CTEView));AddDocTemplate(pDocTemplate);//增加到模板(4)MFC消息映射机制的剖析一,消息映射机制1,消息响应函数:(例:在CDrawView类响应鼠标左键按下消息)在头文件(DrawView.h中声明消息响应函数原型。//{{AFX_MSG(CDrawView)//注释宏afx_msgvoidOnLButtonDown(UlNTnFlags,CPointpoint);//}}AFX_MSG//注释宏说明:在注释宏之间的声明在VC中灰色显示。afx_msg宏表示声明的是一个消息响应函数。在源文件(DrawView.cpp)中进行消息映射。BEGlN_MESSAGE_MAP(CDrawView,CView)//{{AFX_MSG_MAP(CDrawView)ON_WM_LBUTTONDOWN()//}}AFX_MSG_MAP//StandardprintingcommandsON_COMMAND(ID_FILE_PRINT,CView::OnFilePrint)ON_COMMAND(ID_FILE_PRINT_DIRECT,CView::OnFilePrint)ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview)END_MESSAGE_MAP()说明:在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间进行消息映射。宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDown()相关联。这样一旦有消息的产生,就会自动调用相关联的消息响应函数去处理。宏ON_WM_LBUTTONDOWN()定义如下:#defineON_WM_LBUTTONDOWN()\{WM_LBUTTONDOWN,0,0,0,AfxSig_vwp,\(AFX_PMSG)(AFX_PMSGW)(void(AFX_MSG_CALLCWnd::*)(UINT,CPoint))&OnLButtonDown},3)源文件中进行消息响应函数处理。(DrawView.cpp中自动生成OnLButtonDown函数轮廓,如下)voidCDrawView::OnLButtonDown(UINTnFlags,CPointpoint){//TODO:Addyourmessagehandlercodehereand/orcalldefaultCView::OnLButtonDown(nFlags,point);}说明:可见当增加一个消息响应处理,在以上三处进行了修改。可在消息响应函数里添加消息处理代码完成对消息的响应、处理。2,消息响应的方式:在基类中针对每种消息做一个虚函数,当子类对消息响应时候,只要在子类中重写这个虚函数即可。缺点:MFC类派生层次很多,如果在基类对每个消息进行虚函数处理,那么从基类派生的每个子类都将背负一个庞大的虚表,这样浪费内存,故MFC没有采取这中方式而采取消息映射方式。消息映射方式:MFC在后台维护了一个句柄和C++对象指针对照表,当收到一个消息后,通过消息结构里资源句柄(查对照表)就可找到与它对应的一个C++对象指针,然后把这个指针传给基类,基类利用这个指针调用WindowProc()函数对消息进行处理,WindowProc()函数中调用OnWndMsg()函数,真正的消息路由及处理是由OnWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函数,而且是用派生类对象指针调用的,由多态性知最总终调用子类的。在OnWndMsg()函数处理的时候,根据消息种类去查找消息映射,判断所发的消息有没有响应函数,具体方式是到相关的头文件和源文件中寻找消息响应函数声明(从注释宏〃{{AFX_MSG(CDrawView)...〃}}AFX_MSG之间寻找),消息映射(从宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP(之间寻找),最终找到对应的消息处理函数。当然,如果子类中没有对消息进行处理,则消息交由基类处理。说明:virtualLRESULTWindowProc(UINTmessage,WPARAMwParam,LPARAMlParam);virtualBOOLOnWndMsg(UINTmessage,WPARAMwParam,LPARAMlParam,LRESULT*pResult);二,有关绘图使用SDK获取DC句柄:HDChdc;hdc=::GetDc(m_hWnd);〃获取DC句柄MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);LineTo(hdc,point.x,point.y);::ReleaseDC(m_hWnd,hdc);//释放DC利用CDC类指针和CWin类成员函数获取DC。CDC*pDC=GetDC();pDC->MoveTo(m_ptOrigin);pDC->LineTo(point);ReleaseDC(pDC);3利用CClientDC对象。(CClientDC类从CDC类派生来的)CClientDCdc(this);dc.MoveTo(m_ptOrigin);dc.LineTo(point);说明:TheCClientDCclassisderivedfromCDCandtakescareofcallingtheWindowsfunctionsGetDCatconstructiontimeandReleaseDCatdestructiontime.ThismeansthatthedevicecontextassociatedwithaCClientDCobjectistheclientareaofawindow.4,利用CWindowDC对象。(CWindowDC类从CDC类派生来的)CWindowDCdc(this);//dc.MoveTo(m_ptOrigin);dc.LineTo(point);说明:TheCWindowDCclassisderivedfromCDC.ItcallstheWindowsfunctionsGetWindowDCatconstructiontimeandReleaseDCatdestructiontime.ThismeansthataCWindowDCobjectaccessestheentirescreenareaofaCWnd(bothclientandnonclientareas).5,GetParent()得到父窗口指针;GetDesktopWindow()得到屏幕窗口指针。6,利用画笔改变线条颜色和类型:CPenpen(PS_DOT,1,#OOffOO);〃构造画笔对象CClientDCdc(this);CPen*pOldPen=dc.SelectObject(&pen);〃将画笔选入DCdc.MoveTo(m_ptOrigin);dc.LineTo(point);dc.SelectObject(pOldPen);//恢复先前的画笔7,使用画刷(通常利用画刷去填充矩形区域)使用单色画刷CBrushbrush(#ff0000);〃构造画刷对象CClientDCdc(this);dc.FillRect(CRect(m_ptOrigin,point),&brush);〃用指定的画刷去填充矩形区域使用位图画刷CBitmapbitmap;//构造位图对象(使用前需要初试化)bitmap.LoadBitmap(IDB_BITMAP1);〃初试化位图对象CBrushbrush(&bitmap);〃构造位图画刷CClientDCdc(this);dc.FillRect(CRect(m_ptOrigin,point),&brush);〃用指定的位图画刷去填充矩形区域使用透明画刷CBrush*pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));〃获取透明画刷对象指针CClientDCdc(this);CBrush*pOldBrush=dc.SelectObject(pBrush);〃将透明画刷选入DCdc.Rectangle(CRect(m_ptOrigin,point));dc.SelectObject(pOldBrush);//释放透明画刷说明:TheGetStockObjectfunctionretrievesahandletooneofthepredefinedstockpens,brushes,fonts,orpalettes.HGDIOBJGetStockObject(intfnObject//typeofstockobject);ReturnsapointertoaCBrushobjectwhengivenahandletoaWindowsHBRUSHobject.staticCBrush*PASCALFromHandle(HBRUSHhBrush);//FromHandle是一个静态方法,故可用CBrush::FromHandle()形式调用。静态方法不属于某一个具体对象,而属于类本身,在类加载的时候就已经为类静态方法分配了代码去,故可用CBrush::FromHandle()形式调用。静态方法中,不能引用非静态的数据成员和方法。静态数据成员需要在类外单独做初始化,形式如:变量类型类名::变量名=初始值;8,CDC::SetROP2方法:intSetROP2(intnDrawMode);Setsthecurrentdrawingmode.(5)文本编程1,创建插入符:voidCreateSolidCaret(intnWidth,intnHeight);//创建插入符voidCreateCaret(CBitmap*pBitmap);//创建位图插入符voidShowCaret();//显示插入符voidHideCaret();//隐藏插入符staticvoidPASCALSetCaretPos(POINTpoint);//移动插入符号说明:创建插入符要在窗口创建完成之后,CreateSolidCaret函数创建的插入符被初始化为隐藏,所以需要调用ShowCaret()将其显示。使用CreateCaret函数创建位图插入符的时候,不能使用局部的位图对象关联位图资源(与资源相关联的C++对象,当它析构的时候会同时把与它相关联的资源销毁。)2,获取当前字体信息的度量:CDC::GetTextMetricsBOOLGetTextMetrics(LPTEXTMETRIClpMetrics)const;说明:typedefstructtagTEXTMETRIC{/*tm*/inttmHeight;//字体高度。Specifiestheheight(ascent+descent)tmAscent;//基线以上的字体高度inttmDescent;//基线以下的字体高度inttmInternalLeading;inttmExternalLeading;inttmAveCharWidth;//字符平均宽度inttmMaxCharWidth;inttmWeight;BYTEtmItalic;BYTEtmUnderlined;BYTEtmStruckOut;BYTEtmFirstChar;BYTEtmLastChar;BYTEtmDefaultChar;BYTEtmBreakChar;BYTEtmPitchAndFamily;BYTEtmCharSet;inttmOverhang;inttmDigitizedAspectX;inttmDigitizedAspectY;}TEXTMETRIC;3,OnDraw函数:virtualvoidOnDraw(CDC*pDC)当窗口(从无到有或尺寸大小改变等)要求重绘的时候,会发送WM_PAIN消息,调用OnDraw函数进行重绘。4,获取字符串的高度和宽度(区别字符串的长度):CDC::GetTextExtentCSizeGetTextExtent(LPCTSTRlpszString,intnCount)const;CSizeGetTextExtent(constCString&str)const;说明:TheCSizeclassissimilartotheWindowsSIZEstructure。typedefstructtagSIZE{intcx;//thex-extentintcy;//they-extent}SIZE;5,路径层:BOOLBeginPath();//在这作图定义路径层剪切区域BOOLEndPath();BOOLSelectClipPath(intnMode);//调用这个函数来使当前路径层剪切区域与新剪切区域进行互操作。//在这覆盖作图(包含前定义的路径层区域)定义新的剪切区域说明:1)SelectClipPathSelectsthecurrentpathasaclippingregionforthedevicecontext,combiningthenewregionwithanyexistingclippingregionbyusingthespecifiedmode.Thedevicecontextidentifiedmustcontainaclosedpath.////nMode:RGN_AND,RGN_COPY,RGN_DIFF,RGN_OR,RGN_XORRGN_ANDThenewclippingregionincludestheintersection(overlappingareas)ofthecurrentclippingregionandthecurrentpath.RGN_COPYThenewclippingregionisthecurrentpath.RGN_DIFFThenewclippingregionincludestheareasofthecurrentclippingregion,andthoseofthecurrentpathareexcluded.RGN_ORThenewclippingregionincludestheunion(combinedareas)ofthecurrentclippingregionandthecurrentpath.RGN_XORThenewclippingregionincludestheunionofthecurrentclippingregionandthecurrentpath,butwithouttheoverlappingareas.2)应用:当作图的时候,如果想要在整幅图形其中的某个部分和其它部分有所区别,我们可以把这部分图形放到路径层当中,然后指定调用指定互操作模式调用SelectClipPath(intnMode)函数来使路径层和覆盖在其上新绘图剪切区域进行互操作,达到特殊效果。6,关于文本字符串一些函数:COLORREFGetBkColor()const;//得到背景颜色virtualCOLORREFSetBkColor(COLORREFcrColor);//设置背景颜色BOOLSetTextBkColor(COLORREFcr);//设置文本背景颜色virtualCOLORREFSetTextColor(COLORREFcrColor);//设置文本颜色virtualBOOLTextOut(intx,inty,LPCTSTRlpszString,intnCount);//输出文本BOOLTextOut(intx,inty,constCString&str);CStringLeft(intnCount)const;//得到字符串左边nCount个字符intGetLength()const;//得到字符串长度7,字体CFont::CFontCFont();//构造函数//ConstructsaCFontobject.TheresultingobjectmustbeinitializedwithCreateFont,CreateFontIndirect,CreatePointFont,orCreatePointFontIndirectbeforeitcanbeused.选用字体事例代码组:CClientDCdc(this);CFontfont;//构造字体对象font.CreatePointFont(300,'华文行楷蔦NULL);//初始化字体对象,与字体资源相关联CFont*pOldFont=dc.SelectObject(&font);//将新字体选入DCdc.SelectObject(pOldFont);//恢复原字体说明:构造字体对象时候,必须初始化。(初始化是将字体对象与字体资源相关联)。初始化对象时候,选用的字体也可以是系统字体,但不一定都有效,据测试选用。8,在MFC中CEditView和cRichEditView类已经完成了初步的文字处理。可以让应用程序的View类以CEditView和cRichEditView类为基类。9,平滑变色CDC::TextOut(是一个字母一个字母的输出,达不到平滑效果。CDC::DrawText():将文字的输出局限于一个矩形区域,超出矩形区域的文字都被截断。利用这一特点,可每隔些时间增加矩形大小,从而可实现人眼中的平滑效果。CWnd::SetTimer():设置定时器。按设定的时间定时发送WM_TIMER消息。说明:UINTSetTi

温馨提示

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

评论

0/150

提交评论