第0607讲mfc开发基础消息处理_第1页
第0607讲mfc开发基础消息处理_第2页
第0607讲mfc开发基础消息处理_第3页
第0607讲mfc开发基础消息处理_第4页
第0607讲mfc开发基础消息处理_第5页
已阅读5页,还剩34页未读 继续免费阅读

付费下载

下载本文档

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

文档简介

中科研CASoft软件工程师培训讲义语音网络聊天第6~7章MFC消息处理机制和自定义消息王俊峰中科天地软件人才培训中心Created

byJunfeng

Wang本讲重点提要¤

消息的类型¤发送或者寄送Windows消息的本质¤

MFC如何实现发送或者寄送消息¤

MFC如何接收和处理发送或者寄送的消息¤

MFC中的自定义消息分类¤

如何定义自定义消息¤

自定义消息的处理¤

利用自定义消息通讯消息的种类¤

Windows系统概括地讲,有三种类型的消息:1、窗口消息;

2、命令(Command)消息;3、控件通知(Control

Notification)消息;窗口消息窗口消息窗口消息(Window

Message)一般与窗口的内部运作有关,如创建窗口、绘制窗口和销毁窗口等。通常,消息是从系统发送到窗口,或从窗口发送到窗口。当用SendMessage()或PostMessage()发送一个窗口消息时,变量Message、wParam和lParam的格式如下:MessageWM_XXXwParam定义的命令lParam定义的命令WM_XXX可以是许多窗口消息之一,如下列窗口:

WM_CREATE,告诉窗口初始化自己。

WM_PAINT,告诉窗口绘制自己。

WM_MOUSEMOVE,告诉窗口鼠标移经它。参见:【窗口消息】例子,该例子相应了窗口的两个消息

WM_CREATE和WM_CLSOE。命令消息命令消息命令消息一般与处理用户请求相关,当用户单击一个菜单项或工具栏时,命令消息产生,并被发送到能处理该请求的类对象(如,装载文件、编辑文本和保存选项等)。当用SendMessage()或

PostMessage()发送窗口消息时,变量Message、wParam和lParam的格式如下:Message

wParam

lParamWM_COMMAND 0Command

ID

0Command

ID要么是选中菜单项的ID,要么是被单击的工具栏按钮。注意Command

ID不能大于一个字长,如果使它大于一个字长,系统就只用0来填充高位字。某些控件通知也用WM_COMMAND消息,区别两种消息的唯一方法是lParam是否为NULL。参见:【命令消息】例子。本例子主要处理了对菜单消息的相应。

当用户在窗口上单击鼠标右键时,弹出菜单,用户选择不同的菜单,系统发送命令消息。控件通知消息控件通知消息通常,控件通知在某些重要事件发生时,由控件窗口发送到父窗口,如打开一个组合框。控件通知为父窗口进一步控制子窗口提供了机会。例如,打开一个组合框时,父窗口可以用组合框初建时得不到的消息填充它。控件通知经历了一个演变过程,因而SendMessage()的变量Message、wParam和lParam有三种格式。1.

第一控件通知格式第一控件通知格式只是窗口消息的子集。Message

wParam

lParamWM_XXX

定义的命令 定义的命令控件通知消息(续)WM_XXX可以是下面消息中的任何一种:WM_PARENTNOTIFY表示一个控件窗口要么已被建立或销毁,要么鼠标已单击了该窗口。WM_CTLCOLOR、WM_DRAWITEM、WM_MEASUREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKEYTOITEM或WM_COMPAREITEM都是送往父窗口的消息,用来绘制自身的控件窗口。WM_HSCROLL或WM_VSCROLL由滚动条控件发送,通知父窗口滚动窗口。2.第二控件通知格式第二控件通知格式使用WM_COMMAND消息,与命令消息共享。MessagewParamlParamWM_COMMANDXN_XXX控件ID窗口句柄控件通知消息(续)lParam变量用来区分是命令消息还是控件通知。控件通知在lP

aram中有一个有定义的句柄,用来标识发出通知的控件;而命令消息中lParam为NULL。XN_XXX值因发出通知的控件不同而不同,例如,

XN_XXX值为EN_CHANGE,告诉父窗口显示在编辑框控件中的文本已发生变化。3.

第三控件通知格式第三控件通知格式也是最灵活的通知格式,它用WM_NOTIFY消息。MessagewParamlParamWM_NOTIFY控件ID指向NMHDR的指针控件通知消息(续)lParam值指向一个结构,该结构包括有关制作该通知的控件的任何内容,而不受空间和类型的限制,该结构叫做NMHDR。typedef

struct

tagNMHDR

{HWNDhwndFrom;

//

Window

handle

of

Control

Wi

n

d

o

w//

making

the

notification.UINT

idFrom;

//

Control

ID

of

Control

Wi

n

d

o

w//

making

the

notification.UINT

code;

//

notification

code

ex:

theuser//

has

clicked

the

Control

Win

d

o

w}

NMHDR;NMHDR代表通知消息头(Notification

Message

Header)。为什么要这个头?因为某些控件用NMHDR作为头发送一个更大结构的消息,即使那些不知道更大结构内容的函数还是能处理通知头。参见:【通知消息】例子。演示了一个简单的下拉列表框控件所发出的选择改变的通知消息。发送和寄送Windows消息每个窗口使用窗口进程处理发送给它的消息。消息可以来自系统、你的应用程序或别的应用程序。消息告诉窗口进程执行某个任务(如初始化自己、绘制或销毁一个窗口等),或者通知它发生某个事件(如鼠标正单击窗口)。系统或应用程序有两种传输消息的方法:

1、发送消息;2、寄送消息;那么这两种方式有什么不同呢?发送一个消息时,直接调用窗口的窗口进程。通信是即时的,直到窗口进程为调用函数返回一个结果后,应用程序才能继续。寄送一个消息寄送一个消息时,把消息发送到拥有那个窗口的应用程序消息队列中。一有空闲,应用程序就搜索消息队列,并在消息队列中处理消息,即从队列中删除它们,并将它们发送到即定窗口。通信将可能延迟,直到目标应用程序获得处理消息的时间。调用函数发送消息后即返回,但结果只是表示消息寄送成功与否,而不是被调用窗口进程的结果。发送和寄送消息的比较鼠标和键盘消息通常是寄送的,而所有其他消息通常都是发送的。在消息队列中,寄送的消息接受特殊的鼠标和键盘处理。通常,应该尽量发送一个消息,除非想把动作延迟到所有鼠标和键盘消息被处理之后。使用MFC发送一个消息用MFC发送一个消息的方法是,首先,应获取接收消息的CWnd类对象的指针;然后,调用CWnd的成员函数SendMessage()。LRESULT

Res=pWnd->SendMessage(UINTMsg,

WPARAMwParam,LPARAM

lParam);

pWnd指针指向目标CWnd类对象。变量Msg是消息,wParam和lParam变量包含消息的参数,如鼠标单击哪里或选择了什么菜单项。目标窗口返回的消息结果放在变量Res中。发送消息到一个没有CWnd类对象的窗口,可以用下列目标窗口的句柄直接调用

Windows

API:LRESULT

Res=::SendMessage(HWND

hWnd

,

UINT

Msg

,WPARAM

wParam

,

LPARAM

lParam);这里的hWnd是目标窗口的句柄。使用MFC寄送一个消息用MFC寄送一个消息与发送一个消息几乎相同,但寄送时用PostMessage(),而不是SendMessage();返回值Res也不一样,Res不是一个由目标窗口返回的值,而是一个布尔值,用来表示消息是否成功地放到消息队列中。检索一个寄送消息正常情况下,一旦消息被寄送后,应用程序在后台发送它。但是在特殊情况下,需要你自己去删除一个消息,例如想在应用程序接收到某种消息之前停止应用程序。有两种方法可以从应用程序消息队列中删除一个消息,但这两种方法都没有涉及MFC。使用MFC寄送一个消息(续)第一种方法:在不干扰任何事情之下窥视消息队列,看看一个消息是否在那里。BOOL

res=::PeekMessage(LPMSG

lpMsg,

HWND

hWnd,

UINTwMsFilterMin,

UINT

wMsgFilterMax,

UINT

wRemoveMsg

)

;第二种方法:实际上是等待,一直等到一个新的消息到达队列为止,然后删除并返回该消息。BOOL

res=::GetMessage(LPMSG

lpMsg,

HWND

hWnd,

UINTwMsgFilterMin,

UINTwMsgFilterMax);在这两种方法中,变量hWnd指定要截获消息的窗口,如果该变量设为NULL,所有窗口消息将被截获。

wMsgFilterMin和wMsgFilterMax变量与

SendMessage()中的变量Msg相对应,指定查看消息的范围。如果用“0,0”,则所有的消息都将被截获。如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELAST,则所有键盘或鼠标的消息将被截获。使用MFC寄送一个消息(续)wRemoveMsg变量指定PeekMessage()是否应该真正地从队列中删除该消息。(GetMessage()总是删除消息)。该变量可以取两个值:PM_REMOVE,PeekMessage()将删除消息。

PM_NOREMOVE,PeekMessage()将把消息留在队列里,并返回它的一个拷贝。当然,如果把消息留在消息队列中,然后再次调用PeekMessage()查看相同类型的消息,则将返回完全相同的消息。lpMsg变量是一个指向MSG结构的指针,MSG包含检索到的消息。

typedef

struct

tagMSG{HWNDhwnd;//消息投向的窗口句柄UINT message

;WPARAM

wParam

;LPARAM

lParam

;DWORDtime;//消息投递到队列的时间POINTpt;//消息投递到队列时鼠标的位置}

MSG;MFC怎样接收一个寄送的消息MFC处理一个寄送和发送消息的唯一明显不同是寄送的消息要在应用程序的消息队列中花费一些时间。在消息泵(messagepump)弹出它之前,它要一直在队列中。消息泵MFC应用程序中的消息泵在CWinApp的成员函数Run()中。应用程序开始运行时,Run()就被调用,Run()把时间分割成两部分。一部分用来执行后台处理,如取消临时CWnd对象;另一部分用来检查消息队列。当一个新的消息进来时,Run()抽取它—即用GetMessage()从队列中取出该消息,运行两个消息翻译函数,然后用DispatchMessage()函数调用该消息预期的目标窗口进程。MFC怎样接收一个寄送的消息(续)消息泵调用的两个翻译函数是PreTranslateMessage()和::

TranslateMessage()。目标窗口的MFC类可调用

PreTranslateMessage在发送消息给它之前进行消息翻译,例如,

CFrameWnd用PreTranslateMessage()将加速键(如,Ct

rl+S存储文件)转换为命令消息。翻译前的消息通常被处理掉,而翻译后的消息(如果有的话)将被重新寄送到队列里。::TranslateMessage是一个窗口函数,将原始键码转换为键字符。消息一旦被DispatchMessage()发送,MFC处理它就像处理SendMessage()发送的消息一样。(如图所示)MFC怎样接收一个发送的消息处理接收到的消息的目的非常简单:将消息指向一个函数,该函数通过消息中的消息标识符处理它。非MFC窗口用简单的case语句来实现该目标,每个case语句执行一些函数,或调用其他一些函数。MainWndProc(HWND

hWnd,

UINT

message,

WPARAM

wParam,LPARAM

lParam){switch

(message){case

WM_CREATE:……break;default:return

(DefWindowProc(hWnd,

message,

wParam,lParam));}return

(NULL);}MFC怎样接收一个发送的消息任何遗漏的消息将被传输到一个默认的消息处理函数,但是,case语句不能很好地适应C++和封装技术。在

C++环境中,要求消息被一个专门处理该类型消息的类的成员函数处理。因此,MFC不采用case语句,而采用更加复杂和回旋的方法。但它允许用私有类处理消息,而只需做下面三件事情:1、从将要接收消息的CWnd类对象派生类(对于命令消息是CCmdTarget)。2、在派生类中写一个处理消息的成员函数。3、在类中定义一个查找表(叫做消息映像),该表具有成员函数的条目和它要处理的消息的标识符。MFC如何把消息和处理函数联系起来MFC依次调用下面的函数,指引输入消息到处理函数。AfxWndProc()接收消息,寻找消息所属的CWnd对象,然后调用AfxCallWndProc()。AfxCallWndProc()存储消息(消息标识符和参数)供未来参考,然后调用WindowProc()。WindowProc()发送消息给OnWndMsg(),然后,如果消息未被处理,则发送给DefWindowproc()。OnWndMsg()要么为WM_COMMAND消息调用OnCommand(),要么为WM_NOTIFY消息调用OnNotify()。任何被遗漏的消息都将是一个窗口消息。OnWndMsg()

搜索类的消息映像,以找到一个能处理任何窗口消息的

处理函数。如果O

n

W

n

d

M

s

g()不能找到这样的

处理函数,则把消息返回到WindowProc(),由它将消

息发送给DefWindowProc()。MFC如何把消息和处理函数联系起来(续)OnCommand()查看这是不是一个控件通知(lParam不是NULL);如果它是,OnCommand()就试图将消息映射到制造通知的控件;如果它不是一个控件通知,或者控件拒绝映射的消息,OnCommand()就调用

OnCmdMsg()。OnNotify()也试图将消息映射到制造通知的控件;如果映射不成功,OnNotify()就调用相同的OnCmdMsg()函数。根据接收消息的类,OnCmdMsg()将在一个称为命令传递(Command

Routing)的过程中潜在地传递命令消息和控件通知。例如,如果拥有该窗口的类是一个框架类,则命令和通知消息也被传递到视图和文档类,并为该类

寻找一个消息处理函数。为什么要采用消息映射?

为什么要消息映像?这毕竟是C++语言;为什么

OnWndMsg()不为每个窗口消息调用一个预定义的虚拟函数?因为它太占CPU。若是那样,当扫描一个消息映像以加速该过程时,OnWndMsg()可能会做出意想不到的事情,并陷入汇编器。注意通过重载WindowProc()、OnWndMsg()、OnCommand()、OnNotify()或OnCmdMsg()可以修改这一过程。重载OnWndMsg()可以在窗口消息被排序之前插入该过程。重载OnCommand()或OnNotify()可以在消息被反射之前插入该过程。MFC消息映射的实现每个消息映像都被括在两个宏之间BEGIN_MESSAGE_MAP(CXxx

,

CYyy):::Message

Map

entries:::END_MESSAGE_MAPCXxx是该类的派生类名,CYyy是OnWndMsg()要搜索的下一个派生类名,通常CXxx派生于CYyy;在下一个类中的BEGIN_MESSAGE_MAP宏标识派生它的类,以此类推,直到所有类中的消息映像都被搜索为止。MFC消息映射的实现(续)每个消息映像条目使用下面的结构:struct

AFX_MSGMAP_ENTRY{UINT

nMessage;UINT

nCode;UINT

nID;UINT

nLastID;UINT

nSig;AFX_PMSG

pfn;

}

;1、UNINT

nMessage标识特定的W

M_

X

X

X消息。2、UNINTnCode是控件通知代码,对于窗口消息该值为0。我们将在下文中看到,处理命令消息和控件通知的函数使用与此相同的消息映像。3、UINT

nID是命令I

D,对于窗口消息该值为0。4、UINT

nLastID是以nID开始的命令ID范围内的最后一个命令I

D,对于窗口消息该值为0。处理命令消息的函数能够处理某一范围的I

D。5、UINT

nSig定义消息处理函数的调用变量。在AFXMSG_.H中,为n

S

i

g预定义了60多个值,例如,nSig值为iw

w,则在调用消息处理函数前,使OnWndMsg()格式化w

P

aram和l

P

a

r

a

m为两个U

I

N

T变量,返回值为整型。6、AFX_PMSG

pfn是消息处理函数的地址。MFC消息映射的实现(续)因此,当OnWndMsg()得到一个窗口消息时,在它的派生类中寻找第一个消息映像。然后,OnWndMsg()扫描那里的条目进行nMessage匹配,如果找到,它就格式化

wParam和lParam为nSig指定的调用变量,并调用pfn指定的函数。如果没有在第一个消息映像中找到一个条目,它将检查BEGIN_MESSAGE_MAP宏找出下一个要扫描的消息映像,以此类推,直到检查完一个对象中的所有消息映像为止。如果OnWndMsg()扫描一个对象中的所有消息映像后,没有发现消息处理函数,则窗口消息返回给WindowProc(),接着由它将消息发送给

DefWindowProc()。建议:授课老师通过VC编译环境的断点调试技术实时和学员演示消息在MFC框架中的映射原理。自定义窗口消息概述¤消息不仅来自系统内部的标准消息,而且可以自定义自己的窗口类型的消息。这大大地增加了用户控制的灵活性。¤从WM_NULL到WM_USER有70个系统定义的窗口消息。根据自己的意愿,用户可以创建两种自定义的窗口消息:静态定义的大于WM_USER值的窗口消息,在给定字符串标识符时由系统动态定义的窗口消息。注意:请记住,窗口消息主要是为窗口内在运行机制设计的,而不是为处理用户命令设计的。如果只想告诉应用程序的一部分或别的应用程序处理一个用户命令,那么用已定义的WM_COMMAND窗口消息和一个新的命令ID。静态分配的窗口消息¤Windows保留了0和WM_USER-1之间的整数范围,作为系统定义的窗口消息;还有一个从WM_USER一直到OX7fff的整数范围,留给自定义消息用。可以用一个简单的#define语句定义消息:#define

WM_MYMESSAGE1

WM_USER#define

WM_MYMESSAGE2

WM_USER+1……然后,可以像处理任何其他窗口消息一样,发送或寄送这些消息。SendMessage(WM_MYMESSAGE1,wParam,lParam)或PostMessage(WM_MYMESSAGE1,

wParam,

lParam).静态分配的窗口消息的映射¤

可以用下面的宏在消息映像中获取这些新消息:

ON_MESSAGE(WM_MYMESSAGE1,Handler)在这里,处理函数将具有如下格式:

LRESULT

Handler(WPARAM,

LPARAM){}¤通过修改下面的宏,可以定义完全属于用户自己的消息映像宏。#define

ON_MY_MESSAGE1()

\{WM_MYMESSAGE1,

0,

0,

0,

AfxSig_lwl,

(AFX_PMSG)(AFX_PMSGW)\(LRESULT(AFX_MSG_CALL

CWnd::*)(WPARAM,

PARAM))

&OnMyMessage1

}

在这里,自动赋予处理函数名为OnMessage1,但还是具有相同的调用变量。为了改变调用变量,在AfXMSG_.H文件中寻找一个更加合适的n

S

i

g值,并替换AfxSig_lwl,然后修改宏中的最后一行,以映射新变量。动态分配的窗口消息¤为了在应用程序间发送消息,应该用下面的语句创建一个基于描述性字符串的新的窗口消息。UINT

wm_MyMessage1=::RegisterWindowMessage(LPCSTRIdentifier);Identifier是描述性字符串。wm_MyMessage1是在0xc000和0xffff之间的一个动态分配的窗口消息。¤

为了处理在消息映像中注册的消息,可以用:ON_REGISTERED_MESSAGE(wm_MyMessage1,Handler)把由RegisterWindowMessage()分配的值传送给它。¤

该消息的处理函数看起来像:LRESULT

Handler(WPARAM,

LPARAM){}动态分配的窗口消息(续)¤

一个动态分配的窗口消息使得应用程序间窗口消

息的维护更容易。因为用户不必关心是否每个应

用程序都知道WM_MYMESSAGE1是一个特定的

WM_USER+n值,而且所有应用程序可以用一个

像“My

Message1”这样的字符串来标识消息,而

RegisterWindowMessage()计算出所用的整数值。例,一个应用程序首先用标识符“ProgramButton”注册了一个消息,则RegisterWindowMessage()就可能给它分配一个值0xc012。如果另外一个应用

程序注册了相同的标识符,也将返回前面分配的

值0xc012。现在,两个应用程序便可以对窗口消

息进行互相通信,即使它们是在不同时间用不同

组的WM_USER定义写的。VC中自定义消息的实现原理¤

ClassWizard不允许增加用户自定义消息,所以你必须手工输入。输入后,ClassWizard就可以象处理其它消息一样处理你自定义

的消息了。¤

下面是增加自定义消息的步骤:1、定义消息。Microsoft推荐用户自定义消息至少是

WM_USER+100,因为很多新控件也要使用WM_USER消息。2、实现消息处理函数。该函数使用WPRAM和LPARAM参数并返回

LPESULT。LPESULT

CMainFrame::OnMyMessage(WPARAMwParam,

LPARAMlParam){//TODO:处理用户自定义消息...return

0;}VC中自定义消息的实现原理(续)3、在类头文件的AFX_MSG块中说明消息处理函数:class

CMainFrame:public

CMDIFrameWnd{...//一般消息映射函数

protected://

{{AFX_MSG(CMainFrame)afx_msg

int

OnCreate(LPCREATESTRUCTlpCreateStruct);afx_msg

void

OnTimer(UINT

nIDEvent);afx_msg

LRESULT

OnMyMessage(WPARAM

wParam,LPARAM

lParam);//}}AFX_MSGDECLARE_MESSAGE_MAP()}VC中自定义消息的实现原理(续)4、在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中。BEGIN_MESSAGE_MAP(CMainFrame,

CMDIFrameWnd)//{{AFX_MSG_MAP(CMainFrame)ON_WM_CREATE()ON_WM_TIMER()ON_MESSAGE(WM_MY_MESSAGE,

OnMyMessage)//}}AFX_MSG_MAPE

温馨提示

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

评论

0/150

提交评论