|   一派護法 十九級 | 
              先来讲讲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宏将奇数化为偶数。
 
 | 
                
          |   一派護法 十九級 | 
              #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;
 }
 |