MFC的子类化技术

作者在 2008-10-05 23:05:13 发布以下内容
MFC的子类化技术  作者:laiyiling

Windows是一个基于消息的系统,消息在Windows的对象之间进行着传递。子类化和Windows的钩子机制存在于消息系统之中,我们可以利用这些机制来操纵、修改甚至丢弃那些在操作系统或是进程中传递的消息,以求改变系统的一些行为。

     子类化技术用来截取窗口或控件之间的消息,当然是消息在到达目的窗口之前完成的操作。这些被截获的消息既可以保留也可以修改它们的状态,之后就继续发送到目的地。子类化技术实现了一些正常情况下无法实现的功能,试想鼠标右键单击TextBox,系统默认弹出Undo、Cut、Copy、Paste等菜单,我们就可以利用子类化技术来改变这个系统菜单。

    简单的说,子类化就是创建一个新的窗口消息处理过程,并将其插入到原先的默认窗口消息处理过程之前。子类化分为三类:实例子类化(instance subclassing)—从窗口或控件的单一实例截获消息,这种子类化技术最普遍;全局子类化(global subclassing)—能够截获从相同的窗口类创建出来的多个窗口或控件的消息;超类化(superclassing)—和全局子类化很类似,区别在于可以应用在新的窗口类上面。
  
     首先,我们看看这个C++程序:


#include <iostream>
using namespace std;
class Parent
{
public:
  void func { cout << "Parent" << endl; }
};
class Child : public Parent
{
public:
  void func { cout << "Child" << endl; }
};
void main()
{
  Parent p;
  Child c;
  p.func();
  c.func();
}



  现在我来解说一下。这段代码中我定义了两个C++类:父类和子类,并且子类是继承自父类的;它们有一个具有相同名称的成员函数func。在main函数中,我分别构造了父类和子类的对象,并调用了它们各自的成员函数func。结果如下:


Parent
Child

 

   简单说来,这段代码就是子类根据自己的需要改写了func成员函数。而Win32的子类化的原理也与此类似,只不过子类化实际上并没有像C++一样重载 哪个函数,而是靠拦截Windows系统中的某些消息来自己进行处理罢了。举例来说,请大家看以下这段简单的窗口回调过程:


LRESULT CALLBACK ProcMain(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
  switch (Msg)
  {
  case WM_CLOSE:
    EndDialog(hDlg, 0);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  }
  return 0;
}


   在这个回调之中,我手动处理了两个消息:在单击了“关闭”按钮(WM_CLOSE)的时候,我将对话框关闭(EndDialog);在对话框销毁 (WM_DESTROY)的时候,我向系统消息队列中发送了退出的消息来完成结束工作(PostQuitMessage)。也就是说,如果把 WM_CLOSE的响应代码改成:


  case WM_CLOSE:
    ShowWindow(hDlg, SW_MINIMIZE);
    break;


  这样一来,这个对话框就会和MSN一样,在单击了“关闭”之后,就会完成最小化的工作了。那么,对于窗口过程已定义好的系统控件,将如何手动响应它的消息呢?

  我们可以用函数指针的办法,将我们感兴趣的消息拦截下来,处理完之后再让预定义的窗口过程处理。这个过程大致如下:


  WNDPROC OldProc;
  OldProc = (WNDPROC)SetWindowsLong(hWnd, GWL_WNDPROC, (LONG)NewProc);

 

   当然,这里的新窗口过程NewProc是预先由你实现好的。上述代码执行以后,系统在处理hWnd的窗口消息时,就会先进入你实现的NewProc回调 过程,然后在处理过你感兴趣的消息之后,通过CallWindowProc函数和你预先保存的OldProc再次回到原来的回调过程中完成剩余的工作。

  以上就是窗口子类化的原理分析,下面我通过一个实例来实际解说如何对窗口进行子类化。当我们需要一个特殊的Edit来限制浮点数的输入,但是现有的Edit却并不能完成这项工作——因为它只能够单纯的限制大小写或者纯数字。就可以采用“子类化”。

下面我开始按步骤完成对这两个窗口的子类化:
  第一步,在主窗口对话框初始化的时候,保存原有的窗口过程,并设置新的窗口过程。代码如下:


  case WM_INITDIALOG:
    EditProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_EDIT), GWL_WNDPROC, (LONG)ProcFloat);
    StaticProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_ST_HOMEPAGE), GWL_WNDPROC, (LONG)ProcLink);
    break;



  第二步,实现浮点编辑框的窗口过程:


LRESULT CALLBACK ProcFloat(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
  if (Msg == WM_CHAR && wParam != '.' && (wParam <= '0' || wParam >= '9') && wParam != VK_BACK)
  {
    MessageBeep(MB_OK);
    return 0;
  }
  else
    return CallWindowProc(EditProc, hWnd, Msg, wParam, lParam);
}



   这里需要解释的是,由于控件本身的需求,所以只需要拦截一个消息,就是接收字符的WM_CHAR。当用户输入的字符不是小数点、0~9以及退格键(注意 不要少了退格键,否则你将会发现你的编辑框无法删除输入错误的数字)的时候,就发出一声声音以提示输入错误。至于其它的消息,则调用原有的回调函数进行处 理。

    子类化的限制:因子类化是对已存在的某一窗口产生作用,所以其作用范围只有这一窗,又由于可能不清楚该类怎样使用额外的类和窗口字节,所以不能保证正确使 用这些空间存储信息,最后,因窗口已存在,所以新的窗口过程永远不会接收到第一个WM_CREATE消息或其他以前的消息。子类化只适用于改变极少数窗口 行为和属性时使用。
技术 | 阅读 7282 次
文章评论,共2条
vfdff(作者)
2008-10-05 23:29
1
类属性:子类化的就是AFXWND42,子类化了的就是AFXOLECONTRLWND42<br />
char szClassName[100];<br />
::GetClassName(QQWnd,szClassName,100);<br />
可以通过上述示例中的GetClassName函数获取窗口的类属性
vfdff(作者)
2008-10-05 23:31
2
使用子类化技术,让 对话框中的 Edit 控件接受 TAB 键(纯属抄袭MSDN)<br />
<br />
The following example shows how to subclass an instance of an edit control in a dialog box. The subclass window procedure enables the edit control to receive all keyboard input, including the ENTER and TAB keys, whenever the control has the input focus.<br />
<br />
WNDPROC wpOrigEditProc; <br />
 <br />
LRESULT APIENTRY EditBoxProc(<br />
&nbsp;&nbsp;&nbsp; HWND hwndDlg, <br />
&nbsp;&nbsp;&nbsp; UINT uMsg, <br />
&nbsp;&nbsp;&nbsp; WPARAM wParam, <br />
&nbsp;&nbsp;&nbsp; LPARAM lParam) <br />
{ <br />
&nbsp;&nbsp;&nbsp; HWND hwndEdit; <br />
 <br />
&nbsp;&nbsp;&nbsp; switch(uMsg) <br />
&nbsp;&nbsp;&nbsp; { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case WM_INITDIALOG: <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Retrieve the handle to the edit control. <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hwndEdit = GetDlgItem(hwndDlg, ID_EDIT); <br />
 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Subclass the edit control. <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wpOrigEditProc = (WNDPROC) SetWindowLong(hwndEdit, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GWL_WNDPROC, (LONG) EditSubclassProc); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Continue the initialization procedure. <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return TRUE; <br />
 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case WM_DESTROY: <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Remove the subclass from the edit control. <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetWindowLong(hwndEdit, GWL_WNDPROC, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (LONG) wpOrigEditProc); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Continue the cleanup procedure. <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break; <br />
&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp; return FALSE; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UNREFERENCED_PARAMETER(lParam); <br />
} <br />
 <br />
// Subclass procedure <br />
LRESULT APIENTRY EditSubclassProc(<br />
&nbsp;&nbsp;&nbsp; HWND hwnd, <br />
&nbsp;&nbsp;&nbsp; UINT uMsg, <br />
&nbsp;&nbsp;&nbsp; WPARAM wParam, <br />
&nbsp;&nbsp;&nbsp; LPARAM lParam) <br />
{ <br />
&nbsp;&nbsp;&nbsp; if (uMsg == WM_GETDLGCODE) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return DLGC_WANTALLKEYS; <br />
 <br />
&nbsp;&nbsp;&nbsp; return CallWindowProc(wpOrigEditProc, hwnd, uMsg, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wParam, lParam); <br />
}
游客请输入验证码
浏览1970533次