MFC的消息机制

  众所周知,windows是基于消息驱动的,作好消息处理是WINDOWS编程的关键任务之一,用VC制作WINDOWS程式同样离不开消息的处理。这就要求我们对 VC中消息的处理有一个比较清淅的认识。只有这样才可能在必要的时候亲自动手完成一些复杂的消息映射处理。
  在MFC中消息是通过一种的消息映射机制来处理的。其实质是一张消息及其处理函数的一一对应表以及分析处理这张表的应用框架内部的一些程序代码.这样的好处是可以避免像早期的SDK编程一样需要罗列一大堆的CASE语句来处理各种消息。由于不同种类的消息其处理方法是不同的,所以我们有必要先弄清楚 WINDOWS消息的种类。

  WINDOWS 消息的种类:
  1、标准WINDOWS消息:这类消息是以WM_为前缀,不过WM_COMMAND例外。 例如: WM_MOVE、WM_QUIT等。
  2、命令消息:命令消息以WM_COMMAND为消息名。在消息中含有命令的标志符ID,以区分具体的命令。由菜单,工具栏等命令接口对象产生。
  3、控件通知消息:控件通知消息也是以WM_COMMAND为消息名。由编辑框、列表框和子窗口发送给父窗口的通知消息。在消息中包含控件通知码,以区分具体控件的通知消息。
  其中从CWnd派生的类可以接受上面的三种消息,从CCmdTarget派生的类能够接收命令消息。在使用MFCAppWizard创建的应用程序中,消息传递的机制是:

      App 类与Doc类都是从CCmdTarget类派生而来,所以只能接收命令消息(如单击菜单产生的消息),View类与Frame框架类都是从CWnd类派生而来,所以能够接收上面的三种消息。对于以上三种消息,期响应的顺序是这样的:首先由框架类接收到该消息,框架类将该消息递交给其子窗口View,如果View没有对该消息进行响应,则由View递交给Doc,如果Doc也没有对该消息进行响应,那么它回再次将该消息回交给View,View再回交给Frame框架,框架检查自己是否对该消息有相应的处理函数,如果没有则递交给App,如果App也没有则由Windows系统自己处理。

      命令消息:
  在以上三种消息中,标准的WINDOWS消息映射是相当简单的。可直接通过类向导完成不同消息的映射处理,所以不在本文讨论之列。
  凡是从CcmdTarget类派生的类都可以有消息映射,消息映射包括如下两方面的内容:
  1、在类的定义文件中(.H)中加上一条宏调用(通常这条语句中类定义的最后):

DECLARE_MESSAGE_MAP()

  2、在类的实现文件(.CPP)中加上消息映射表:

1BEGIN_MESSAGE_MAP(类名,父类名)
2………..
3消息映射入口项.
4………. 
5END_MESSAGE_MAP( )
6

  幸运的是除了某些类(如没有基类的类或直接从CobjectO类派生的类)外,其它许多类均可由类向导生成.尽管生成的类只是一个框架,需要我们补充内容。但消息映射表已经为我们加好了,只是入口项有待我们加入。命令消息映射入口项是一个ON_COMMAND的宏。比如文件菜单下的"打开…"菜单(ID值为ID_FILE_OPEN)对应的消息映射入口项为:
  ON_COMMAND(ID_FILE_NEW,OnFileOpen)
  3、加入消息映射入口项之后需要完成消息处理函数。在类中消息处理函数都是类的成员函数,要响应一个消息,就必须定义一个该消息的处理函数。定义一个消息处理函数包括以下三方面的内容:
      3.1 在类定义中加入消息处理函数的函数原型(函数声明)
      3.2 在类的消息映射表中加入相应的消息映射入口项
      3.3 在类的实现中加入消息处理函数的函数体
  需要说明的是消息处理函数的原型一定要以afx_msg打头,作为约定,消息处理函数名一般以On打头。比如:
      afx_msg OnFileOpen();// 函数原型
  
      控件通知消息:

      控件通知消息相对而言就复杂一点了,限于篇幅不能一一涉及,这里我们仅讨论 WM_NOTIFY消息的处理。
  WM_NOTFY产生的原因如下:
  在WINDOWS3.X中控件通知它们父窗口,如鼠标点击,控件背景绘制事件,通过发送一个消息到父窗口。简单的通知仅发送一个WM_COMMAND消息,包含一个通知码(比如BN_CLICKED)和一个在wParam中的控件ID及一个在lPraram中的控件句柄。因为wParam 和lParam均被使用,就没有方法传送其它的附加信息了。比如在BN_CLICKED 通知消息中,就没有办法发送关于当鼠标点击时光标的位置信息,在这种情况下就只能使用一些特殊的消息,包括:WM_CTLCOLOR、WM_VSCROLL和 WM_HSCROLL等等。值得一提的是这些消息能被反射回发送它们的控件,就是所谓的消息反射。
  在WIN32中同样可以使用那些在WINDOWS3.1中使用的通知消息,不过不像过去通过增加特殊目的的消息来为新的通知发送附加的数据,而是使用一个叫 WM_NOTIFY的消息,它能以一个标准的风格传送大量的附加数据。
      WM_NOTIFY消息包含一个存在wParam中的发送消息控件的ID和一个存在lParam中的指向一个结构体的指针。这个结构可能是NMHDR结构体,也可能是第一个成员是NMHDR的更大的结构,因为NMHDR是第一个成员,所以指向这个结构的指针也可以指向NMHDR。在许多情况下,这个指针将指向一个更大的结构,当你使用时必需转换它,只有很少的通知消息,比如通用通知消息(它的名字以NM_打头)。工具提示控件的 TTN_SHOW和TTN_POP实际上在使用NMHDR结构.
  NMHDR结构包含了发送消息控件的句柄,ID及通知码(如TTN_SHOW),其格式如下:
1Typedef sturct tagNMHDR{
2    HWND hwndFrom;
3    UINT idFrom;
4    UINT code;
5   }
 NMHDR;
6
  对TTN_SHOW消息而言,code成员的值将设为TTN_SHOW。
  类向导可以创建ON_NOTIFY消息映射入口并为你提供一个处理函数的框架,来处理 WM_NOTIFY类型的消息。ON_NOTIFY消息映射宏有如下语法:   
ON_NOTIFY(wNotifyCode,id,memberFxn)
  wNotifyCode:要处理的通知消息通知码,比如:LVN_KEYDOWN;Id:控件标识ID;MemberFxn:处理此消息的成员函数。此成员函数必需有如下的原形申明:
afx_msg void memberFxn( NMHDR * pNotifyStruct, LRESULT * result); 
  比如:假设你想成员函数OnKeydownList1处理ClistCtrl(标识ID=IDC_LIST1)的 LVN_KEYDOWN消息,你可以使用类向导添加如下的消息映射:   
ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )
  在上面的例子中,类向导提供如下函数:
Code
  这时类向导提供了一个适当类型的指针.你既可以通过pNMHDR,也可以通过 pLVKeyDow来访问这个通知结构。
  如前所述,有时我们可能需要为一组控件处理相同的WM_NOTIFY消息,这时需要使用ON_NOTIFY_RANGE而不是ON_NOTIFY。当你使用 ON_NOTIFY_RANGE时,你需要指定控件的ID范围.其消息映射入口及函数原型如下:
ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )
      wNotifyCode:消息通知码,比如:LVN_KEYDOWN;id: 第一控件的标识ID;idLast:最后一个控件的标识ID(标识值一定要连续);memberFxn: 消息处理函数。成员函数必须有如下原型申明:
afx_msg void memberFxn( UINT id, NMHDR * pNotifyStruct, LRESULT * result );

  其中id表示发送通知消息的控件标识ID。

转到博客首页查看更多随笔

posted on 2009-10-10 18:32  lantionzy  阅读(1708)  评论(0编辑  收藏  举报