子类化(Subclass)是指替换窗口过程(WNDPROC)
超类(Superclass)是替换窗口过程(WNDPROC),并且替换ClassName。
Subclass不太像继承,而像一种外挂(HOOK)行为。
Superclass更像继承,因为生成了新的窗口类,并且继承了行为。
WinX中Subclass和Superclass用同一个类实现,都是 WinX::SubclassWindow。
关于用法上:
以Button为例,如果是SubClass,那么用户先要有一个Button,然后Subclass它。
也就是Subclass发生在CreateWindow之后。
如果是Superclass,那么用户CreateWindow时直接传入新的窗口类名称,根本没有Button被生成。
当然,这要求CreateWindow之前调用该窗口类的RegisterClass
MFC是子类化技术
---------------------------------------------------------------------------------------华丽分割---------------------------------
窗体子类化的作用:窗口子类化技术最大的特点就是能够截获windows的消息。一旦用户自定义的窗口函数截取了传向原窗口函数的消息,就可以对被截取的消息进行如下处理:
将其传给原窗口函数。这是对大多数消息应该采取的措施,因为子类通常只对原来的窗口特性做少量的改动。
截取该消息,阻止其向原窗口函数发送。
修改该消息,修改完毕以后再向原窗口函数发送。
Windows SDK提供了一些设计好的窗口类,如edit listbox treeview 等。通过截取这些通用窗口类的消息,用户程序可以为它们添加新的特性,改善其外观,扩充其功能
子类化的优点:不需创建新的窗口类,不需了解一个窗口的窗口过程。这在原来的窗口函数是由别人编写,而且创建过程不可见的情况下非常有用。子类化比较容易实现,因为只需写一个窗口函数
VC中实现窗口子类化:
VC基于SDK编程的窗口子类化的基本步骤如下:
正常创建原始窗口,得到窗口的句柄。
调用GetWindowLong得到原来的窗口函数OldWndProc
调用SetWindowLong设置新的窗口函数NewWndProc
新的窗口函数的代码如下所示:
LRESULT NewWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
if(msg==WM_USER+N)
{
}
return CallWindowProc(OldWndProc,hwnd,msg,wParam,lParam);
}
注意:调用旧的窗口函数时,不能直接用OldWndProc(....),必须CallWndProc,否则出现堆栈错误;
窗口子类化允许你接管被子类化的窗口,使你对它有绝对的控制权
子类化并不局限于控件,可以子类化任意窗口,现在我们把精力集中于如何子类化一个窗口上,让我们想想Windows如何知道button控件的窗口处理函数放在哪里,
通过WNDCLASSEX 成员 lpfnWndProc指出了窗口函数地址,如果能用自己编写的窗口函数地址来替换这个成员变量,那Windows不就把消息发到自定义窗口函数了么,通过调用SetWindowLong来实现。工作还是比较简单的:写一个窗口函数用于处理发给button控件的消息,用参数GWL_WNDPROC调用SetWindowLong,如果调用成功就返回与调用功能相联系的一个32位整数。注意:有一些不处理的消息,需要派遣给原来的窗口函数来处理。
在创建了button控件后,通过调用SetWindowLong把原来的窗口函数地址替换为自定义函数的地址,从而对它实施了窗口子类化,要注意为了调用函数CallWindowproc,我们存储了原窗口函数地址,自己编写的hBmpButtonProc仅仅是个普通的窗口函数,当然也可以调用一次SetWindowLong来储存这个32位值.
---------------------------------------------------------------------------------------华丽分割---------------------------------
利用子类化技术拓展Edit控件功能
认识子类化技术
把一个窗体子类化其实就是使用SetwindowLong为该窗体设计一个新的窗体过程,然后可以在该窗体函数中处理特定消息,而那些不关心的消息可以通过CallWindowProc传递给默认处理
从功能上说,子类化技术有点类似于钩子函数(SetWindowHookEx),都可以对一些特定的消息进行处理。但两者有本质区别,钩子函数是针对系统的消息传递机制,而子类化是针对特定的窗体。利用钩子可以处理某一类或几类消息,而利用子类化技术则能处理特定窗体的绝大部分消息。
一个对话框(IDD_TEST_SAMPLE)和一个EDIT控件(IDC_TEST)
#include "resource.h"
#include <tchar.h>
#include <windows.h>
LONG DefWndProc=NULL;
LRESULT CALLBACK EditNewProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
BOOL CALLBACK TestSampleProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
DialogBox(hInstance,MAKEINTRESOURCE(IDD_TEST_SAMPLE,0,TestSampleProc);
return 0;
}
LRESULT CALLBACK EditNewProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
// return CallWindowProc((WNDPROC)DefWndProc,hwnd,msg,wParam,lParam);
if(msg==WM_CHAR)
{
.......
}
}
BOOL CALLBACK TestSampleProc(HWND hwnd,MSG msg,WPARAM wParam,LPARAM lParam)
{
LONG DlgWndProc,EditWndProc;
TCHAR buf[128];
switch(msg)
{
case WM_INITDIALOG:
DlgWndProc=GetWindowLong(hwnd,GWL_WNDPROC);
EditWndProc=GetWindowLong(GetDlgItem(hwnd,IDC_TEST),GWL_WNDPROC);
wsprintf(buf,_T("DlgWndProc :%08X\nEditWndProc : %08X\nDefWndProc:%08X\n"),
DlgWndProc,EditWndProc,DefWndProc);
MessageBox(hwnd,buf,_T("zileihuazhiqian"),0);
DefWndProc=GetDlgItem(hwnd,IDC_TEST),GWL_WNDPROC,(LONG)EditNewProc);
return true;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hwnd,0);
return true;
}
break;
case WM_LBUTTONDOWN:
DlgWndProc=GetWindowLong(hwnd,GWL_WNDPROC);
EditWndProc=GetWindowLong(GetDlgItem(hwnd,IDC_TEST),GWL_WNDPROC);
wsprintf(buf,_T("DlgWndProc : %08X\nEditWndProc: 08X\nDefWndProc : 08X\n"),
DlgWndProc,EditWndProc,DefWndProc);
MessageBox(hwnd,buf,_T("zileihuazhihou"),0);
return false;
}
return false;
}