設置 | 登錄 | 註冊

目前共有4篇帖子。

【教程】Win32 WNDCLASS窗口類中的額外空間cbClsExtra和cbWndExtra的用法

1樓 巨大八爪鱼 2016-2-19 23:16
先來講講wc.cbWndExtra這個成員,這個成員指定了系統為每個窗口分配多少字節的額外空間。創建窗口後,如果想要使用這段額外空間,可以使用以下六個函數之一:
GetWindowWord(hWnd, n)  // 從第n個字節開始,讀取兩個字節
GetWindowLong(hWnd, n)  // 從第n個字節開始,讀取四個字節
GetWindowLongPtr(hWnd, n) // 從第n個字節開始讀取。如果系統是32位,則讀取4個字節;如果系統是64位,則讀取8個字節。(這個一般用來讀取指針變量)
SetWindowWord(hWnd, n, value)
SetWindowLong(hWnd, n, value)
SetWindowLongPtr(hWnd, n, value)
還有另外6個類似的函數,是以GetClass或SetClass開頭的,那些函數是用來操作wc.cbClsExtra為窗口類分配的空間的,但是傳入的參數仍然是hWnd,而非窗口類的名稱或指針,也就是根據hWnd來確定是哪一個窗口類。
 
例如,讓wc.cbWndExtra=3,也就是為每個窗口分配3個字節的額外空間。註冊窗口類後創建窗口,然後執行:
char str[100];
int a, b;
SetWindowWord(hWnd, 0, 2016); // 語句1
a = GetWindowWord(hWnd, 0); // 語句2
b = GetWindowWord(hWnd, 1); // 語句3
 
sprintf_s(str, "a=%d, b=%d", a, b);
MessageBoxA(hWnd, str, "額外空間", MB_ICONINFORMATION);
運行程序,可以看到a的值為2016,b的值為7。
創建窗口時分配的3個字節,初始值都是0:
[00000000][00000000][00000000]
語句1把第0個字節和第1個字節分別設為了2016的低八位和高八位,因為2016對應的二進位數是[00000111][11100000],所以這3個字節變成了:
[11100000][00000111][00000000]
語句2的作用是從第0個字節開始,讀取兩個字節,也就是讀取第0和第1個字節,然後賦給變量a,剛好就是2016。
語句3是從第1個字節開始讀兩個字節賦給b,所以就把第1個字節賦給b的低位,第2個字節賦給b的高位。
b = [00000000][00000111] = 7
 
如果執行GetWindowWord(hWnd, 2)會怎麼樣呢?這條語句的作用是同時讀取第2個字節和第3個字節,但是因為根本就沒有第3個字節,所以這個函數會執行失敗,返回0。也就是說,只要讀取時有一個字節不存在,那麼整個函數就會讀取失敗。
因此,cbClsExtra和cbWndExtra都只能設置為0或者大於等於2的數,如果設置為1,那麼即便是分配了空間也無法使用。
 
根據這一點,我們就可以編寫在額外空間中讀寫數據塊的函數,然後把字符串或者一個結構體變量放入這段額外空間中。

下面的示例程序將一個struct student結構體寫入了這段額外空間中。為了確保函數讀取最後一個字節時不會出錯,設置空間分配大小時使用了EXTRA_BYTES宏將奇數化為偶數。
2樓 巨大八爪鱼 2016-2-19 23:16
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>

struct student
{
    char id[13];
    char name[10];
    int age;
};
char *hex = "0123456789ABCDEF";

// 奇數化為偶數
#define EXTRA_BYTES(n) (((n + 1) >> 1) << 1)

// 寫入字符串。end為BOOL類型,表示是否寫入字符串最後的'\0'
#define extra_puts(hWnd, dest, str, end) extra_write(hWnd, dest, str, strlen(str) + end)

// 獲取分配的額外空間大小
#define extra_size(hWnd) GetClassLong(hWnd, GCL_CBWNDEXTRA)

// 讀取字符,src為額外空間的第幾字節
char extra_getc(HWND hWnd, int src)
{
    WORD word;
    if (src % 2 == 0)
    {
        word = GetWindowWord(hWnd, src);
        return word & 0xff;
    }
    else
    {
        word = GetWindowWord(hWnd, src - 1);
        return word >> 8;
    }
}

// 讀取\0結束的字符串
void extra_gets(HWND hWnd, int src, void *buffer)
{
    int i;
    char ch;
    WORD value;
    for (i = -(src % 2); ; i += 2)
    {
        value = GetWindowWord(hWnd, src + i);
        if (i != -1)
        {
            ch = *(((BYTE *)buffer) + i) = value & 0xff;
            if (ch == '\0')
                break;
        }
        ch = *(((BYTE *)buffer) + i + 1) = value >> 8;
        if (ch == '\0')
            break;
    }
}

// 寫入一個字符
void extra_putc(HWND hWnd, int src, char ch)
{
    WORD word;
    if (src % 2 == 0)
    {
        word = GetWindowWord(hWnd, src);
        SetWindowWord(hWnd, src, (word & 0xff00) + ch);
    }
    else
    {
        word = GetWindowWord(hWnd, src - 1);
        SetWindowWord(hWnd, src - 1, (word & 0x00ff) + (ch << 8));
    }
}

// 讀取數據塊(例如結構體)
void extra_read(HWND hWnd, int src, void *buffer, int n)
{
    int i;
    WORD value;
    for (i = -(src % 2); i < n; i += 2)
    {
        // dest+i始終為偶數
        value = GetWindowWord(hWnd, src + i);
        if (i != -1)
            *(((BYTE *)buffer) + i) = value & 0xff;
        if (i + 1 != n)
            *(((BYTE *)buffer) + i + 1) = value >> 8;
    }
}

// 寫入數據塊
void extra_write(HWND hWnd, int dest, void *data, int n)
{
    int i;
    WORD value;
    for (i = -(dest % 2); i < n; i += 2)
    {
        // dest+i始終為偶數
        if (i == -1)
            value = GetWindowWord(hWnd, dest - 1) & 0xff; // 讀取前一個字節
        else
            value = *(((BYTE *)data) + i);
        if (i + 1 == n)
            value += GetWindowWord(hWnd, dest + i) & 0xff00; // 讀取後一個字節
        else
            value += *(((BYTE *)data) + i + 1) << 8;
        SetWindowWord(hWnd, dest + i, value);
    }
}




LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    // 這裡的hWnd的值和主函數裡的hWnd是完全相同的
    char *str, *pStr;
    char str2[100];
    int size, i, value;

    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;
    struct student stu;

    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);

        // 輸出全部額外空間的內存情況
        size = extra_size(hWnd);
        pStr = str = new char[size * 4];
        for (i = 0; i < size; i += 2)
        {
            value = GetWindowWord(hWnd, i);
            *pStr++ = hex[(value >> 4) & 0x0f];
            *pStr++ = hex[value & 0x0f];
            *pStr++ = ' ';
            *pStr++ = hex[(value >> 12) & 0x0f];
            *pStr++ = hex[(value >> 8) & 0x0f];
            *pStr++ = ' ';
        }
        *pStr = '\0';
        GetClientRect(hWnd, &rect);
        DrawTextA(hdc, str, strlen(str), &rect, DT_WORDBREAK);
        delete[] str;

        // 輸出結構體的內容
        extra_read(hWnd, 0, &stu, sizeof(struct student));
        sprintf_s(str2, sizeof(str2), "學號: %s    姓名:%s    年齡:%d", stu.id, stu.name, stu.age);
        TextOutA(hdc, 0, 100, str2, strlen(str2));

        EndPaint(hWnd, &ps);
        break;
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return FALSE;
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    WNDCLASS wc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = EXTRA_BYTES(sizeof(struct student)); // 確保分配的空間為偶數
    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = TEXT("Example Window");
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&wc);

    RECT rect;
    SetRect(&rect, 0, 0, 640, 480);
    AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);

    HWND hWnd = CreateWindow(wc.lpszClassName, TEXT("Example"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL);
    if (!hWnd)
        return 1;
    ShowWindow(hWnd, nCmdShow);

    struct student stu;
    strcpy_s(stu.id, "200000000012");
    strcpy_s(stu.name, "張三");
    stu.age = 15;
    extra_write(hWnd, 0, &stu, sizeof(struct student));

    // 一定要在額外空間寫入數據後再刷新窗口,不然窗口顯示時只能看到一串0
    // UpdateWindow立即執行WM_PAINT,執行完了函數才返回
    UpdateWindow(hWnd);
    
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
3樓 巨大八爪鱼 2016-2-19 23:17
【運行結果】

4樓 巨大八爪鱼 2016-2-19 23:17
其中最上面一排是這段額外空間的內存情況,中間那一排是輸出的結構體信息。
注意UpdateWindow不要放到extra_write前面了,否則窗口打開時全是一串0,其它什麼也沒有。

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。