MFC消息映射
帐 号: 注册帐号
密 码:  找回密码
      记住用户名和密码
MFC消息映射

摘要:控件通知消息有很多种,但是有一种是很常用,但是又不是很容易掌握的,那就是WM_NOTIFY,我试着对此做一下比较全面的论述,有不对的地方,还希望各路大虾批评指正。

    控件通知消息
   
在《深度解析VC中的消息(上)》中,我们提到了消息的分类有3种:窗口消息、命令消息和控件通知消息,我们这里要谈的是最后一种:控件通知消息。
   
控件通知消息,是指这样一种消息,一个窗口内的子控件发生了一些事情,需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等。例如,单击或双击一个控件、在控件中选择部分文本、操作控件的滚动条都会产生通知消息。 她类似于命令消息,当用户与控件窗口交互时,那么控件通知消息就会从控件窗口发送到它的主窗口。但是这种消息的存在并不是为了处理用户命令,而是为了让主窗口能够改变控件,例如加载、显示数据。例如按下一个按钮,他向父窗口发送的消息也可以看作是一个控件通知消息;单击鼠标所产生的消息可以由主窗口直接处理,然后交给控件窗口处理。
   
控件通知消息主要由窗口类即直接或间接由CWND类派生类处理。

    控件通知格式
   
控件通知经历了一个演变过程,因而SendMessage( )的变量MessagewParamlParam有三种格式。
   
第一控件通知格式
   
第一控件通知格式只是窗口消息的子集。它的特征格式如下:WM_XXXX。它主要来自下面的3种消息类型:
    (1)
表示一个控件窗口要么已经被创建或销毁,要么已经被鼠标单击的消息:WM_PARENTNOTIFY
    (2)
发送到父窗口,用来绘制自身窗口的消息,例如: WM_CTLCOLORWM_DRAWITEMWM_MEASUREITEMWM_DELETEITEMWM_CHARTOITEMWM_VKTOITEMWM_COMMANDWM_COMPAREITEM
    (3)
有滚动调控件发送,通知父窗口滚动窗口的消息:WM_VSCROLLWM_HSCROLL
   
第二控件通知格式
   
第二控件通知格式与命令消息共享,它的特征格式如下:WM_COMMAND
   
WM_COMMAND中,lParam用来区分是命令消息还是控件通知消息:如果lParamNULL,则这是个命令消息,否则lParam里面放的必然就是控件的句柄,是一个控件通知消息。对于wParam则是低位放的是控件ID,高位放的是相应的消息事件。
   
第三控件通知格式
   
这个才真正涉及到我们要讲的内容,同时他也是最为灵活的一种格式。它的特征格式如下:WM_NOTIFY
   
WM_NOTIFY中,lParam中放的是一个称为NMHDR结构的指针。在wParam中放的则是控件的ID

    NMHDR结构的由来
    NMHDR
结构是很值得一提的,该结构包括有关制作该通知的控件的任何内容,而不受空间和类型的限制,他的来历也是很有意思的。
   
在最初的windows3.x中,根本就不存在什么WM_NOTIFY,控件通知它们父窗口,如鼠标点击,控件背景绘制事件,通过发送一个消息到父窗口。简单的通知仅发送一个WM_COMMAND消息,包含一个通知码和一个在wParam中的控件ID及一个在lPraram中的控件句柄。这样一来,wParamlParam就都被填充了,没有额外的空间来传递一些其它的消息,例如鼠标按下的位置和时间。
   
为了克服这个困难,windows3.x就提出了一个比较低级的解决策略,那就是给一些消息添加一些附加消息,最为明显的就是控件自画用到的DRAWITEMSTRUCT。不知道大家对这个结构熟悉不,不过,如果你是老手,你应该非常清楚这个结构,这个结构包含了9个内容,几乎你需要控制的信息都给你提供了。为什么说它比较低级呢?因为不同的消息附加的内容不同,结果就是一盘散沙,非常混乱。
   
win32中,MS又提出了一个更好的解决方案:引进NMHDR结构。这个结构的引进就是消息统一起来,利用它可以传递复杂的信息。这个结构的布局如下:
    NMHDR
    {
        HWnd hWndFrom ;
相当于原WM_COMMAND传递方式的lParam
        UINT idFrom ;  
相当于原WM_COMMAND传递方式的wParamlow-order
        UINT code ;    
相当于原WM_COMMAND传递方式的Notify Code(wParam"s high-order)
    }

   
对于这个结构的应用于WM_NOTIFY信息结构,结果WM_NOTIFY就变成了:
    A
、无附加信息。结构变得很简单,就是一个NMHDR结构。
    B
、有附加信息。定义一个大的结构,它的第一个元素就是NMHDR结构,它的后面放置附加信息。
   
   
WM_NOTIFY结构的好处
   
除了上面我们所说的好处外,WN_NOTIFY还有自己的独特的好处:
   
由于在大结构中,第一个成员为NMHDR,这样一来,我们就可以利用指向NMHDR的指针来传递结构地址,根据指针的特性,无论消息有没有附加信息,这个指针都适用,也能够很方便的进行强制转换。

    分析ON_NOTIFY
   类向导可以创建ON_NOTIFY消息映射入口并提供一个处理函数的框架,来处理 WM_NOTIFY类型的消息。ON_NOTIFY消息映射宏有如下语法.
   ON_NOTIFY(wNotifyCode,id,memberFxn)
  其中:wNotifyCode:要处理的通知消息通知码。比如上面我们提到的LVN_KEYDOWNId:控件标识IDMemberFxn:处理此消息的成员函数。
  此成员函数有如下的原型声明:
   afx_msg void memberFxn( NMHDR * pNotifyStruct, LRESULT * result);
  比如:假设你想成员函数OnKeydownList1处理ClistCtrl(标识ID=IDC_LIST1)的 LVN_KEYDOWN消息,你可以使用类向导添加如下的消息映射:
   ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )
  在上面的例子中,类向导提供如下函数:
   void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
    {
     LV_KEYDOWN* pLVKey= (LV_KEYDOWN*)pNMHDR;
     *pResult = 0;
     }
  这时类向导提供了一个适当类型的指针,你既可以通过pNMHDR,也可以通过 pLVKey来访问这个通知结构。

    ON_NOTIFY_RANGE
  有时我们可能需要为一组控件处理相同的WM_NOTIFY消息。这时需要使用ON_NOTIFY_RANGE而不是ON_NOTIFY。不过,很不幸的是,VC6ClassWizard并不支持这个消息,所以我们必须手工添加。方法和一般的手工添加的消息一样,不过需要注意的是:
    (1)
当你使用 ON_NOTIFY_RANGE,你需要指定控件的ID范围.其消息映射入口及函数原型如下:
     ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )
    其中:wNotifyCode:消息通知码.比如:LVN_KEYDOWNid: 第一控件的标识ID
     idLast:最后一个控件的标识ID。(标识值一定要连续)memberFxn: 消息处理函数。
  (2)成员函数必须有如下原型申明:afx_msg void memberFxn( UINT id, NMHDR * pNotifyStruct, LRESULT * result );

    结束语:
    WM_NOTIFY
是一个很值得思考的结构,另外他和Reflect Message联系非常紧密,我们接下来就要讨论一下反射消息。

 摘要:Windows编程和Dos编程,一个很大的区别就是,windows编程是事件驱动,消息传递的。所以,要做好windows编程,必须对消息机制有一个清楚的认识,本文希望能够对消息的传递做一个全面的论述,由于小生初学VC,里面可能有一些错误的地方,还往各位大虾批评、指正。

     消息是指什么?
    
消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。
   
消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做MSGMSG含有来自windows应用程序消息队列的消息信息,它在Windows中声明如下:
    typedef struct tagMsg
    {
       HWND    hwnd;      
接受该消息的窗口句柄
       UINT    message;   
消息常量标识符,也就是我们通常所说的消息号
       WPARAM  wParam;     32
位消息的特定附加信息,确切含义依赖于消息值
       LPARAM  lParam;     32
位消息的特定附加信息,确切含义依赖于消息值
       DWORD   time;      
消息创建时的时间
       POINT   pt;        
消息创建时的鼠标/光标在屏幕坐标系中的位置
    }MSG;
   
消息可以由系统或者应用程序产生。系统在发生输入事件时产生消息。举个例子, 当用户敲键, 移动鼠标或者单击控件。系统也产生消息以响应由应用程序带来的变化, 比如应用程序改变系统字体改变窗体大小。应用程序可以产生消息使窗体执行任务,或者与其他应用程序中的窗口通讯。

     消息中有什么?
    
我们给出了上面的注释,是不是会对消息结构有了一个比较清楚的认识?如果还没有,那么我们再试着给出下面的解释:
     hwnd 32
位的窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)
     message
用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。消息标识符以常量命名的方式指出消息的含义。当窗口过程接收到消息之后,他就会使用消息标识符来决定如何处理消息。例如、WM_PAINT告诉窗口过程窗体客户区被改变了需要重绘。符号常量指定系统消息属于的类别,其前缀指明了处理解释消息的窗体的类型。
     wParam
通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。
     lParam
通常是一个指向内存中数据的指针。由于WParamlParamPointer都是32位的,因此,它们之间可以相互转换。

     消息标识符的值
    
系统保留消息标识符的值在0x00000x03ff(WM_USER-1)范围。这些值被系统定义消息使用。 应用程序不能使用这些值给自己的消息。应用程序消息从WM_USER0X0400)到0X7FFF,或0XC0000XFFFFWM_USER0X7FFF范围的消息由应用程序自己使用;0XC0000XFFFF范围的消息用来和其他应用程序通信,我们顺便说一下具有标志性的消息值:
     WM_NULL---0x0000   
空消息。
     0x0001----0x0087   
主要是窗口消息。
     0x00A0----0x00A9   
非客户区消息
     0x0100----0x0108   
键盘消息
     0x0111----0x0126   
菜单消息
     0x0132----0x0138   
颜色控制消息
     0x0200----0x020A   
鼠标消息
     0x0211----0x0213   
菜单循环消息
     0x0220----0x0230   
多文档消息
     0x03E0----0x03E8    DDE
消息
     0x0400              WM_USER
     0x8000              WM_APP
     0x0400----0x7FFF   
应用程序自定义私有消息

     消息有哪几种?
    
其实,windows中的消息虽然很多,但是种类并不繁杂,大体上有3种:窗口消息、命令消息和控件通知消息。
    
窗口消息大概是系统中最为常见的消息,它是指由操作系统和控制其他窗口的窗口所使用的消息。例如CreateWindowDestroyWindowMoveWindow等都会激发窗口消息,还有我们在上面谈到的单击鼠标所产生的消息也是一种窗口消息。
    
命令消息,这是一种特殊的窗口消息,他用来处理从一个窗口发送到另一个窗口的用户请求,例如按下一个按钮,他就会向主窗口发送一个命令消息。
    
控件通知消息,是指这样一种消息,一个窗口内的子控件发生了一些事情,需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等。例如,单击或双击一个控件、在控件中选择部分文本、操作控件的滚动条都会产生通知消息。 她类似于命令消息,当用户与控件窗口交互时,那么控件通知消息就会从控件窗口发送到它的主窗口。但是这种消息的存在并不是为了处理用户命令,而是为了让主窗口能够改变控件,例如加载、显示数据。例如按下一个按钮,他向父窗口发送的消息也可以看作是一个控件通知消息;单击鼠标所产生的消息可以由主窗口直接处理,然后交给控件窗口处理。
   
其中窗口消息及控件通知消息主要由窗口类即直接或间接由CWND类派生类处理。相对窗口消息及控件通知消息而言,命令消息的处理对象范围就广得多,它不仅可以由窗口类处理,还可以由文档类,文档模板类及应用类所处理。
   
由于控件通知消息很重要的,人们用的也比较多,但是具体的含义往往令初学者晕头转向,所以我决定把常见的几个列出来供大家参考:
   
按扭控件
     BN_CLICKED       
用户单击了按钮
 BN_DISABLE 
按钮被禁止
 BN_DOUBLECLICKED 
用户双击了按钮
 BN_HILITE  
/户加亮了按钮
 BN_PAINT  
按钮应当重画
 BN_UNHILITE 
加亮应当去掉
   
组合框控件
 CBN_CLOSEUP 
组合框的列表框被关闭
 CBN_DBLCLK 
用户双击了一个字符串
 CBN_DROPDOWN 
组合框的列表框被拉出
 CBN_EDITCHANGE 
用户修改了编辑框中的文本
 CBN_EDITUPDATE 
编辑框内的文本即将更新
 CBN_ERRSPACE 
组合框内存不足
 CBN_KILLFOCUS 
组合框失去输入焦点
 CBN_SELCHANGE 
在组合框中选择了一项
 CBN_SELENDCANCEL 
用户的选择应当被取消
 CBN_SELENDOK 
用户的选择是合法的
 CBN_SETFOCUS 
组合框获得输入焦点
   
编辑框控件
 EN_CHANGE 
编辑框中的文本己更新
 EN_ERRSPACE 
编辑框内存不足
 EN_HSCROLL 
用户点击了水平滚动条
 EN_KILLFOCUS 
编辑框正在失去输入焦点
 EN_MAXTEXT 
插入的内容被截断
 EN_SETFOCUS 
编辑框获得输入焦点
 EN_UPDATE 
编辑框中的文本将要更新
 EN_VSCROLL 
用户点击了垂直滚动条消息含义
   
列表框控件
 LBN_DBLCLK 
用户双击了一项
 LBN_ERRSPACE 
列表框内存不够
 LBN_KILLFOCUS 
列表框正在失去输入焦点
 LBN_SELCANCEL 
选择被取消
 LBN_SELCHANGE 
选择了另一项
 LBN_SETFOCUS 
列表框获得输入焦点

未完(待续...)

 

深度解析VC中的消息()

     队列消息和非队列消息
   
从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数数系统给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。
    
对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINTWM_TIMERWM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。
    
一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。
   
非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。 例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,例如下面我们要谈到的函数。
    
    
消息的发送
    
了解了上面的这些基础理论之后,我们就可以进行一下简单的消息发送与接收。
    
把一个消息发送到窗口有3种方式:发送、寄送和广播。
    
发送消息的函数有SendMessageSendMessageCallbackSendNotifyMessageSendMessageTimeout;寄送消息的函数主要有PostMessagePostThreadMessagePostQuitMessage;广播消息的函数我知道的只有BroadcastSystemMessageBroadcastSystemMessageEx
     SendMessage
的原型如下:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),这个函数主要是向一个或多个窗口发送一条消息,一直等到消息被处理之后才会返回。不过需要注意的是,如果接收消息的窗口是同一个应用程序的一部分,那么这个窗口的窗口函数就被作为一个子程序马上被调用;如果接收消息的窗口是被另外的线程所创建的,那么窗口系统就切换到相应的线程并且调用相应的窗口函数,这条消息不会被放进目标应用程序队列中。函数的返回值是由接收消息的窗口的窗口函数返回,返回的值取决于被发送的消息。
     PostMessage
的原型如下:BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),该函数把一条消息放置到创建hWnd窗口的线程的消息队列中,该函数不等消息被处理就马上将控制返回。需要注意的是,如果hWnd参数为HWND_BROADCAST,那么,消息将被寄送给系统中的所有的重叠窗口和弹出窗口,但是子窗口不会收到该消息;如果hWnd参数为NULL,则该函数类似于将dwThreadID参数设置成当前线程的标志来调用PostThreadMEssage函数。
   从上面的这2个具有代表性的函数,我们可以看出消息的发送方式和寄送方式的区别所在:被发送的消息是否会被立即处理,函数是否立即返回。被发送的消息会被立即处理,处理完毕后函数才会返回;被寄送的消息不会被立即处理,他被放到一个先进先出的队列中,一直等到应用程序空线的时候才会被处理,不过函数放置消息后立即返回。
   实际上,发送消息到一个窗口处理过程和直接调用窗口处理过程之间并没有太大的区别,他们直接的唯一区别就在于你可以要求操作系统截获所有被发送的消息,但是不能够截获对窗口处理过程的直接调用。
   以寄送方式发送的消息通常是与用户输入事件相对应的,因为这些事件不是十分紧迫,可以进行缓慢的缓冲处理,例如鼠标、键盘消息会被寄送,而按钮等消息则会被发送。
   广播消息用得比较少,BroadcastSystemMessage函数原型如下:
      long BroadcastSystemMessage(DWORD dwFlags,LPDWORD lpdwRecipients,UINT uiMessage,WPARAM wParam,LPARAM lParam);
该函数可以向指定的接收者发送一条消息,这些接收者可以是应用程序、可安装的驱动程序、网络驱动程序、系统级别的设备驱动消息和他们的任意组合。需要注意的是,如果dwFlags参数是BSF_QUERY并且至少一个接收者返回了BROADCAST_QUERY_DENY,则返回值为0,如果没有指定BSF_QUERY,则函数将消息发送给所有接收者,并且忽略其返回值。

   消息的接收
  消息的接收主要有3个函数:GetMessagePeekMessageWaitMessage
   GetMessage原型如下:BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax);该函数用来获取与hWnd参数所指定的窗口相关的且wMsgFilterMinwMsgFilterMax参数所给出的消息值范围内的消息。需要注意的是,如果hWndNULL,则GetMessage获取属于调用该函数应用程序的任一窗口的消息,如果wMsgFilterMinwMsgFilterMax都是0,则GetMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT则只有在其处理之后才被删除。
   PeekMessage原型如下:BOOL PeekMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);该函数用于查看应用程序的消息队列,如果其中有消息就将其放入lpMsg所指的结构中,不过,与GetMessage不同的是,PeekMessage函数不会等到有消息放入队列时才返回。同样,如果hWndNULL,则PeekMessage获取属于调用该函数应用程序的任一窗口的消息,如果hWnd=-1,那么函数只返回把hWnd参数为NULLPostAppMessage函数送去的消息。如果wMsgFilterMinwMsgFilterMax都是0,则PeekMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT则只有在其处理之后才被删除。
   WaitMessage原型如下:BOOL VaitMessage();当一个应用程序无事可做时,该函数就将控制权交给另外的应用程序,同时将该应用程序挂起,直到一个新的消息被放入应用程序的队列之中才返回。

      消息的处理
   接下来我们谈一下消息的处理,首先我们来看一下VC中的消息泵:
   while(GetMessage(&msg, NULL, 0, 0))
      {
 if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))
         {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
         }
      }

      首先,GetMessage从进程的主线程的消息队列中获取一个消息并将它复制到MSG结构,如果队列中没有消息,则GetMessage函数将等待一个消息的到来以后才返回。 如果你将一个窗口句柄作为第二个参数传入GetMessage,那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。这时候就要利用GetMessagePeekMessage指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄,或者两者同时指定。当应用程序要查找一个后入消息队列的消息是很有用。WM_KEYFIRST WM_KEYLAST 常量用于接受所有的键盘消息。 WM_MOUSEFIRST WM_MOUSELAST 常量用于接受所有的鼠标消息。
   然后TranslateAccelerator判断该消息是不是一个按键消息并且是一个加速键消息,如果是,则该函数将把几个按键消息转换成一个加速键消息传递给窗口的回调函数。处理了加速键之后,函数TranslateMessage将把两个按键消息WM_KEYDOWNWM_KEYUP转换成一个WM_CHAR,不过需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然将传递给窗口的回调函数。    
   处理完之后,DispatchMessage函数将把此消息发送给该消息指定的窗口中已设定的回调函数。如果消息是WM_QUIT,则GetMessage返回0,从而退出循环体。应用程序可以使用PostQuitMessage来结束自己的消息循环。通常在主窗口的WM_DESTROY消息中调用。
   下面我们举一个常见的小例子来说明这个消息泵的运用:
   if (::PeekMessage(&msg, m_hWnd, WM_KEYFIRST,WM_KEYLAST, PM_REMOVE))
      {
          if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
...
      }
 
  这里我们接受所有的键盘消息,所以就用WM_KEYFIRST WM_KEYLAST作为参数。最后一个参数可以是PM_NOREMOVE 或者 PM_REMOVE,表示消息信息是否应该从消息队列中删除。                
   
 所以这段小代码就是判断是否按下了Esc键,如果是就进行处理。

   窗口过程
   窗口过程是一个用于处理所有发送到这个窗口的消息的函数。任何一个窗口类都有一个窗口过程。同一个类的窗口使用同样的窗口过程来响应消息。 系统发送消息给窗口过程将消息数据作为参数传递给他,消息到来之后,按照消息类型排序进行处理,其中的参数则用来区分不同的消息,窗口过程使用参数产生合适行为。
   一个窗口过程不经常忽略消息,如果他不处理,它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。窗口过程必须return一个值作为它的消息处理结果。大多数窗口只处理小部分消息和将其他的通过DefWindowProc传递给系统做默认的处理。窗口过程被所有属于同一个类的窗口共享,能为不同的窗口处理消息。下面我们来看一下具体的实例:
   LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
   {
 int wmId, wmEvent;
 PAINTSTRUCT ps;
 HDC hdc;
 TCHAR szHello[MAX_LOADSTRING];
 LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

 switch (message)
 {
  case WM_COMMAND:
   wmId    = LOWORD(wParam);
   wmEvent = HIWORD(wParam);
   // Parse the menu selections:
   switch (wmId)
   {
    case IDM_ABOUT:
       DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
       break;
    case IDM_EXIT:
       DestroyWindow(hWnd);
       break;
    default:
       return DefWindowProc(hWnd, message, wParam, lParam);
   }
   break;
  case WM_PAINT:
   hdc = BeginPaint(hWnd, &ps);
   // TODO: Add any drawing code here...
   RECT rt;
   GetClientRect(hWnd, &rt);
   DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
   EndPaint(hWnd, &ps);
   break;
  case WM_DESTROY:
   PostQuitMessage(0);
   break;
  default:
   return DefWindowProc(hWnd, message, wParam, lParam);
 
    }
 
   return 0;
   }

   消息分流器
   通常的窗口过程是通过一个switch语句来实现的,这个事情很烦,有没有更简便的方法呢?有,那就是消息分流器,利用消息分流器,我们可以把switch语句分成更小的函数,每一个消息都对应一个小函数,这样做的好处就是对消息更容易管理。
   之所以被称为消息分流器,就是因为它可以对任何消息进行分流。下面我们做一个函数就很清楚了:
   void MsgCracker(HWND hWnd,int id,HWND hWndCtl,UINT codeNotify)
      {
          switch(id)
          {
     case ID_A:
                  if(codeNotify==EN_CHANGE)...
                  break;
             case ID_B:
                  if(codeNotify==BN_CLICKED)...
                  break;
             ....
           }
      }
     
然后我们修改一下窗口过程:
   LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
      {
 switch(message)
         {
             HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);
             HANDLE_MSG(hWnd,WM_DESTROY,MsgCracker);
     default:
  return DefWindowProc(hWnd, message, wParam, lParam);
 
    }
 
     return 0;
   }
     

copyright:2016-2020|邮箱:imalib@vip.163.com

蜀ICP备16020986号