`
mqttb32q
  • 浏览: 14064 次
社区版块
存档分类
最新评论

SubClassWindow,SubClassDlgItem

 
阅读更多

SubClassWindow,SubClassDlgItem
2011年06月01日
  
msdn上的解析
  CWnd::SubclassWindow
  BOOL SubclassWindow( HWND hWnd );
  Return Value
  Nonzero if the function is successful; otherwise 0.
  Parameters
  hWnd
  A handle to the window.
  Remarks
  Call this member function to "dynamically subclass" a window and attach it to this CWnd object. When a window is dynamically subclassed, windows messages will route through the CWnd’s message map and call message handlers in the CWnd’s class first. Messages that are passed to the base class will be passed to the default message handler in the window.
  Subclass(子类化)是MFC中最常用的窗体技术之一。子类化完成两个工作:一是把窗体类对象attach到一个windows窗体实体中(即把一个窗体的hwnd赋给该类)。另外就是把该类对象的消息加入到消息路由中,使得该类可以捕获消息。
  例如一个CEdit的派生类CMyEdit 只允许键入0-9, A-F, 则我们可以改写WM_CHAR消息响应函数,然后用SubclassWindow子类化到对话框的一个文本框实体上(可用GetDlgItem), 这样对话框上文本框的消息就会重定向到CMyEdit上。
  SubDlgItem 与 SubclassWindow 区别不大,但前者只限定于对话框控件,后者是一切具有HWND的窗体
  一:超类化概述
  
在MFC中窗体实例对某个窗体句柄超类化后,系统提供了这样两种能力:
  1.我们对该窗体实例调用成员函数将会直接改变相关窗体句柄对应的窗体
  2.系统传给相关窗体句柄的消息会先经过该窗体实例的消息映射
  我举一个例子来说明:
  比如我自己写了一个类叫CSuperEdit(父类为CEdit),在该类中我声明了void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);并在消息循环里添加了ON_WM_CHAR 一行
  现在我只要在对话框CProg1Dlg 中声明CSuperEdit m_edit;然后在CProg1Dlg::OnInitDialog中,添加以下代码,就完成了“超类化”:
  HWND hWndControl = ::GetDlgItem(pParent->m_hWnd, IDC_EDIT1);
  m_edit.SubclassWindow (hWndControl);
  这样超类化处理以后:
  当我们调用m_edit.SetWindowText("");,后IDC_EDIT1窗体上对应的文字就会改变为""
  当用户在IDC_EDIT1窗体中敲键盘时,系统会调用我自己写的CSuperEdit::OnChar函数(而不是原先的CEdit::OnChar)
  二:超类化实现的概述
  所有的秘密都在CWnd::SubclassWindow 中,让我们查看一下它到底做了些什么吧,以下是函数体(在WINCORE.CPP文件内):
  BOOL CWnd::SubclassWindow(HWND hWnd)
  {
  if (!Attach(hWnd))
  return FALSE;
  // allow any other subclassing to occur
  PreSubclassWindow ();
  // now hook into the AFX WndProc
  WNDPROC* lplpfn = GetSuperWndProcAddr();
  WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)AfxGetAfxWndProc());
  ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());
  return TRUE;
  }
  结合注释不难想到PreSubclassWindow 是非功能性的函数,所以我们只要研究两个函数就可以了解CWnd::SubclassWindow 的大概功能 CWnd::Attach和 ::AfxGetAfxWndProc
  两者中当中CWnd::Attach 对应于实现了功能1,即“我们对该窗体实例调用成员函数将会直接改变相关窗体句柄对应的窗体”
  ::AfxGetAfxWndProc函数对应于实现了功能2,即“系统传给相关窗体句柄的消息会先经过该窗体实例的消息映射”
  三:功能1的实现
  CWnd::Attach 的函数体如下(在WINCORE.CPP文件内):
  BOOL CWnd::Attach(HWND hWndNew)
  {
  if (hWndNew == NULL)
  return FALSE;
  CHandleMap* pMap = afxMapHWND(TRUE); // create map if not exist
  ASSERT(pMap != NULL);
  pMap->SetPermanent(m_hWnd = hWndNew, this);
  return TRUE;
  }
  最关键的是m_hWnd = hWndNew 一句(接触过windows的API的朋友都知道,windows系统所有窗体操作函数都是把窗体句柄作为一个调用参数),显然只要我把窗体的句柄保存下来,那我就可以在系统中唯一地指定一个窗体,然后对该窗体进行操作
  是的,思路就是这么简单。我们现在看到CWnd(别忘了CsuperEdit 是从CWnd继承的,这里的CWnd实际就是CsuperEdit )在Attach 函数中把IDC_EDIT1 的句柄保存在了成员变量m_hWnd 中,那么实现功能1,自然也就不在话下了
  至于CHandleMap::SetPermanent 函数则是用来延长句柄的使用期的,与“超类化”无关,不在此处讨论,其具体实现可参考WINHAND_.H文件
  四:功能2的实现
  四点一:窗体句柄的GWL_WNDPROC属性
  在前面的讨论中,我说过功能2是跟::AfxGetAfxWndProc 有关的,该函数的实现是这样的(也是在WINCORE.CPP文件中):
  WNDPROC AFXAPI AfxGetAfxWndProc()
  {
  #ifdef _AFXDLL
  return AfxGetModuleState()->m_pfnAfxWndProc;
  #else
  return &AfxWndProc;
  #endif
  }
  这是指在DLL中调用的话返回AfxGetModuleState()->m_pfnAfxWndProc;否则返回AfxWndProc 函数的地址。于是在一般的可执行文件中CWnd::SubclassWindow 为功能2所做的事可以简化为一行::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)&AfxWndProc);
  该函数的作用是把窗体句柄hWnd 的GWL_WNDPROC 属性设置为AfxWndProc 的地址,那么现在急需解决的问题是:窗体句柄的GWL_WNDPROC 属性是干什么用的?其实不用我说,大家都猜得到(因为我们是在讨论窗体的消息嘛,而且我也一直在说AfxWndProc是一个函数),它的作用是指定窗体消息的处理函数
  对于该属性更准确地描述如下:对于发给窗体的所有消息,Windows操作系统将会以该消息为参数调用窗体句柄的GWL_WNDPROC属性所指定的函数
  四点二:被传递到MFC环境中
  
(本节参考了侯捷老师《深入浅出MFC》中“消息映射与命令传递”一章的“两万五千里长征”)
  于是功能2可以表述为:AfxWndProc函数是如何找到我为CSuperEdit 类所写的消息映射的?还是从函数体出发
  LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
  {
   // special message which identifies the window as using AfxWndProc
   if (nMsg == WM_QUERYAFXWNDPROC)
    return 1;
   // all other messages route through message map
   CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
   return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
  }
  如上所列::AfxWndProc 整个函数只有四行,显然它仅仅是包装了::AfxCallWndProc 函数,只是把hWnd参数包装成pWnd,然后转道::AfxCallWndProc。
  ::AfxCallWndProc该函数才是真正做了一些事的,但其中与消息传递有关直接关系的就一句:
  LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
  WPARAM wParam = 0, LPARAM lParam = 0)
  {
   ...
   // delegate to object's WindowProc
   lResult = pWnd->WindowProc(nMsg, wParam, lParam);
   ...
   return lResult;
  }
  现在我们已经看到通过::AfxWndProc/::AfxCallWndProc 两个函数的接力,操作系统中消息被传递到MFC环境中的。
  进一步的讨论可以把所有的目光都集中到LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
  四点三:总结
  我们看到转机了:为了实现不同的函数调用,OOP(面对对象编程)本身提供继承、虚函数之类的许多的方法。MFC正是一种面对对象的语言
  现在CsuperEdit 是继承自CEdit,CEdit 又继承自CWnd,我们要让程序调用CsuperEdit::OnChar 也就没什么技术难度。比如,可以在CWnd中写一个响应键盘消息的虚函数 virtual void CWnd::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);,并在CWnd::WindowProc 中调用OnChar
  那么我只要重载CsuperEdit::OnChar 函数,程序自然而然就会调用我写的函数了
  微软为了减小程序文件的体积,做了一些优化工作,它未用virtual 修饰符来修饰所有的函数,而是把“要响应的消息和相应的响应函数”登记在一张MESSAGE_MAP(称,消息映射)里。
  在AFXMSG_.H文件中ON_WM_CHAR 宏定义被为{WM_CHAR, 0, 0, ... &OnChar},它的作用就是把WM_CHAR和当前类(现在指CsuperEdit)的OnChar函数,填加到了消息映射的登记表中
  既然有了“消息映射”这样一张的登记表,对于“让CWnd在接受到WM_CHAR 消息时调用CsuperEdit::OnChar”的算法和代码,估计你我都能在两小时内实现,我就不在此处罗嗦了,至于MFC中的相关的代码请参考“深入浅出”一书
分享到:
评论

相关推荐

    SubclassWindow

    SubclassWindow

    走出MFC子类化的迷宫子类化,SUBCLASSWINDOW ,MFC消息机制

    走出MFC子类化的迷宫子类化,SUBCLASSWINDOW ,MFC消息机制

    仿QQ宠的VC程序

    仿QQ宠的VC程序,使用CShockwaveFlash控件,用SetLayeredWindowAttributes进行透明,用HWND hWnd = m_FlashPlayer.Detach();m_FlashPlayer.SubclassWindow(hWnd);子类化控制 CShockwaveFlash控件的事件。

    Softgroup Components.NET Subclass and Hook Objects v2.0.3870

    SubclassWindow: provides native subclass of windows messages for a form and/or control. Requirements Prior to running Setup to install the Softgroup .Net Subclass and Hook Objects you must already ...

    易语言彗星热键模块

    彗星热键模块,彗星注册窗口热键,彗星撤销窗口热键,热键处理初始化,CallProc,SetHotKeyProp,GetHotKeyProp,IsHotKey,Hook_WndProc,Hook_OnCreate,HotKey_CallWindowProc,HotKey_SubClassWindow,HotKey_OnMessage,...

    VC2010&2012;框架背景

    网上现有的介绍VC中如何给框架加背景图片资料只对VC2010以下的有效,而VC2010及其以上对框架类做了某些更改,使得不在适用,在m_wndMDIClient.SubclassWindow(m_hWndMDIClient);时会出错。我上传的资料对VC2010&2012...

    stdafx.h代码

    #undef SubclassWindow #undef CopyRgn #endif #ifdef _AFX_PACKING #pragma pack(push, _AFX_PACKING) #endif ///////////////////////////////////////////////////////////////////////////// // Classes ...

Global site tag (gtag.js) - Google Analytics