MFC编程案例chapter003.pdf
《MFC编程案例chapter003.pdf》由会员分享,可在线阅读,更多相关《MFC编程案例chapter003.pdf(20页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、下载下载第3章 消 息 处 理第1章讨论了M F C用户界面的基本要素:窗口、窗口类和 C W n d;第2章讨论了构成M F C库的其他类,尤其是那些构成 M F C应用程序内核的类。在本章中,我们将讨论 M F C类和它们的窗口怎样进行互相通信的。我们发现有三种类型的消息:窗口、命令(C o m m a n d)和控件通知(Control Notification),并且这些消息既可以发送(s e n t),也可以寄送(p o s t);接着,将跟踪一个被M F C窗口进程处理的消息;最后,将讨论重定向消息的方法。3.1 发送或寄送一个消息第1章已提及,每个窗口使用窗口进程处理发送给它的消
2、息。消息可以来自系统、你的应用程序或别的应用程序。消息告诉窗口进程执行某个任务(如初始化自己、绘制或销毁一个窗口等),或者通知它发生某个事件(如鼠标正单击窗口)。系统或应用程序有两种传输消息的方法:发送消息或寄送消息。3.1.1 发送一个消息发送一个消息时,直接调用窗口的窗口进程。通信是即时的,直到窗口进程为调用函数返回一个结果后,应用程序才能继续。3.1.2 寄送一个消息寄送一个消息时,把消息发送到拥有那个窗口的应用程序消息队列中。一有空闲,应用程序就搜索消息队列,并在消息队列中处理消息,即从队列中删除它们,并将它们发送到即定窗口。通信将可能延迟,直到目标应用程序获得处理消息的时间。调用函数
3、发送消息后即返回,但结果只是表示消息寄送成功与否,而不是被调用窗口进程的结果(见图3-1)。图3-1 发送消息时通信是即时的,而寄送消息时通信可能延迟3.1.3 发送一个消息与寄送一个消息的比较鼠标和键盘消息通常是寄送的,而所有其他消息通常都是发送的。在消息队列中,寄送当应用程序空闲时,抽出寄送到队列中的消息并调用该窗口的窗口进程被发送的消息直接调用该窗口的窗口进程被寄送的消息延迟在应用程序消息队列中消息队列消息n+1消息n+2消息n+3消息n+4WndProc地址消息泵窗口对象的消息接受特殊的鼠标和键盘处理。通常,应该尽量发送一个消息,除非想把动作延迟到所有鼠标和键盘消息被处理之后。3.2
4、怎样使用MFC发送一个消息用M F C发送一个消息的方法是,首先,应获取接收消息的 C W n d类对象的指针;然后,调用C W n d的成员函数SendMessage()。L R E S U LT Res=pWnd-SendMessage(UINT Msg,WPARAM wParam,LPARAM lParam);p W n d指针指向目标C W n d类对象。变量M s g是消息,w P a r a m和l P a r a m变量包含消息的参数,如鼠标单击哪里或选择了什么菜单项。目标窗口返回的消息结果放在变量 R e s中。发送消息到一个没有C W n d类对象的窗口,可以用下列目标窗口的
5、句柄直接调用 Windows API:L R E S U LT Res=:SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);这里的h W n d是目标窗口的句柄。3.3 怎样用MFC寄送一个消息用M F C寄送一个消息与发送一个消息几乎相同,但寄送时用 PostMessage(),而不是用SendMessage();返回值R e s也不一样,R e s不是一个由目标窗口返回的值,而是一个布尔值,用来表示消息是否成功地放到消息队列中。检索一个寄送消息正常情况下,一旦消息被寄送后,应用程序在后台发送它。但是在特殊情况下,需要你自
6、己去删除一个消息,例如想在应用程序接收到某种消息之前停止应用程序。有两种方法可以从应用程序消息队列中删除一个消息,但这两种方法都没有涉及 M F C。第一种方法:在不干扰任何事情之下窥视消息队列,看看一个消息是否在那里。BOOL res=:PeekMessage(LPMSG lpMsg,HWND hWnd,UINT wMsFilterMin,UINT wMsgFilterMax,UINTw R e m o v e M s g);第二种方法:实际上是等待,一直等到一个新的消息到达队列为止,然后删除并返回该消息。BOOL res=:GetMessage(LPMSG lpMsg,HWND hWnd,
7、UINT wMsgFilterMin,UINT wMsgFilterMax);在这两种方法中,变量 h W n d指定要截获消息的窗口,如果该变量设为 N U L L,所有窗口消息将被截获。w M s g F i l t e r M i n和w M s g F i l t e r M a x变量与SendMessage()中的变量M s g相对应,指定查看消息的范围。如果用“0,0”,则所有的消息都将被截获。如果用 W M _ K E Y F I R S T,W M _ K E Y L A S T或W M _ M O U S E F I R S T,WM_MOUSELAST,则所有键盘或鼠标的
8、消息将被截获。w R e m o v e M s g变 量 指 定 PeekMessage()是 否 应 该 真 正 地 从 队 列 中 删 除 该 消 息。(GetMessage()总是删除消息)。该变量可以取两个值:P M _ R E M O V E,PeekMessage()将删除消息。P M _ N O R E M O V E,PeekMessage()将把消息留在队列里,并返回它的一个拷贝。当然,如果把消息留在消息队列中,然后再次调用 PeekMessage()查看相同类型的消息,则将返回完全相同的消息。第3章 消 息 处 理33下载l p M s g变量是一个指向M S G结构的指
9、针,M S G包含检索到的消息。typedef struct tagMSG H W N Dh w n d;/window handle message is intended forU I N Tm e s s a g e;W PA R A Mw P a r a m;L PA R A Ml P a r a m;D W O R Dt i m e;/the time the message was put in the queueP O I N Tp t;/the location of the mouse cursor when the/message was put in the queue
10、MSG;3.4 三种类型的消息在M F C应用程序中传输的消息有三种类型:窗口消息、命令消息和控件通知。3.4.1 窗口消息窗口消息(Window Message)一般与窗口的内部运作有关,如创建窗口、绘制窗口和销毁窗口等。通常,消息是从系统发送到窗口,或从窗口发送到窗口。当用S e n d M e s s a g e()或P o s t M e s s a g e()发送一个窗口消息时,变量 M e s s a g e、w P a r a m和l P a r a m的格式如下:M e s s a g ewPa r a mlPa r a mW M _ X X X定义的命令定义的命令W M _
11、X X X可以是许多窗口消息之一,如下列窗口:W M _ C R E AT E,告诉窗口初始化自己。W M _ PA I N T,告诉窗口绘制自己。W M _ M O U S E M O V E,告诉窗口鼠标移经它。有关某些公共窗口消息,参见附录 B。若需要窗口消息的完全的列表,请参考 M F C文档。3.4.2 命令消息命令消息一般与处理用户请求相关,当用户单击一个菜单项或工具栏时,命令消息产生,并被发送到能处理该请求的类对象(如,装载文件、编辑文本和保存选项等)。当用SendMessage()或PostMessage()发送窗口消息时,变量M e s s a g e、w P a r a m
12、和l P a r a m的格式如下:M e s s a g ew P a r a ml P a r a mW M _ C O M M A N D0Command ID0Command ID要么是选中菜单项的I D,要么是被单击的工具栏按钮。注意 Command ID不能大于一个字长,如果使它大于一个字长,系统就只用 0来填充高位字。某些控件通知也用W M _ C O M M A N D消息,区别两种消息的唯一方法是 l P a r a m是否为N U L L。3.4.3 控件通知通常,控件通知在某些重要事件发生时,由控件窗口发送到父窗口,如打开一个组合框。34第一部分 基 础 知 识下载控件通
13、知为父窗口进一步控制子窗口提供了机会。例如,打开一个组合框时,父窗口可以用组合框初建时得不到的消息填充它。控件通知经历了一个演变过程,因而 SendMessage()的变量M e s s a g e、w P a r a m和l P a r a m有三种格式。1.第一控件通知格式第一控件通知格式只是窗口消息的子集。M e s s a g ew P a r a ml P a r a mW M _ X X X定义的命令定义的命令W M _ X X X可以是下面消息中的任何一种:W M _ PA R E N T N O T I F Y表示一个控件窗口要么已被建立或销毁,要么鼠标已单击了该窗口。W M
14、_ C T L C O L O R、W M _ D R AW I T E M、W M _ M E A S U R E I T E M、W M _ D E L E T E I T E M、W M _ C H A RTO I T E M、W M _ V K E Y TO I T E M或W M _ C O M PA R E I T E M都是送往父窗口的消息,用来绘制自身的控件窗口。W M _ H S C R O L L或W M _ V S C R O L L由滚动条控件发送,通知父窗口滚动窗口。2.第二控件通知格式第二控件通知格式使用W M _ C O M M A N D消息,与命令消息共享。M
15、 e s s a g ew P a r a ml P a r a mW M _ C O M M A N DX N _ X X X控件I D窗口句柄l P a r a m变量用来区分是命令消息还是控件通知。控件通知在 l P a r a m中有一个有定义的句柄,用来标识发出通知的控件;而命令消息中 l P a r a m为N U L L。X N _ X X X值因发出通知的控件不同而不同,例如,X N _ X X X值为E N _ C H A N G E,告诉父窗口显示在编辑框控件中的文本已发生变化。还有其他一些例子列在附录 B中。3.第三控件通知格式第三控件通知格式也是最灵活的通知格式,它用
16、W M _ N O T I F Y消息。M e s s a g ew P a r a ml P a r a mW M _ N O T I F Y控件I D指向N M H D R的指针l P a r a m值指向一个结构,该结构包括有关制作该通知的控件的任何内容,而不受空间和类型的限制,该结构叫做N M H D R。typedef struct tagNMHDR HWND hwndFrom;/Window handle of Control Wi n d o w/making the notification.UINT idFrom;/Control ID of Control Wi n d o
17、 w/making the notification.UINT code;/notification code ex:the user/has clicked the Control Wi n d o w NMHDR;N M H D R代表通知消息头(Notification Message Header)。为什么要这个头?因为某些控件用N M H D R作为头发送一个更大结构的消息,即使那些不知道更大结构内容的函数还是能处理第3章 消 息 处 理35下载通知头。3.5 MFC怎样接收一个寄送的消息M F C处理一个寄送和发送消息的唯一明显不同是寄送的消息要在应用程序的消息队列中花费一些时间。
18、在消息泵(message pump)弹出它之前,它要一直在队列中。消息泵M F C应用程序中的消息泵在C Wi n A p p的成员函数Run()中。应用程序开始运行时,Run()就被调用,Run()把时间分割成两部分。一部分用来执行后台处理,如取消临时 C W n d对象;另一部分用来检查消息队列。当一个新的消息进来时,Run()抽取它即用GetMessage()从队列中取出该消息,运行两个消息翻译函数,然后用 DispatchMessage()函数调用该消息预期的目标窗口进程(见图3-2)。图3-2 消息泵执行后台处理并检查消息队列消息泵调用的两个翻译函数是P r e TranslateM
19、essage()和:TranslateMessage()。目标窗口的M F C类可调用P r e Tr a n s l a t e M e s s a g e在发送消息给它之前进行消息翻译,例如,C F r a m e W n d用P r e TranslateMessage()将加速键(如,C t r l+S存储文件)转换为命令消息。翻译前的消息通常被处理掉,而翻译后的消息(如果有的话)将被重新寄送到队列里。:Tr a n s l a t e M e s s a g e是一个窗口函数,将原始键码转换为键字符。消息一旦被DispatchMessage()发送,M F C处理它就像处理SendM
20、essage()发送的消息一样。3.6 MFC怎样处理一个接收到的消息处理接收到的消息的目的非常简单:将消息指向一个函数,该函数通过消息中的消息标识符处理它。非M F C窗口用简单的c a s e语句来实现该目标,每个c a s e语句执行一些函数,或调用其他一些函数。MainWndProc(HWND hWnd,UINT message,W PARAM wParam,L PARAM lParam)s w i t c h(m e s s a g e)case WM_CREAT E:36第一部分 基 础 知 识下载空闲处理(内务处理、后台处理)新的消息出现在消息队列中?翻译字符消息发送消息到窗口进
21、程b r e a k;case WM_PA I N T:b r e a k;d e f a u l t:r e t u r n(D e f WindowProc(hWnd,message,wParam,l P a r a m);r e t u r n(N U L L);任何遗漏的消息将被传输到一个默认的消息处理函数,但是,c a s e语句不能很好地适应C+和封装技术。在C+环境中,要求消息被一个专门处理该类型消息的类的成员函数处理。因此,M F C不采用c a s e语句,而采用更加复杂和回旋的方法。但它允许用私有类处理消息,而只需做下面三件事情:从将要接收消息的C W n d类对象派生类(
22、对于命令消息是C C m d Ta rg e t)。在派生类中写一个处理消息的成员函数。在类中定义一个查找表(叫做消息映像),该表具有成员函数的条目和它要处理的消息的标识符。然后,M F C依次调用下面的函数,指引输入消息到处理函数。1)AfxWndProc()接收消息,寻找消息所属的C W n d对象,然后调用AfxCallWndProc()。2)AfxCallWndProc()存储消息(消息标识符和参数)供未来参考,然后调用WindowProc()。3)WindowProc()发送消息给 OnWndMsg(),然后,如果消息未被处理,则发送给D e f Windowproc()。4)OnW
23、ndMsg()要么为W M _ C O M M A N D消息调用OnCommand(),要么为W M _ N O T I F Y消息调用OnNotify()。任何被遗漏的消息都将是一个窗口消息。OnWndMsg()搜索类的消息映像,以找到一个能处理任何窗口消息的处理函数。如果 O n W n d M s g()不能找到这样的处理函数,则把消息返回到WindowProc(),由它将消息发送给D e f WindowProc()。5)OnCommand()查看这是不是一个控件通知(l P a r a m不是 N U L L);如果它是,OnCommand()就试图将消息映射到制造通知的控件;如果
24、它不是一个控件通知,或者控件拒绝映射的消息,OnCommand()就调用OnCmdMsg()。6)OnNotify()也试图将消息映射到制造通知的控件;如果映射不成功,OnNotify()就调用相同的OnCmdMsg()函数。7)根据接收消息的类,OnCmdMsg()将在一个称为命令传递(Command Routing)的过程中潜在地传递命令消息和控件通知。例如,如果拥有该窗口的类是一个框架类,则命令和通知消息也被传递到视图和文档类,并为该类寻找一个消息处理函数。为什么要消息映像?为什么要消息映像?这毕竟是C+语言;为什么OnWndMsg()不为每个窗口消息调用一个预定义的虚拟函数?因为它太占
25、 C P U。若是那样,当扫描一个消息映像以加速该过程时,OnWndMsg()可能会做出意想不到的事情,并陷入汇编器。注意通过重载WindowProc()、OnWndMsg()、OnCommand()、OnNotify()或第3章 消 息 处 理37下载OnCmdMsg()可以修改这一过程。重载OnWndMsg()可以在窗口消息被排序之前插入该过程。重载OnCommand()或OnNotify()可以在消息被反射之前插入该过程。消息映射和命令传递将在下面部分进行详细讨论。现在,一步一步地跟踪窗口进程接收和解释一个消息。1.AfxWndProc()所有M F C窗口的窗口进程是:L R E S
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- MFC 编程 案例 chapter003
限制150内