深入剖析MFC中Windows消息处理机制.doc_第1页
深入剖析MFC中Windows消息处理机制.doc_第2页
深入剖析MFC中Windows消息处理机制.doc_第3页
深入剖析MFC中Windows消息处理机制.doc_第4页
深入剖析MFC中Windows消息处理机制.doc_第5页
已阅读5页,还剩3页未读 继续免费阅读

VIP免费下载

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

文档简介

深入剖析MFC中Windows消息处理机制本人对Windows系统、MFC谈不上有深入的了解,但对MFC本身包装API的机制很有兴趣,特别是读了候老师的深入浅出MFC后,感觉到Visual C+的Application FrameWork十分精制。在以前,我对SDI结构处理消息有一定的认识,但对于模式对话框的消息机制不了解,读了深入一书也没能得到解决,近日,通过在网友的帮助和查阅MSDN,自认为已经了解。一时兴起,写下这些文字,没有其它目的,只是希望让后来者少走弯路,也希望和我一样喜欢钻牛角尖的人共同讨论、学习。如果你是牛人,那么你现在要慎重考虑有没有充足的时间读这些幼稚文字。正文:Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础。如何理解?举了例子,当你CLICK Windows开始BUTTON时,为什么就会弹出一个菜单呢?当你单击鼠标左键时,操作系统中与MOUSE相关的驱动程序在第一时间内得到这个信号LBUTTONDOWN,然后它通知操作系统嗨,鼠标左键被单击了!,操作系统得到这一信号后,马上要判断用户单击鼠标左键,这是针对哪个窗口呢?如何判断?这很简单!当前状态中,具有焦点的窗口或控件就是了这里当然是开始BUTTON了。然后操作系统马上向这个窗口发送一条消息到这个窗口所在进程的消息队列,消息内容应是消息本身的代号、附加参数、窗口句柄等等了。那么,只有操作系统才有资格发送消息至某一窗口的消息队列吗?不然,其它程序也有资格。你可以在你的程序中调用:SendMessage、PostMessage。这样,被单击的窗口得到了一条由操作系统发送的包含CLICK的消息,操作系统已经暂时不再管窗口的任何事,因为它还要忙于处理其它事务。你的程序得到一条消息后如何做呢?Windows对于你在开始BUTTON上的单击事件做出如下反映:弹出一菜单。可是,得到消息到做出反映这一过程是如何实现的呢?这就是本文讨论的主要内容当然只是针对MFC了。我首先简要谈一下SDI,然后会花更多文字描述模式对话框。对于SDI窗口,你的应用程序类的InitInstance()大约如下:BOOL CEx06aApp:InitInstance()CSingleDocTemplate*pDocTemplate;pDocTemplate=new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CEx06aDoc),RUNTIME_CLASS(CMainFrame),/main SDI frame window RUNTIME_CLASS(CEx06aView);AddDocTemplate(pDocTemplate);CCommandLineInfo cmdInfo;ParseCommandLine(cmdInfo);if(!ProcessShellCommand(cmdInfo)return FALSE;m_pMainWnd-ShowWindow(SW_SHOW);m_pMainWnd-UpdateWindow();return TRUE;完成一些如动态生成相关文档、视,显示主框架窗口、处理参数行信息等工作。这些都是显示在你工程中的明码。我们现在把断点设置到return TRUE;一句,跟入MFC源码中,看看到底MFC内部做了什么。程序进入SRCWinMain.cpp,下一个大动作应是:nReturnCode=pThread-Run();注意了,重点来了。F11进入int CWinApp:Run()if(m_pMainWnd=NULL&AfxOleGetUserCtrl()/Not launched/Embedding or/Automation,but has no main window!TRACE0(Warning:m_pMainWnd is NULL in CWinApp:Run-quitting application.n);AfxPostQuitMessage(0);return CWinThread:Run();再次F11进入:int CWinThread:Run()ASSERT_VALID(this);/for tracking the idle time state BOOL bIdle=TRUE;LONG lIdleCount=0;/acquire and dispatch messages until aWM_QUIT message is received.for(;)/phase1:check to see if we can do idle work while(bIdle&!:PeekMessage(&m_msgCur,NULL,NULL,NULL,PM_NOREMOVE)/call OnIdle while in bIdle state if(!OnIdle(lIdleCount+)bIdle=FALSE;/assumeno idlestate/phase2:pump messages while available do/pump message,but quit on WM_QUIT if(!PumpMessage()return ExitInstance();/resetno idlestate after pumpingnormalmessage if(IsIdleMessage(&m_msgCur)bIdle=TRUE;lIdleCount=0;while(:PeekMessage(&m_msgCur,NULL,NULL,NULL,PM_NOREMOVE);ASSERT(FALSE);/not reachableBOOL CWinThread:IsIdleMessage(MSG*pMsg)/Return FALSE if the message just dispatched should _not_/cause OnIdle to be run.Messages which do not usually/affect the state of the user interface and happen very/often are checked for./redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE if(pMsg-message=WM_MOUSEMOVE|pMsg-message=WM_NCMOUSEMOVE)/mouse move at same position as last mouse move?if(m_ptCursorLast=pMsg-pt&pMsg-message=m_nMsgLast)return FALSE;m_ptCursorLast=pMsg-pt;/remember for next time m_nMsgLast=pMsg-message;return TRUE;/WM_PAINT and WM_SYSTIMER(caret blink)return pMsg-message!=WM_PAINT&pMsg-message!=0x0118;这是SDI处理消息的中心机构,但请注意,它觉对不是核心!分析一下,在无限循环FOR内部又出现一个WHILE循环while(bIdle&!:PeekMessage(&m_msgCur,NULL,NULL,NULL,PM_NOREMOVE)/call OnIdle while in bIdle state if(!OnIdle(lIdleCount+)bIdle=FALSE;/assumeno idlestate这段代码是当你程序进程的消息队列中没有消息时,会调用OnIdle做一些后备工作,临时对象在这里被删除。当然它是虚函数。其中的PeekMessage,是查看消息队列,如果有消息返回TRUE,如果没有消息返回FALSE,这里指定PM_NOREMOVE,是指查看过后不移走消息队列中刚刚被查看到的消息,也就是说这里的PeekMessage只起到一个检测作用,显然返回FALSE时即没有消息,才会进入循环内部,执行OnIdle,当然了,你的OnIdle返回FLASE,会让程序不再执行OnIdle。你可能要问:消息机制和绘图机制是微软Windows及其周边其他产品和生俱来的,是Win系列OS作为一个操作系统进行微机内部实现的二大支柱和特征,消息系统是Windows下一切应用程式间,包括Windows自身,进行交互和通讯的渠道,是Windows实现对运行在其下的任何应用程式进行控制及应用程式对Windows进行响应的解决手段,因此对Windows的编程,无论是在哪种语言规范和IDE下,都不可避免地要涉及到消息处理,虽然有些编程语言如VB用事件驱动编程机制在很大程度上封装了消息的复杂性,但若要深入Win32编程,就必须学习Windows的消息系统,正如游戏编程要掌控Win的绘图机制相同,而只要您一旦深韵了这二大支柱和基本,您就掌控了Win32编程的根本。消息的产生来源于系统事情(包括计时器事件)和用户事情,Windows用消息来调入和关闭(更有其他处理,如绘制一个窗口等)应用程式,一个典型表现是在关机操作中,Windows发一个关机的消息给任何正在运行的应用程式,告知他们退出内存,此时,应用程式用回应消息的方法来响应OS,因此,消息是应用程式和WinOS交互的手段.消息的主体是应用程式之间和应用程式和OS之间,(这是通俗的说法,其实在一个应用程式的内部,各窗口组件之间也存在着消息的流动,窗口组件和他们的父窗口和上层窗口之间当然也有消息的传递过程(如命令传递,后面在跟踪一个消息的路径中将会详谈),Windows内部实时流动的消息数量是如此的宠大,程式实现之外的手工分析是一种很自不量力的事情)消息的最终主体却是窗口和窗口之间,窗口和OS之间-因为在MFC的技术规范里,只有窗口进程才能发送和接收一个消息并处理他,当然一些非界面窗口类如文档类也能处理一个消息,消息的最终归宿是某个窗口类的成员函数,也就是进入消息处理函数被处理,或被某个非界面类也就是内部处理类如文档类处理,系统中默认的窗口类和用户注册的窗口类都有进程,都能在内存中创建实在的窗口对象,窗口对象和窗口类接收和处理(千万注意:接收一个消息和处理一个消息是相差甚大的二个过程,后面将在讨论重定向一个消息技术时将谈到)发往他或由他主动发往别的窗口进程或OS的消息,修改窗口进程干涉窗口进程对消息的处理过程(而不是接收过程,这个区分的周详解释请参见后面从注意消息泵并不是个.起的文字)是可能的(窗口进程只是一段函数),但是假如这个窗口进程属于别人,如系统的窗口类,您将没有源程式进行修改,但却能够用消息重定的技术加以干涉,比如用户自定义的窗口类,用户完万能够自定义他的窗口进程,编写自己的消息泵,实现对消息的重定向,编写用户自己的消息泵属于Win32编程中重定向一个消息的七大技术之一。MFC中有七种技术能够用来重定向一个消息,他们是:1,子分类2,超分类3,OnCmdMsg(),4,SetCapture5,编写自己的消息泵,6,SetWindowsHookEx(),人们常说的钩子函数,便是其中之一.在谈完消息泵的概念后,我们将一步一步追踪一个消息在系统中的路径,然后才能讨论对他的重定向。消息泵并不是个窗口类的窗口进程,虽然他们都是函数,同样都对注入到这个窗口进程的消息进行工作,而并不最终处理消息本身(上面已说到原因),消息泵是个通俗的说法,他只和消息被发往窗口进程后的接收工作有关而不和处理过程有关(上面也已说到消息的接收和处理是二不同过程),而窗口进程恰恰相反他只和处理有关不和接收有关下面开始详述。消息泵被包含在CWinApp的成员函数Run()中.Windows消息机制Windows操作系统最大的特点就是其图形化的操作界面,其图形化界面是建立在其消息处理机制这个基础之上的。如果不理解Windows消息处理机制,肯定无法深入的理解Windows编程。可惜很多程序员对Windows消息只是略有所闻,对其使用知之甚少,更不了解其内部实现原理,本文试着一步一步向大家披露我理解的Windows消息机制。可以说,掌握了这一部分知识,就是掌握了Windows编程中的神兵利器,灵活运用它,将会极大的提高我们的编程能力。一、消息概述Windows窗体是怎样展现在屏幕上的呢?众所周知,是通过API绘制实现的。Windows操作系统提供了一系列的API函数来实现界面的绘制功能,例如:²;DrawText绘制文字²;DrawEdge绘制边框²;DrawIcon绘制图标²;BitBlt绘制位图²;Rectangle绘制矩形²;再复杂的程序界面都是通过这个函数来实现的。那什么时候调用这些函数呢?显然我们需要一个控制中心,用来进行发号施令,我们还需要一个命令传达机制,将命令即时的传达到目的地。这个控制中心,就是一个动力源,就像一颗心脏,源源不断地将血液送往各处。这个命令传达机制就是Windows消息机制,Windows消息就好比是身体中的血液,它是命令传达的使者。Windows消息控制中心一般是三层结构,其顶端就是Windows内核。Windows内核维护着一个消息队列,第二级控制中心从这个消息队列中获取属于自己管辖的消息,后做出处理,有些消息直接处理掉,有些还要发送给下一级窗体(Window)或控件(Control)。第二级控制中心一般是各Windows应用程序的Application对象。第三级控制中心就是Windows窗体对象,每一个窗体都有一个默认的窗体过程,这个过程负责处理各种接收到的消息。如下图所示:(注:windows指windows操作系统;窗口:即windows窗口;窗体:包括窗口,以及有句柄的控件;control指控件,控件本身也可能是一个window,也可能不是;Application即应用程序,应用程序也可能不会用到Windows消息机制,这里我们专门讨论有消息循环的应用程序)图1包含了Windows机制的大部分内容,下面所讲的所有内容实际上都是对张图的解释或扩充。消息是以固定的结构传送给应用程序的,结构如下:Public Type MSG hwnd As Long message As Long wParam As Long lParam As Long time As Long pt As POINTAPI End Type其中hwnd是窗体的句柄,message是一个消息常量,用来表示消息的类型,wParam和lParam都是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)句柄,画笔句柄,画刷句柄,应用程序句柄(hInstance)等。这种句柄是不能接收消息的。但不管是哪种句柄,都是系统中对象的唯一标识。本文只讨论窗体句柄。那为什么句柄使窗口具有了如此独特的特性呢?实际是都是由于消息的原因。由于有了句柄,窗体能够接收消息,也就知道了该什么时候绘制自己,绘制子控件,知道了鼠标在什么时候点击了窗口的哪个部分,从而作出相应的处理。句柄就好像是一个人的身份证,有了它,你就可以从事各种社会活动;否则的话,你要么是一个社会看不到的黑户,要么跟在别人后面,通过别人来证明你的存在。三、消息的传送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)procedure TApplication.Run;begin FRunning:=True;try AddExitProc(DoneApplication);if FMainForm nil then begin case CmdShow of SW_SHOWMINNOACTIVE:FMainForm.FWindowState:=wsMinimized;SW_SHOWMAXIMIZED:MainForm.WindowState:=wsMaximized;end;if FShowMainForm then if FMainForm.FWindowState=wsMinimized then Minimize else FMainForm.Visible:=True;Repeat/注:循环开始try HandleMessage;except HandleException(Self);end;until Terminated;/循环结束条件end;finally FRunning:=False;end;end;第三步:消息循环中对消息的处理。procedure TApplication.HandleMessage;var Msg:TMsg;begin if not ProcessMessage(Msg)then Idle(Msg);end;function TApplication.ProcessMessage(var Msg:TMsg):Boolean;var Handled:Boolean;begin Result:=False;if PeekMessage(Msg,0,0,0,PM_REMOVE)then begin Result:=True;if Msg.Message WM_QUIT then begin Handled:=False;if Assigned(FOnMessage)then FOnMessage(Msg,Handled);if not IsHintMsg(Msg)and not Handled and not IsMDIMsg(Msg)and not IsKeyMsg(Msg)and not IsDlgMsg(Ms

温馨提示

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

评论

0/150

提交评论