子类化和超类化(win32 SDK)
本文所提到的子类化和超类化中的“类”并不是C++中的“类”,而是windows32 SDK编程中的RegisterClass函数用到的类,比如一个窗口类,它是WNDCLASS或者是WNDCLASSEX这样的一个结构。虽然它和C++中的“类”并不是一个概念,但是却有许多相似之处,即它们都有“模板”这样的一个概念或者功能。这里的子类化是对窗口功能的调整与扩展,而超类化是对类功能的调整与扩展。下面我们就用2个实例分别说明这两个概念。
文本输入框,我们可能都很熟悉,一般的文本输入框可以输入任何的字符,而当我们指定了ES_NUMBER风格之后,则该文本框只能输入数字,假设我们现在要求文本框只能输入16进制数,那么该怎么处理呢?如果我们重写文本框控件的所有功能,显然是吃力不讨好的事情,可是现有的文本框又不能满足我们的要求,它的所有功能都被windows封装起来了,没法直接修改,这样,一个好的方法就是我们继承原来的文本框,但是在此基础上进行小小的修改, 我们拦截windows发给文本框的WM_CHAR消息,然后判断输入的字符是不是'0'-'9','a'-'f'('A'-'F'),如果是,则我们将消息转发给文本框的默认窗口处理过程,否则,我们将消息丢弃,这样文本框就不能接收到除了16进制字符以外的字符消息了,即其它的字符被屏蔽了,但是其它的非WM_CHAR消息仍然丢给默认的窗口过程。
下面的程序,在16进制和10进制数之间进行相互的转换。16进制文本编辑框是我们子类化的控件。代码如下:
// resource.h
// 资源的ID值定义
#define IDD_MAIN 101
#define IDC_HEX 1001
#define IDC_DEC 1002
#define IDC_STATIC -1
// SubClass.rc
// 资源文件
#include "resource.h"
#include "afxres.h"
////////////////
// 对话框定义
// Dialog
//
IDD_MAIN DIALOG DISCARDABLE 0, 0, 130, 47
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Hex <> Dec"
FONT 10, "System"
BEGIN
LTEXT "Hex",IDC_STATIC,7,7,22,8
LTEXT "Dec",IDC_STATIC,7,29,22,8
EDITTEXT IDC_HEX,39,7,79,14,ES_AUTOHSCROLL
EDITTEXT IDC_DEC,39,26,79,14,ES_AUTOHSCROLL | ES_NUMBER
END
//SubClass.c
// 实现文件
#include
#include "resource.h"
HWND hWinMain = NULL; // 主对话框句柄
DWORD dwOption = 0; // 标志位,用于控制WM_COMMAND消息的处理
WNDPROC lpOldProcEdit = NULL; // 16进制编辑框的默认窗口过程地址
char szFmtDec2Hex[] = "%08X";
char szFmtHex2Dec[] = "%u";
// 16进制编辑框允许输入的字符,\x08表示的退格键,用于删除一个字符
char szAllowedChar[] = "0123456789ABCDEFabcdef\x08";
// 新的16进制编辑框窗口处理过程,这里根据需要只拦截了WM_CHAR消息
// 其它的消息,仍然由原来的窗口处理过程来处理
LRESULT CALLBACK ProcEdit(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int i;
if ( uMsg == WM_CHAR )
{
for (i = 0; i < sizeof(szAllowedChar); i++ )
{
if ( szAllowedChar[i] == (char)wParam )
{ // 将小写的'a'-'f'转换为大写
if ( wParam > '9' )
{
wParam &= ~0x20;
}
return CallWindowProc(lpOldProcEdit, hWnd, uMsg, wParam, lParam);
}
}
return TRUE;
}
return CallWindowProc(lpOldProcEdit, hWnd, uMsg, wParam, lParam);
}
// 将16进制数值转换为10进制
void Hex2Dec()
{
char szBuffer[512];
unsigned long res = 0;
unsigned int nbase = 1;
int nLen;
int i;
unsigned int tmp;
GetDlgItemText(hWinMain, IDC_HEX, szBuffer, sizeof(szBuffer));
nLen = strlen(szBuffer);
nLen--;
for ( i = nLen; i >=0; i-- )
{
if ( szBuffer[i] >'9' )
tmp = szBuffer[i] - 'A' + 10;
else
tmp = szBuffer[i] - '0';
tmp *= nbase;
nbase <<= 4;
res += tmp;
}
wsprintf(szBuffer, szFmtHex2Dec, res);
SetDlgItemText(hWinMain, IDC_DEC, szBuffer);
}
// 将10进制数值转换为16进制
void Dec2Hex()
{
char szBuffer[512];
unsigned int n = GetDlgItemInt(hWinMain, IDC_DEC, NULL, FALSE);
wsprintf(szBuffer, szFmtDec2Hex, n);
SetDlgItemText(hWinMain, IDC_HEX, szBuffer);
}
// 主对话框过程
LRESULT CALLBACK ProcDlgMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HWND hWndHex;
switch ( uMsg )
{
case WM_CLOSE:
EndDialog(hWnd, 0);
break;
case WM_INITDIALOG:
{
hWinMain = hWnd;
SendDlgItemMessage(hWnd, IDC_HEX, EM_LIMITTEXT, 8, 0);
SendDlgItemMessage(hWnd, IDC_DEC, EM_LIMITTEXT, 10, 0);
hWndHex = GetDlgItem(hWnd, IDC_HEX);
lpOldProcEdit = (WNDPROC)SetWindowLong(hWndHex, GWL_WNDPROC, (LONG)ProcEdit);
}
break;
case WM_COMMAND:
/* 由于在调用Hex2Dec的时候使用了SetDlgItemText, 而此函数会发送WM_COMMAND消息
当收到WM_COMMAND消息的时候又进行转换计算,然后又调用WM_COMMAND, 这样,程序就
陷入了WM_COMMAND消息的死循环, 为了避免此种情况,于是设置了一个标志dwOption,
当dwOption为1的时候,表示正在计算和转换,转换完毕之后重新置为0,这样就能防止
正在计算的时候又处理新的WM_COMMAND消息,避免了死循环
*/
if ( !dwOption )
{
dwOption = 1;
if ( LOWORD(wParam) == IDC_HEX )
Hex2Dec();
else if ( LOWORD(wParam) == IDC_DEC )
Dec2Hex();
dwOption = 0;
}
break;
default:
return FALSE;
}
return TRUE;
}
int WINAPI WinMain(IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, IN LPSTR lpCmdLine, IN int nShowCmd )
{
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)ProcDlgMain, (LPARAM)0);
return 0;
}
上面的程序在VC6++下调试通过。编译方法是,在VC6++下,新建一个win32 application , 然后将上面3个文件加入到工程,F7编译即可。
上面的子类化只是针对一个控件,可是当我们需要多个16进制输入框的时候,该怎么办呢?如果一个一个的子类化,显然是不合适的,那么好的方法就是将该文本框这个“类”进行改写, 让它符合我们的需要,然后,所有的16进制输入框都从这个“类”“继承”而来。
下面的例子,用了5个16进制输入框,每个输入框都从我们的“类”“继承”而来,拥有相同的功能,即只能输入16进制字符和退格键,不能输入其它的字符。具体实现代码如下:
// resource.h
// 资源ID定义文件
#define IDD_MAIN 101
// SuperClass.rc
// 资源定义文件
#include "resource.h"
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
// 可以看出下面的对话框有5个自定义控件,而这个控件的“类”就是"HexEdit"
// "HexEdit"是我们自定义的一个"类"名,它继承自“Edit”,它只能输入16进制
// 字符和退格键,其它的功能跟一般的Edit文本输入框没有两样。
IDD_MAIN DIALOG DISCARDABLE 0, 0, 126, 113
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Hex Edit Box (Super Cls)"
FONT 10, "System"
BEGIN
CONTROL "", -1, "HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,7,9,104,14
CONTROL "", -1, "HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,7,49,104,14
CONTROL "", -1, "HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,7,29,104,14
CONTROL "", -1, "HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,7,69,104,14
CONTROL "", -1, "HexEdit",ES_LEFT | WS_BORDER | WS_TABSTOP,7,89,104,14
END
// SuperClass.c
// 实现文件
#include
#include "resource.h"
HINSTANCE hInst = NULL;
HWND hWinMain = NULL;
WNDPROC lpOldProcEdit = NULL;
char szAllowedChar[] = "0123456789abcdefgABCDEFG\x08";
char szEditClass[] = "Edit";
char szClass[] = "HexEdit";
//16进制文本框的新的窗口过程,只处理WM_CHAR消息,其它的丢给默认的处理过程
LRESULT CALLBACK ProcEdit(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int i;
int nLen = strlen(szAllowedChar);
if ( uMsg == WM_CHAR )
{
for ( i = 0; i < nLen; i++ )
{
if ( szAllowedChar[i] == (char)wParam ) // wParam是字符的ASCII码
{
// 如果字符大于‘9’,表示是'a'-'f' 或者是'A' - 'F'
// 无论大小写,一律转换为大写
if ( wParam > '9' )
wParam &= ~0x20;
return CallWindowProc(lpOldProcEdit, hWnd, uMsg, wParam, lParam);
}
}
return TRUE;
}
return CallWindowProc(lpOldProcEdit, hWnd, uMsg, wParam, lParam);
}
// 超类化, 用GetClassInfoEx获取原有的类“Edit”的所有信息
// 然后用我们自定义的窗口处理过程取代原有的窗口过程
// 用我们自定义的类名“HexEdit”取代原有的类名“Edit”
// 然后用RegisterClassEx向windows注册我们的新类
void SuperClass()
{
WNDCLASSEX stWC;
stWC.cbSize = sizeof(stWC);
GetClassInfoEx(NULL, szEditClass, &stWC);
lpOldProcEdit = stWC.lpfnWndProc;
stWC.lpfnWndProc = ProcEdit;
stWC.hInstance = hInst;
stWC.lpszClassName = szClass;
RegisterClassEx(&stWC);
}
LRESULT CALLBACK ProcDlgMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if ( uMsg == WM_CLOSE )
{
EndDialog(hWnd, 0);
return TRUE;
}
return FALSE;
}
int WINAPI WinMain(IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, IN LPSTR lpCmdLine, IN int nShowCmd )
{
hInst = hInstance;
SuperClass();
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)ProcDlgMain, (LPARAM)0);
return 0;
}
以上的编译过程同上面的子类化程序。需要说明的是,这里面的两个程序是由罗云彬的32位汇编语言中的程序改写而来的,如果想详细的了解其中的原理,你也可以看看罗云彬用32位汇编写的程序。
阅读(1814) | 评论(0) | 转发(0) |