« Shader快速复习:Reflection And Refraction(反射与折射)图像处理里的空间域滤波 »

WIN32下的窗口子类化

什么是子类化?又是跟继承有关吗?有时候是我们太想当然了。父类与子类不是那么一个只存在于一种特定语言里的东西。从辨证学角度来瞎扯,父子关系是一般性与特殊性的有机结合口牙~........(瞎扯!)——ZwqXin.com

事情是这样的,有一天,ZwqXin正在弄着些什么代码,然后弄着弄着,突然双拳紧握,一击打在桌子上,碎碎念:怎么办怎么办!原来......原来.......是编程遇到麻烦了啊:我弄了个对话框,然后在对话框里放了个Static控件,是用来在它上面画画的。真的算画画吧,简化一下——就是在控件上有条直线,我通过鼠标在控件框框上左键单击+移动鼠标,来改变该直线的斜率属性。——本来以为是简单的事,蓦然发现无法弄成功。归因,就是在消息处理的时候出问题了。

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/MFC/sub-winproc-win32.html

对话框自然得有一个消息处理函数DLGPROC来处理针对该对话框发生的事情吧,该STATIC控件隶属于本对话框,也容易得到其句柄和矩形位置大小属性。那么,我在DLGPROC里处理我所需要的WM_LBUTTONDOWN, WM_LBUTTONUP和WM_MOUSEMOVE 消息,判断鼠标位置是否在STATIC控件范围内(矩形框内),若是,就进行相应的直线斜率改变&重画处理等处理,可不行吗?

不行,当鼠标在STATIC控件内无论干什么,都没有上述消息传入,完全捕捉不到,而移到STATIC控件范围外才有,但这不是我需要的啊。为什么呢?GOOGLE一下,都说STATIC控件无法响应单击等简单消息外的其他鼠标消息——鼠标在它里面完全是死寂的。至于单击,也是在WM_COMMAND里接收:

  1. LRESULT CALLBACK DLGPROC(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
  2. {
  3.  switch(msg)
  4. {
  5.   ........
  6.      case WM_COMMAND
  7.      {
  8.         if(HIWORD(wparam) == STN_CLICKED && LOWORD(wparam) == IDC_GSTATIC)
  9.            { 
  10.            onMouseDown = true;
  11.            GetCursorPos(&OriMousePt);
  12.            ScreenToClient(StaticWND,&OriMousePt);
  13.             ...........
  14.             }
  15.   .........
  16.      }
  17. }
  18.  
  19. return false;
  20. }

STN_CLICKED是少数几个STATIC文本控件能默认接受的消息(事实上就STN_CLICKED, STN_DBLCLK, STN_ENABLE, STN_DISABLE等那么几个,后两个也没什么用..),同时还得通过资源号识别是哪个STATIC控件发出的(这里我是IDC_GSTATIC),通过wparam的高低位进来。

怎么办,MOUSEMOVE等消息难道“消失”了?不可能的,WINDOWS的消息处理机制不会故意去放漏这些消息。这只是微软自以为是的一道坎。继续GOOGLE,都是什么继承CSTATIC之类的说法。哎呀,我不想就这样转去MFC啊~~于是在CSDN发帖求救。马上有回帖了,第一条是“子类化就可以了”,第二条是“惟有子类化了”........子类化!

难道是继承吗?搞这么复杂!再向GOOGLE求救。看到这篇文章解说Win32的窗口子类化,发现自己想错了。

所谓WIN32窗口的子类化,是说窗口内子窗口的消息被委托给一个“子窗口消息处理函数”来完成。对啊,父类子类不一定就是说C++里那个“类”呀!对话框是窗口,它里面的控件也是窗口——子窗口[WINDOWS游戏编程大师技巧笔记之——WIN32编程] 。但是,它的“父亲”DLGPROC都完成不了的事情,它(SUBPROC)能做到么?确实,MFC能做到的东西,WIN32没可能望天的(毕竟MFC只是....)。

原理上,父窗口在初始化(生成)的时候,就可以指定子窗口的消息处理函数。然后该子窗口就能捕获发生在父窗口的子窗口部分(这里就是那个STATIC框)处的事件,并首先处理。都说了,消息不会漏掉的嘛,只是STATIC的接收器默认被限制了。譬如说,我若还需要WM_PAINT消息的话,如果我在父窗口的消息处理函数和子窗口的消息处理函数里都放一个的话(事实上我后来也这么做了),该控件内的重画消息按正常地流入父窗口,但首先被子窗口的处理函数捕获处理,处理完后再回到父窗口消息处理渠被父窗口的消息处理函数处理。一切是这么实现的:

  1. WNDPROC ParentGPROC = NULL;
  2. LRESULT CALLBACK SUBGPROC(HWND subhwnd, UINT msg, WPARAM wparam, LPARAM lparam);
  3.  
  4. LRESULT CALLBACK DLGPROC(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) 
  5.  switch(msg) 
  6.         case WM_INITDIALOG: 
  7.          .........
  8.       ParentGPROC = (WNDPROC)SetWindowLong(StaticWND, GWL_WNDPROC, (LONG)SUBGPROC);
  9.       // 感谢评论中fdafa指出这个笔误:SubGPROC = (WNDPROC)SetWindowLong(StaticWND, GWL_WNDPROC, (LONG)SUBGPROC);
  10.         .........
  11.        break;
  12.  
  13.   ........ 
  14.      case WM_COMMAND 
  15.      { 
  16.         if(HIWORD(wparam) == STN_CLICKED && LOWORD(wparam) == IDC_GSTATIC) 
  17.            {  
  18.            onMouseDown = true; 
  19.            GetCursorPos(&OriMousePt); 
  20.            ScreenToClient(StaticWND,&OriMousePt); 
  21.             ........... 
  22.             } 
  23.   ......... 
  24.      } 
  25.    break;
  26.  
  27. return false; 
  28. }
  29.  
  30. LRESULT CALLBACK SUBGPROC(HWND subhwnd, UINT msg, WPARAM wparam, LPARAM lparam)
  31. {
  32.     switch(msg)
  33.     {
  34.     case WM_PAINT:
  35.             SThdc = BeginPaint(StaticWND, &ps);
  36.                 
  37.     ..............
  38.  
  39.             EndPaint(StaticWND, &ps);
  40.             return 0;
  41.     break;
  42.     
  43.  
  44.     case WM_LBUTTONDOWN:
  45.         onMouseDown = true; 
  46.         GetCursorPos(&OriMousePt);
  47.         ScreenToClient(subhwnd,&OriMousePt);
  48.         .........
  49.     break;
  50.     case WM_LBUTTONUP:
  51.         onMouseDown = false;
  52.     break;
  53.     case WM_MOUSEMOVE:
  54.         if(onMouseDown)
  55.         {
  56.             POINT MosuePos;
  57.             GetCursorPos(&MosuePos);
  58.             ScreenToClient(subhwnd,&MosuePos);
  59.             ..............
  60.                  InvalidateRect(StaticWND,NULL,TRUE);
  61.         }
  62.     break;
  63.     default:
  64.         return CallWindowProc(ParentGPROC ,subhwnd, msg, wparam, lparam);
  65.     }
  66.     
  67.  
  68.     return 0;
  69. }

SetWindowLong接收一个GWL_WNDPROC的标志,表明是用参数内的SUBGPROC函数指针代替当前父窗口的消息处理,并返回父窗口消息处理函数的指针ParentGPROC。

子窗口消息处理函数最后的CallWindowProc(ParentGPROC,subhwnd, msg, wparam, lparam); 就是为了把处理权交还给父窗口处理函数。我不知道在初始化父窗口时建立的这种关系为什么能持续下去,但事实就是这样,也可以看成只是为了方便消息渠里的消息找消息处理函数指针。

上面那篇文章最后还说了些注意事项,也就是资源ID和SS_NOTIFY样式之类那些,也要好好注意喔。

总之,WIN32“子类化”成功,找你找得好辛苦哦!WM_MOUSEMOVE!

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/MFC/sub-winproc-win32.html

  • quote 1.fdafa
  • SubGPROC = (WNDPROC)SetWindowLong(StaticWND, GWL_WNDPROC, (LONG)SUBGPROC);
    SubGPROC不是笔误ParentGPROC么?这么关键的地方,程序调试过?我就不信了。
    zwqxin 于 2012-2-8 22:02:49 回复
    确实笔误,是ParentGPROC = (WNDPROC)SetWindowLong(StaticWND, GWL_WNDPROC, (LONG)SUBGPROC);这个ParentGPROC就是之后用于SUBGPROC里调用的CallWindowProc函数。谢谢提醒。
    另外网站内容也不知道为什么在chrome下复制有点问题。可能是Z-BLOG系统的兼容问题。
  • 2012-2-8 11:11:02 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.