消息映射.docx

上传人:b****5 文档编号:8074710 上传时间:2023-01-28 格式:DOCX 页数:17 大小:22.21KB
下载 相关 举报
消息映射.docx_第1页
第1页 / 共17页
消息映射.docx_第2页
第2页 / 共17页
消息映射.docx_第3页
第3页 / 共17页
消息映射.docx_第4页
第4页 / 共17页
消息映射.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

消息映射.docx

《消息映射.docx》由会员分享,可在线阅读,更多相关《消息映射.docx(17页珍藏版)》请在冰豆网上搜索。

消息映射.docx

消息映射

消息映射的实现

Windows消息概述

Windows应用程序的输入由Windows系统以消息的形式发送给应用程序的窗口。

这些窗口通过窗口过程来接收和处理消息,然后把控制返还给Windows。

消息的分类

队列消息和非队列消息

从消息的发送途径上看,消息分两种:

队列消息和非队列消息。

队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。

这里,对消息队列阐述如下:

Windows维护一个系统消息队列(Systemmessagequeue),每个GUI线程有一个线程消息队列(Threadmessagequeue)。

鼠标、键盘事件由鼠标或键盘驱动程序转换成输入消息并把消息放进系统消息队列,例如WM_MOUSEMOVE、WM_LBUTTONUP、WM_KEYDOWN、WM_CHAR等等。

Windows每次从系统消息队列移走一个消息,确定它是送给哪个窗口的和这个窗口是由哪个线程创建的,然后,把它放进窗口创建线程的线程消息队列。

线程消息队列接收送给该线程所创建窗口的消息。

线程从消息队列取出消息,通过Windows把它送给适当的窗口过程来处理。

除了键盘、鼠标消息以外,队列消息还有WM_PAINT、WM_TIMER和WM_QUIT。

这些队列消息以外的绝大多数消息是非队列消息。

系统消息和应用程序消息

从消息的来源来看,可以分为:

系统定义的消息和应用程序定义的消息。

系统消息ID的范围是从0到WM_USER-1,或0X80000到0XBFFFF;应用程序消息从WM_USER(0X0400)到0X7FFF,或0XC000到0XFFFF;WM_USER到0X7FFF范围的消息由应用程序自己使用;0XC000到0XFFFF范围的消息用来和其他应用程序通信,为了ID的唯一性,使用:

:

RegisterWindowMessage来得到该范围的消息ID。

消息结构和消息处理

消息的结构

为了从消息队列获取消息信息,需要使用MSG结构。

例如,:

:

GetMessage函数(从消息队列得到消息并从队列中移走)和:

:

PeekMessage函数(从消息队列得到消息但是可以不移走)都使用了该结构来保存获得的消息信息。

MSG结构的定义如下:

typedefstructtagMSG{//msg

HWNDhwnd;

UINTmessage;

WPARAMwParam;

LPARAMlParam;

DWORDtime;

POINTpt;

}MSG;

该结构包括了六个成员,用来描述消息的有关属性:

接收消息的窗口句柄、消息标识(ID)、第一个消息参数、第二个消息参数、消息产生的时间、消息产生时鼠标的位置。

应用程序通过窗口过程来处理消息

如前所述,每个“窗口类”都要登记一个如下形式的窗口过程:

LRESULTCALLBACKMainWndProc(

HWNDhwnd,//窗口句柄

UINTmsg,//消息标识

WPARAMwParam,//消息参数1

LPARAMlParam//消息参数2

应用程序通过窗口过程来处理消息:

非队列消息由Windows直接送给目的窗口的窗口过程,队列消息由:

:

DispatchMessage等派发给目的窗口的窗口过程。

窗口过程被调用时,接受四个参数:

awindowhandle(窗口句柄);

amessageidentifier(消息标识);

two32-bitvaluescalledmessageparameters(两个32位的消息参数);

需要的话,窗口过程用:

:

GetMessageTime获取消息产生的时间,用:

:

GetMessagePos获取消息产生时鼠标光标所在的位置。

在窗口过程里,用switch/case分支处理语句来识别和处理消息。

应用程序通过消息循环来获得对消息的处理

每个GDI应用程序在主窗口创建之后,都会进入消息循环,接受用户输入、解释和处理消息。

消息循环的结构如下:

while(GetMessage(&msg,(HWND)NULL,0,0)){//从消息队列得到消息

if(hwndDlgModeless==(HWND)NULL||

!

IsDialogMessage(hwndDlgModeless,&msg)&&

!

TranslateAccelerator(hwndMain,haccel,&msg)){

TranslateMessage(&msg);

DispatchMessage(&msg);//发送消息

}

}

消息循环从消息队列中得到消息,如果不是快捷键消息或者对话框消息,就进行消息转换和派发,让目的窗口的窗口过程来处理。

当得到消息WM_QUIT,或者:

:

GetMessage出错时,退出消息循环。

MFC消息处理

使用MFC框架编程时,消息发送和处理的本质也如上所述。

但是,有一点需要强调的是,所有的MFC窗口都使用同一窗口过程,程序员不必去设计和实现自己的窗口过程,而是通过MFC提供的一套消息映射机制来处理消息。

因此,MFC简化了程序员编程时处理消息的复杂性。

所谓消息映射,简单地讲,就是让程序员指定要某个MFC类(有消息处理能力的类)处理某个消息。

MFC提供了工具ClassWizard来帮助实现消息映射,在处理消息的类中添加一些有关消息映射的内容和处理消息的成员函数。

程序员将完成消息处理函数,实现所希望的消息处理能力。

如果派生类要覆盖基类的消息处理函数,就用ClassWizard在派生类中添加一个消息映射条目,用同样的原型定义一个函数,然后实现该函数。

这个函数覆盖派生类的任何基类的同名处理函数。

下面几节将分析MFC的消息机制的实现原理和消息处理的过程。

为此,首先要分析ClassWizard实现消息映射的内幕,然后讨论MFC的窗口过程,分析MFC窗口过程是如何实现消息处理的。

消息映射的定义和实现

MFC处理的三类消息

根据处理函数和处理过程的不同,MFC主要处理三类消息:

Windows消息,前缀以“WM_”打头,WM_COMMAND例外。

Windows消息直接送给MFC窗口过程处理,窗口过程调用对应的消息处理函数。

一般,由窗口对象来处理这类消息,也就是说,这类消息处理函数一般是MFC窗口类的成员函数。

控制通知消息,是控制子窗口送给父窗口的WM_COMMAND通知消息。

窗口过程调用对应的消息处理函数。

一般,由窗口对象来处理这类消息,也就是说,这类消息处理函数一般是MFC窗口类的成员函数。

需要指出的是,Win32使用新的WM_NOFITY来处理复杂的通知消息。

WM_COMMAND类型的通知消息仅仅能传递一个控制窗口句柄(lparam)、控制窗ID和通知代码(wparam)。

WM_NOTIFY能传递任意复杂的信息。

命令消息,这是来自菜单、工具条按钮、加速键等用户接口对象的WM_COMMAND通知消息,属于应用程序自己定义的消息。

通过消息映射机制,MFC框架把命令按一定的路径分发给多种类型的对象(具备消息处理能力)处理,如文档、窗口、应用程序、文档模板等对象。

能处理消息映射的类必须从CCmdTarget类派生。

在讨论了消息的分类之后,应该是讨论各类消息如何处理的时候了。

但是,要知道怎么处理消息,首先要知道如何映射消息。

MFC消息映射的实现方法

MFC使用ClassWizard帮助实现消息映射,它在源码中添加一些消息映射的内容,并声明和实现消息处理函数。

现在来分析这些被添加的内容。

在类的定义(头文件)里,它增加了消息处理函数声明,并添加一行声明消息映射的宏DECLARE_MESSAGE_MAP。

在类的实现(实现文件)里,实现消息处理函数,并使用IMPLEMENT_MESSAGE_MAP宏实现消息映射。

一般情况下,这些声明和实现是由MFC的ClassWizard自动来维护的。

看一个例子:

在AppWizard产生的应用程序类的源码中,应用程序类的定义(头文件)包含了类似如下的代码:

//{{AFX_MSG(CTttApp)

afx_msgvoidOnAppAbout();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

应用程序类的实现文件中包含了类似如下的代码:

BEGIN_MESSAGE_MAP(CTApp,CWinApp)

//{{AFX_MSG_MAP(CTttApp)

ON_COMMAND(ID_APP_ABOUT,OnAppAbout)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

头文件里是消息映射和消息处理函数的声明,实现文件里是消息映射的实现和消息处理函数的实现。

它表示让应用程序对象处理命令消息ID_APP_ABOUT,消息处理函数是OnAppAbout。

为什么这样做之后就完成了一个消息映射?

这些声明和实现到底作了些什么呢?

接着,将讨论这些问题。

在声明与实现的内部

DECLARE_MESSAGE_MAP宏:

首先,看DECLARE_MESSAGE_MAP宏的内容:

#ifdef_AFXDLL

#defineDECLARE_MESSAGE_MAP()\

private:

\

staticconstAFX_MSGMAP_ENTRY_messageEntries[];\

protected:

\

staticAFX_DATAconstAFX_MSGMAPmessageMap;\

staticconstAFX_MSGMAP*PASCAL_GetBaseMessageMap();\

virtualconstAFX_MSGMAP*GetMessageMap()const;\

#else

#defineDECLARE_MESSAGE_MAP()\

private:

\

staticconstAFX_MSGMAP_ENTRY_messageEntries[];\

protected:

\

staticAFX_DATAconstAFX_MSGMAPmessageMap;\

virtualconstAFX_MSGMAP*GetMessageMap()const;\

#endif

DECLARE_MESSAGE_MAP定义了两个版本,分别用于静态或者动态链接到MFCDLL的情形。

BEGIN_MESSAE_MAP宏

然后,看BEGIN_MESSAE_MAP宏的内容:

#ifdef_AFXDLL

#defineBEGIN_MESSAGE_MAP(theClass,baseClass)\

constAFX_MSGMAP*PASCALtheClass:

:

_GetBaseMessageMap()\

{return&baseClass:

:

messageMap;}\

constAFX_MSGMAP*theClass:

:

GetMessageMap()const\

{return&theClass:

:

messageMap;}\

AFX_DATADEFconstAFX_MSGMAPtheClass:

:

messageMap=\

{&theClass:

:

_GetBaseMessageMap,&theClass:

:

_messageEntries[0]};\

constAFX_MSGMAP_ENTRYtheClass:

:

_messageEntries[]=\

{\

#else

#defineBEGIN_MESSAGE_MAP(theClass,baseClass)\

constAFX_MSGMAP*theClass:

:

GetMessageMap()const\

{return&theClass:

:

messageMap;}\

AFX_DATADEFconstAFX_MSGMAPtheClass:

:

messageMap=\

{&baseClass:

:

messageMap,&theClass:

:

_messageEntries[0]};\

constAFX_MSGMAP_ENTRYtheClass:

:

_messageEntries[]=\

{\

#endif

#defineEND_MESSAGE_MAP()\

{0,0,0,0,AfxSig_end,(AFX_PMSG)0}\

};\

对应地,BEGIN_MESSAGE_MAP定义了两个版本,分别用于静态或者动态链接到MFCDLL的情形。

END_MESSAGE_Top

5楼rivershan(阿门)回复于2002-11-0309:

22:

07得分0

成员函数

_GetBaseMessageMap()

用来得到基类消息映射的函数。

GetMessageMap()

用来得到自身消息映射的函数。

消息映射实现的解释

消息映射实现的实质是初始化声明中定义的静态成员函数_messageEntries和messageMap,实现所声明的静态或虚拟函数GetMessageMap、_GetBaseMessageMap。

这样,在进入WinMain函数之前,每个可以响应消息的MFC类都生成了一个消息映射表,程序运行时通过查询该表判断是否需要响应某条消息。

对消息映射入口表(消息映射数组)的初始化

如前所述,消息映射数组的元素是消息映射条目,条目的格式符合结构AFX_MESSAGE_ENTRY的描述。

所以,要初始化消息映射数组,就必须使用符合该格式的数据来填充:

如果指定当前类处理某个消息,则把和该消息有关的信息(四个)和消息处理函数的地址及原型组合成为一个消息映射条目,加入到消息映射数组中。

显然,这是一个繁琐的工作。

为了简化操作,MFC根据消息的不同和消息处理方式的不同,把消息映射划分成若干类别,每一类的消息映射至少有一个共性:

消息处理函数的原型相同。

对每一类消息映射,MFC定义了一个宏来简化初始化消息数组的工作。

例如,前文提到的ON_COMMAND宏用来映射命令消息,只要指定命令ID和消息处理函数即可,因为对这类命令消息映射条目,其他四个属性都是固定的。

ON_COMMAND宏的初始化内容如下:

{WM_COMMAND,

CN_COMMAND,

(WORD)ID_APP_ABOUT,

(WORD)ID_APP_ABOUT,

AfxSig_vv,

(AFX_PMSG)OnAppAbout

}

这个消息映射条目的含义是:

消息ID是ID_APP_ABOUT,OnAppAbout被转换成AFX_PMSG指针类型,AfxSig_vv是MFC预定义的枚举变量,用来标识OnAppAbout的函数类型为参数空(Void)、返回空(Void)。

在消息映射数组的最后,是宏END_MESSAGE_MAP的内容,它标识消息处理类的消息映射条目的终止。

对messageMap的初始化

如前所述,messageMap的类型是AFX_MESSMAP。

经过初始化,域lpEntries保存了消息映射数组_messageEntries的地址;如果动态链接到MFCDLL,则pfnGetBaseMap保存了_GetBaseMessageMap成员函数的地址;否则pBaseMap保存了基类的消息映射数组的地址。

对函数的实现

_GetBaseMessageMap()

它返回基类的成员变量messagMap(当使用MFCDLL时),使用该函数得到基类消息映射入口表。

GetMessageMap():

它返回成员变量messageMap,使用该函数得到自身消息映射入口表。

顺便说一下,消息映射类的基类CCmdTarget也实现了上述和消息映射相关的函数,不过,它的消息映射数组是空的。

既然消息映射宏方便了消息映射的实现,那么有必要详细的讨论消息映射宏。

下一节,介绍消息映射宏的分类、用法和用途。

消息映射宏的种类

为了简化程序员的工作,MFC定义了一系列的消息映射宏和像AfxSig_vv这样的枚举变量,以及标准消息处理函数,并且具体地实现这些函数。

这里主要讨论消息映射宏,常用的分为以下几类。

用于Windows消息的宏,前缀为“ON_WM_”。

这样的宏不带参数,因为它对应的消息和消息处理函数的函数名称、函数原型是确定的。

MFC提供了这类消息处理函数的定义和缺省实现。

每个这样的宏处理不同的Windows消息。

例如:

宏ON_WM_CREATE()把消息WM_CREATE映射到OnCreate函数,消息映射条目的第一个成员nMessage指定为要处理的Windows消息的ID,第二个成员nCode指定为0。

用于命令消息的宏ON_COMMAND

这类宏带有参数,需要通过参数指定命令ID和消息处理函数。

这些消息都映射到WM_COMMAND上,也就是将消息映射条目的第一个成员nMessage指定为WM_COMMAND,第二个成员nCode指定为CN_COMMAND(即0)。

消息处理函数的原型是void(void),不带参数,不返回值。

除了单条命令消息的映射,还有把一定范围的命令消息映射到一个消息处理函数的映射宏ON_COMMAND_RANGE。

这类宏带有参数,需要指定命令ID的范围和消息处理函数。

这些消息都映射到WM_COMMAND上,也就是将消息映射条目的第一个成员nMessage指定为WM_COMMAND,第二个成员nCode指定为CN_COMMAND(即0),第三个成员nID和第四个成员nLastID指定了映射消息的起止范围。

消息处理函数的原型是void(UINT),有一个UINT类型的参数,表示要处理的命令消息ID,不返回值。

(3)用于控制通知消息的宏

这类宏可能带有三个参数,如ON_CONTROL,就需要指定控制窗口ID,通知码和消息处理函数;也可能带有两个参数,如具体处理特定通知消息的宏ON_BN_CLICKED、ON_LBN_DBLCLK、ON_CBN_EDITCHANGE等,需要指定控制窗口ID和消息处理函数。

控制通知消息也被映射到WM_COMMAND上,也就是将消息映射条目的第一个成员的nMessage指定为WM_COMMAND,但是第二个成员nCode是特定的通知码,第三个成员nID是控制子窗口的ID,第四个成员nLastID等于第三个成员的值。

消息处理函数的原型是void(void),没有参数,不返回值。

还有一类宏处理通知消息ON_NOTIFY,它类似于ON_CONTROL,但是控制通知消息被映射到WM_NOTIFY。

消息映射条目的第一个成员的nMessage被指定为WM_NOTIFY,第二个成员nCode是特定的通知码,第三个成员nID是控制子窗口的ID,第四个成员nLastID等于第三个成员的值。

消息处理函数的原型是void(NMHDR*,LRESULT*),参数1是NMHDR指针,参数2是LRESULT指针,用于返回结果,但函数不返回值。

对应地,还有把一定范围的控制子窗口的某个通知消息映射到一个消息处理函数的映射宏,这类宏包括ON__CONTROL_RANGE和ON_NOTIFY_RANGE。

这类宏带有参数,需要指定控制子窗口ID的范围和通知消息,以及消息处理函数。

对于ON__CONTROL_RANGE,是将消息映射条目的第一个成员的nMessage指定为WM_COMMAND,但是第二个成员nCode是特定的通知码,第三个成员nID和第四个成员nLastID等于指定了控制窗口ID的范围。

消息处理函数的原型是void(UINT),参数表示要处理的通知消息是哪个ID的控制子窗口发送的,函数不返回值。

对于ON__NOTIFY_RANGE,消息映射条目的第一个成员的nMessage被指定为WM_NOTIFY,第二个成员nCode是特定的通知码,第三个成员nID和第四个成员nLastID指定了控制窗口ID的范围。

消息处理函数的原型是void(UINT,NMHDR*,LRESULT*),参数1表示要处理的通知消息是哪个ID的控制子窗口发送的,参数2是NMHDR指针,参数3是LRESULT指针,用于返回结果,但函数不返回值。

(4)用于用户界面接口状态更新的ON_UPDATE_COMMAND_UI宏

这类宏被映射到消息WM_COMMND上,带有两个参数,需要指定用户接口对象ID和消息处理函数。

消息映射条目的第一个成员nMessage被指定为WM_COMMAND,第二个成员nCode被指定为-1,第三个成员nID和第四个成员nLastID都指定为用户接口对象ID。

消息处理函数的原型是void(CCmdUI*),参数指向一个CCmdUI对象,不返回值。

对应地,有更新一定ID范围的用户接口对象的宏ON_UPDATE_COMMAND_UI_RANGE,此宏带有三个参数,用于指定用户接口对象ID的范围和消息处理函数。

消息映射条目的第一个成员nMessage被指定为WM_COMMAND,第二个成员nCode被指定为-1,第三个成员nID和第四个成员nLastID用于指定用户接口对象ID的范围。

消息处理函数的原型是void(CCmdUI*),参数指向一个CCmdUI对象,函数不返回值。

之所以不用当前用户接口对象ID作为参数,是因为CCmdUI对象包含了有关信息。

(5)用于其他消息的宏

例如用于用户定义消息的ON_MESSA

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工作范文 > 行政公文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1