📄 mfc教程_ 消息映射的实现.htm
字号:
<P></P>
<P
align=justify>如前所述,消息映射数组的元素是消息映射条目,条目的格式符合结构AFX_MESSAGE_ENTRY的描述。所以,要初始化消息映射数组,就必须使用符合该格式的数据来填充:如果指定当前类处理某个消息,则把和该消息有关的信息(四个)和消息处理函数的地址及原型组合成为一个消息映射条目,加入到消息映射数组中。</P>
<P
align=justify>显然,这是一个繁琐的工作。为了简化操作,MFC根据消息的不同和消息处理方式的不同,把消息映射划分成若干类别,每一类的消息映射至少有一个共性:消息处理函数的原型相同。对每一类消息映射,MFC定义了一个宏来简化初始化消息数组的工作。例如,前文提到的ON_COMMAND宏用来映射命令消息,只要指定命令ID和消息处理函数即可,因为对这类命令消息映射条目,其他四个属性都是固定的。ON_COMMAND宏的初始化内容如下:</P>
<P align=justify>{WM_COMMAND,</P>
<P align=justify>CN_COMMAND,</P>
<P align=justify>(WORD)ID_APP_ABOUT,</P>
<P align=justify>(WORD)ID_APP_ABOUT,</P>
<P align=justify>AfxSig_vv,</P>
<P align=justify>(AFX_PMSG)OnAppAbout</P>
<P align=justify>}</P>
<P
align=justify>这个消息映射条目的含义是:消息ID是ID_APP_ABOUT,OnAppAbout被转换成AFX_PMSG指针类型,AfxSig_vv是MFC预定义的枚举变量,用来标识OnAppAbout的函数类型为参数空(Void)、返回空(Void)。</P>
<P align=justify>在消息映射数组的最后,是宏END_MESSAGE_MAP的内容,它标识消息处理类的消息映射条目的终止。</P>
<P align=justify></P>
<LI>对messageMap的初始化
<P></P>
<P align=justify>如前所述,messageMap的类型是AFX_MESSMAP。</P>
<P align=justify>经过初始化,域lpEntries保存了消息映射数组_messageEntries的地址;如果动态链接到MFC
DLL,则pfnGetBaseMap保存了_GetBaseMessageMap成员函数的地址;否则pBaseMap保存了基类的消息映射数组的地址。</P>
<P align=justify></P>
<LI>对函数的实现
<P></P></LI></OL>
<P align=justify>_GetBaseMessageMap()</P>
<P align=justify>它返回基类的成员变量messagMap(当使用MFC DLL时),使用该函数得到基类消息映射入口表。</P>
<P align=justify></P>
<P align=justify>GetMessageMap():</P>
<P align=justify>它返回成员变量messageMap,使用该函数得到自身消息映射入口表。</P>
<P align=justify></P>
<P align=justify>顺便说一下,消息映射类的基类CCmdTarget也实现了上述和消息映射相关的函数,不过,它的消息映射数组是空的。</P>
<P align=justify></P>
<P align=justify>既然消息映射宏方便了消息映射的实现,那么有必要详细的讨论消息映射宏。下一节,介绍消息映射宏的分类、用法和用途。</P>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889013></A><A name=_Toc445782416></A><A
name=_Toc452640904></A><A name=_Toc457298972></A><B>消息映射宏的种类</B>
<P></P></LI></OL></OL></OL>
<P
align=justify>为了简化程序员的工作,MFC定义了一系列的消息映射宏和像AfxSig_vv这样的枚举变量,以及标准消息处理函数,并且具体地实现这些函数。这里主要讨论消息映射宏,常用的分为以下几类。</P>
<OL>
<P align=justify>
<LI>用于Windows消息的宏,前缀为“ON_WM_”。
<P></P>
<P
align=justify>这样的宏不带参数,因为它对应的消息和消息处理函数的函数名称、函数原型是确定的。MFC提供了这类消息处理函数的定义和缺省实现。每个这样的宏处理不同的Windows消息。</P>
<P
align=justify>例如:宏ON_WM_CREATE()把消息WM_CREATE映射到OnCreate函数,消息映射条目的第一个成员nMessage指定为要处理的Windows消息的ID,第二个成员nCode指定为0。</P>
<P align=justify></P>
<LI>用于命令消息的宏ON_COMMAND
<P></P></LI></OL>
<P
align=justify>这类宏带有参数,需要通过参数指定命令ID和消息处理函数。这些消息都映射到WM_COMMAND上,也就是将消息映射条目的第一个成员nMessage指定为WM_COMMAND,第二个成员nCode指定为CN_COMMAND(即0)。消息处理函数的原型是void
(void),不带参数,不返回值。</P>
<P
align=justify>除了单条命令消息的映射,还有把一定范围的命令消息映射到一个消息处理函数的映射宏ON_COMMAND_RANGE。这类宏带有参数,需要指定命令ID的范围和消息处理函数。这些消息都映射到WM_COMMAND上,也就是将消息映射条目的第一个成员nMessage指定为WM_COMMAND,第二个成员nCode指定为CN_COMMAND(即0),第三个成员nID和第四个成员nLastID指定了映射消息的起止范围。消息处理函数的原型是void
(UINT),有一个UINT类型的参数,表示要处理的命令消息ID,不返回值。</P>
<P align=justify>(3)用于控制通知消息的宏</P>
<P
align=justify>这类宏可能带有三个参数,如ON_CONTROL,就需要指定控制窗口ID,通知码和消息处理函数;也可能带有两个参数,如具体处理特定通知消息的宏ON_BN_CLICKED、ON_LBN_DBLCLK、ON_CBN_EDITCHANGE等,需要指定控制窗口ID和消息处理函数。</P>
<P
align=justify>控制通知消息也被映射到WM_COMMAND上,也就是将消息映射条目的第一个成员的nMessage指定为WM_COMMAND,但是第二个成员nCode是特定的通知码,第三个成员nID是控制子窗口的ID,第四个成员nLastID等于第三个成员的值。消息处理函数的原型是void
(void),没有参数,不返回值。</P>
<P
align=justify>还有一类宏处理通知消息ON_NOTIFY,它类似于ON_CONTROL,但是控制通知消息被映射到WM_NOTIFY。消息映射条目的第一个成员的nMessage被指定为WM_NOTIFY,第二个成员nCode是特定的通知码,第三个成员nID是控制子窗口的ID,第四个成员nLastID等于第三个成员的值。消息处理函数的原型是void
(NMHDR*, LRESULT*),参数1是NMHDR指针,参数2是LRESULT指针,用于返回结果,但函数不返回值。</P>
<P
align=justify>对应地,还有把一定范围的控制子窗口的某个通知消息映射到一个消息处理函数的映射宏,这类宏包括ON__CONTROL_RANGE和ON_NOTIFY_RANGE。这类宏带有参数,需要指定控制子窗口ID的范围和通知消息,以及消息处理函数。</P>
<P
align=justify>对于ON__CONTROL_RANGE,是将消息映射条目的第一个成员的nMessage指定为WM_COMMAND,但是第二个成员nCode是特定的通知码,第三个成员nID和第四个成员nLastID等于指定了控制窗口ID的范围。消息处理函数的原型是void
(UINT),参数表示要处理的通知消息是哪个ID的控制子窗口发送的,函数不返回值。</P>
<P
align=justify>对于ON__NOTIFY_RANGE,消息映射条目的第一个成员的nMessage被指定为WM_NOTIFY,第二个成员nCode是特定的通知码,第三个成员nID和第四个成员nLastID指定了控制窗口ID的范围。消息处理函数的原型是void
(UINT, NMHDR*,
LRESULT*),参数1表示要处理的通知消息是哪个ID的控制子窗口发送的,参数2是NMHDR指针,参数3是LRESULT指针,用于返回结果,但函数不返回值。</P>
<P align=justify>(4)用于用户界面接口状态更新的ON_UPDATE_COMMAND_UI宏</P>
<P
align=justify>这类宏被映射到消息WM_COMMND上,带有两个参数,需要指定用户接口对象ID和消息处理函数。消息映射条目的第一个成员nMessage被指定为WM_COMMAND,第二个成员nCode被指定为-1,第三个成员nID和第四个成员nLastID都指定为用户接口对象ID。消息处理函数的原型是
void (CCmdUI*),参数指向一个CCmdUI对象,不返回值。</P>
<P
align=justify>对应地,有更新一定ID范围的用户接口对象的宏ON_UPDATE_COMMAND_UI_RANGE,此宏带有三个参数,用于指定用户接口对象ID的范围和消息处理函数。消息映射条目的第一个成员nMessage被指定为WM_COMMAND,第二个成员nCode被指定为-1,第三个成员nID和第四个成员nLastID用于指定用户接口对象ID的范围。消息处理函数的原型是
void (CCmdUI*),参数指向一个CCmdUI对象,函数不返回值。之所以不用当前用户接口对象ID作为参数,是因为CCmdUI对象包含了有关信息。</P>
<P align=justify>(5)用于其他消息的宏</P>
<P
align=justify>例如用于用户定义消息的ON_MESSAGE。这类宏带有参数,需要指定消息ID和消息处理函数。消息映射条目的第一个成员nMessage被指定为消息ID,第二个成员nCode被指定为0,第三个成员nID和第四个成员也是0。消息处理的原型是LRESULT
(WPARAM, LPARAM),参数1和参数2是消息参数wParam和lParam,返回LRESULT类型的值。</P>
<P align=justify>(6)扩展消息映射宏</P>
<P
align=justify>很多普通消息映射宏都有对应的扩展消息映射宏,例如:ON_COMMAND对应的ON_COMMAND_EX,ON_ONTIFY对应的ON_ONTIFY_EX,等等。扩展宏除了具有普通宏的功能,还有特别的用途。关于扩展宏的具体讨论和分析,见4.4.3.2节。</P>
<P align=justify>作为一个总结,下表列出了这些常用的消息映射宏。</P>
<P align=center>表4-1 常用的消息映射宏</P>
<P align=left>
<TABLE cellSpacing=1 cellPadding=7 width=499 border=1>
<TBODY>
<TR>
<TD vAlign=top width="41%">
<P align=justify>消息映射宏 </P></TD>
<TD vAlign=top width="59%">
<P align=justify>用途 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_COMMAND </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把command message映射到相应的函数 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_CONTROL </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把control notification
message映射到相应的函数。MFC根据不同的控制消息,在此基础上定义了更具体的宏,这样用户在使用时就不需要指定通知代码ID,如ON_BN_CLICKED。
</P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_MESSAGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把user-defined message.映射到相应的函数 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_REGISTERED_MESSAGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把registered user-defined
message映射到相应的函数,实际上nMessage等于0x0C000,nSig等于宏的消息参数。nSig的真实值为Afxsig_lwl。
</P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_UPDATE_COMMAND_UI </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把user interface user update command message映射到相应的函数上。
</P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_COMMAND_RANGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把一定范围内的command IDs 映射到相应的函数上 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_UPDATE_COMMAND_UI_RANGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把一定范围内的user interface user update command
message映射到相应的函数上 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_CONTROL_RANGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把一定范围内的control notification message映射到相应的函数上
</P></TD></TR></TBODY></TABLE>
<P></P>
<P align=justify></P>
<DIR>
<P align=justify>在表4-1中,宏ON_REGISTERED_MESSAGE的定义如下:</P></DIR>
<P align=justify>#define ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn)
\</P>
<P align=justify>{ 0xC000, 0, 0, 0,\</P>
<P align=justify>(UINT)(UINT*)(&nMessageVariable), \</P>
<P align=justify>/*implied 'AfxSig_lwl'*/ \</P>
<P align=justify>(AFX_PMSG)(AFX_PMSGW)(LRESULT\</P>
<P align=justify>(AFX_MSG_CALL CWnd::*)\</P>
<P align=justify>(WPARAM, LPARAM))&memberFxn }</P>
<P
align=justify>从上面的定义可以看出,实际上,该消息被映射到WM_COMMAND(0XC000),指定的registered消息ID存放在nSig域内,nSig的值在这样的映射条目下隐含地定为AfxSig_lwl。由于ID和正常的nSig域存放的值范围不同,所以MFC可以判断出是否是registered消息映射条目。如果是,则使用AfxSig_lwl把消息处理函数转换成参数1为Word、参数2为long、返回值为long的类型。</P>
<P
align=justify>在介绍完了消息映射的内幕之后,应该讨论消息处理过程了。由于CCmdTarge的特殊性和重要性,在4.3节先对其作一个大略的介绍。</P>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889014></A><A name=_Toc445782417></A><A
name=_Toc452640905></A><A name=_Toc457298973></A><B>CcmdTarget类</B>
<P></P></LI></OL></OL>
<P
align=justify>除了CObject类外,还有一个非常重要的类CCmdTarget。所有响应消息或事件的类都从它派生。例如,CWinapp,CWnd,CDocument,CView,CDocTemplate,CFrameWnd,等等。</P>
<P
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -