天天看點

子類化和超類化(win32 SDK)

                                                            子類化和超類化(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 <windows.h>

#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:

        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 <windows.h>

#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;

}

以上的編譯過程同上面的子類化程式。

繼續閱讀