Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1619924
  • 博文数量: 441
  • 博客积分: 20087
  • 博客等级: 上将
  • 技术积分: 3562
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-19 15:35
文章分类

全部博文(441)

文章存档

2014年(1)

2012年(1)

2011年(8)

2010年(16)

2009年(15)

2008年(152)

2007年(178)

2006年(70)

分类: C/C++

2008-03-12 16:13:22

子类化和超类化(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) |
给主人留下些什么吧!~~