深入学习Win32(一)
字数 1819 2025-08-07 08:22:29

Win32编程深入教学文档

0x00 前言

Win32 API是Windows平台下最基本的API接口,学习Win32编程是理解Windows操作系统底层机制的重要途径。本教程将从底层角度深入讲解Win32编程的关键知识点。

0x01 字符编码基础

多字节字符(窄字节字符)

  • ASCII码表:0111 1111 (0-127)
  • 扩展ASCII码表:1111 1111 (0-255)
  • 汉字表示:两个扩展ASCII码(2字节)拼成一个汉字
  • 问题:可能与韩文、日文编码冲突导致乱码

Unicode字符

  • 统一编码标准,每个符号占2字节
  • 解决多字节字符的兼容性问题

C语言中的字符处理

// 多字节字符
char x[] = "中国";  // d6 d0 b9 fa 00 (扩展ASCII编码)

// 宽字符
wchar_t x1[] = L"中国";  // 2d 4e fd 56 00 00 (Unicode编码)

// 打印处理
printf("%s\n", x);  // 使用控制台默认编码
wprintf(L"%s\n", x1);  // 默认使用英文

// 设置本地化支持中文
#include <locale.h>
setlocale(LC_ALL, "");

字符串操作函数对比

多字节字符函数 宽字符函数 功能描述
strlen wcslen 获取字符串长度
strcpy wcscpy 字符串复制
strcat wcscat 字符串拼接
strcmp wcscmp 字符串比较
strstr wcsstr 字符串查找

0x02 Win32 API字符处理

Win32字符类型

char      CHAR      // 多字节字符
wchar_t   WCHAR     // 宽字符
TCHAR               // 宏,根据项目字符集决定是CHAR还是WCHAR

Win32字符串指针类型

PSTR/LPSTR      // 指向多字节字符串
PWSTR/LPWSTR    // 指向宽字符串
PTSTR/LPTSTR    // 宏,根据项目字符集决定

字符赋值示例

CHAR cha[] = "中国";
WCHAR chw[] = L"中国";
TCHAR cht[] = TEXT("中国");  // 根据项目设置自动选择编码

PSTR pszChar = "china";
PWSTR pszWChar = L"china";
PTSTR pszTChar = TEXT("china");

API的多版本支持

Win32 API为需要字符串参数的函数提供两个版本和一个宏:

MessageBoxA(0, "内容多字节", "标题", MB_OK);      // 多字节版本
MessageBoxW(0, L"内容宽字节", L"标题", MB_OK);    // 宽字符版本
MessageBox(0, TEXT("根据项目字符集决定"), TEXT("标题"), MB_OK);  // 宏版本

0x03 Win32程序结构

WinMain函数

int APIENTRY WinMain(
    HINSTANCE hInstance,      // 当前实例句柄(ImageBase)
    HINSTANCE hPrevInstance,   // 永远为0
    LPSTR lpCmdLine,          // 命令行参数
    int nCmdShow              // 窗口显示方式
)

调试输出

// 调试输出函数
void __cdecl OutputDebugStringF(const char *format, ...) {
    va_list vlArgs;
    char *strBuffer = (char*)GlobalAlloc(GPTR, 4096);
    va_start(vlArgs, format);
    _vsnprintf(strBuffer, 4096-1, format, vlArgs);
    va_end(vlArgs);
    strcat(strBuffer, "\n");
    OutputDebugStringA(strBuffer);
    GlobalFree(strBuffer);
}

// 调试宏定义
#ifdef _DEBUG
#define DbgPrintf OutputDebugStringF
#else
#define DbgPrintf  // Release版本自动移除
#endif

错误处理

DWORD errorCode = GetLastError();  // 获取最后错误代码
// 使用MSDN查询错误代码含义

0x04 窗口与消息机制

消息结构体MSG

typedef struct tagMSG {
    HWND hwnd;      // 窗口句柄
    UINT message;   // 消息类型
    WPARAM wParam;  // 附加信息
    LPARAM lParam;  // 附加信息
    DWORD time;     // 消息创建时间
    POINT pt;       // 消息创建时的鼠标位置
} MSG, *PMSG;

消息处理流程

  1. 用户输入触发事件
  2. 事件被封装为MSG结构
  3. 消息进入系统队列
  4. 分流到应用程序消息队列
  5. 通过消息循环从队列取出消息
  6. 判断消息类型并处理

窗口创建与消息循环

// 1. 定义窗口类
WNDCLASS wndclass = {0};
wndclass.hbrBackground = (HBRUSH)COLOR_MENU;
wndclass.lpfnWndProc = WindowProc;  // 回调函数
wndclass.lpszClassName = className;
wndclass.hInstance = hInstance;

// 2. 注册窗口类
RegisterClass(&wndclass);

// 3. 创建窗口
HWND hwnd = CreateWindow(
    className,              // 类名
    TEXT("我的第一个窗口"),  // 标题
    WS_OVERLAPPEDWINDOW,   // 样式
    10, 10, 600, 300,      // 位置和大小
    NULL, NULL, hInstance, NULL);

// 4. 显示窗口
ShowWindow(hwnd, SW_SHOW);

// 5. 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

窗口回调函数

LRESULT CALLBACK WindowProc(
    HWND hwnd,      // 窗口句柄
    UINT uMsg,      // 消息类型
    WPARAM wParam,  // 附加信息
    LPARAM lParam   // 附加信息
) {
    switch (uMsg) {
        case WM_CREATE: {
            // 窗口创建消息处理
            return 0;
        }
        case WM_DESTROY: {
            PostQuitMessage(0);  // 退出消息循环
            return 0;
        }
        // 其他消息处理...
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);  // 默认处理
}

0x05 常见窗口消息解析

WM_CREATE

  • 触发时机:窗口创建时
  • wParam:未使用
  • lParam:指向CREATESTRUCT结构体指针

WM_MOVE

  • 触发时机:窗口移动时
  • wParam:未使用
  • lParam:
    • LOWORD: x坐标
    • HIWORD: y坐标

WM_SIZE

  • 触发时机:窗口大小改变时
  • wParam:调整类型
    • SIZE_MAXIMIZED: 最大化
    • SIZE_MINIMIZED: 最小化
    • SIZE_RESTORED: 恢复
  • lParam:
    • LOWORD: 新宽度
    • HIWORD: 新高度

WM_COMMAND

  • 触发时机:子窗口(如按钮)事件发生时
  • wParam:
    • LOWORD: 控件ID
    • HIWORD: 通知代码
  • lParam:控件句柄

0x06 子窗口控件

创建按钮

void CreateButton(HWND hwnd) {
    HWND hwndPushButton = CreateWindow(
        TEXT("button"),          // 类名
        TEXT("普通按钮"),        // 标题
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,  // 样式
        10, 10, 80, 20,         // 位置和大小
        hwnd,                   // 父窗口
        (HMENU)1001,            // 控件ID
        hAppInstance,           // 实例句柄
        NULL);
}

按钮类型

  • BS_PUSHBUTTON: 普通按钮
  • BS_CHECKBOX: 复选框
  • BS_RADIOBUTTON: 单选按钮

子窗口消息处理

case WM_COMMAND: {
    switch (LOWORD(wParam)) {
        case 1001:  // 按钮1 ID
            MessageBox(hwnd, "按钮1被点击", "提示", MB_OK);
            return 0;
        case 1002:  // 按钮2 ID
            // 处理代码...
            return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

0x07 对话框编程

对话框资源创建

  1. 在资源文件中添加Dialog资源
  2. 设置对话框属性(ID,标题等)
  3. 添加控件(按钮,文本框等)

对话框创建与显示

// 对话框消息处理函数
BOOL CALLBACK DialogProc(
    HWND hwndDlg,   // 对话框句柄
    UINT uMsg,      // 消息
    WPARAM wParam,  // 附加信息
    LPARAM lParam   // 附加信息
) {
    switch (uMsg) {
        case WM_INITDIALOG:
            return TRUE;
        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case IDOK:
                    EndDialog(hwndDlg, 0);
                    return TRUE;
            }
            break;
    }
    return FALSE;
}

// 显示对话框
DialogBox(
    hInstance,                  // 实例句柄
    MAKEINTRESOURCE(IDD_DIALOG), // 对话框ID
    NULL,                       // 父窗口
    DialogProc);                // 回调函数

获取对话框控件内容

case IDC_BUTTON_OK: {
    // 1. 获取文本框句柄
    HWND hEditUser = GetDlgItem(hDlg, IDC_EDIT_USERNAME);
    
    // 2. 获取文本框内容
    TCHAR szUserBuff[0x50];
    GetWindowText(hEditUser, szUserBuff, 0x50);
    
    MessageBox(NULL, szUserBuff, TEXT("用户名"), MB_OK);
    return TRUE;
}

0x08 资源文件处理

图标资源

// 加载图标
HICON hBigIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON_BIG));
HICON hSmallIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON_SMALL));

// 设置图标
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hBigIcon);
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon);

资源表结构

PE文件中的资源表是最复杂的结构之一,位于数据目录的第三项(下标2)。

资源目录结构

typedef struct _IMAGE_RESOURCE_DIRECTORY {
    DWORD Characteristics;
    DWORD TimeDateStamp;
    WORD MajorVersion;
    WORD MinorVersion;
    WORD NumberOfNamedEntries;  // 以名称命名的资源数量
    WORD NumberOfIdEntries;     // 以ID命名的资源数量
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

资源目录项结构

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
    union {
        struct {
            DWORD NameOffset:31;
            DWORD NameIsString:1;
        };
        DWORD Name;
        WORD Id;
    };
    union {
        DWORD OffsetToData;
        struct {
            DWORD OffsetToDirectory:31;
            DWORD DataIsDirectory:1;
        };
    };
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

资源类型

Windows预定义了16种资源类型:

  1. 光标
  2. 位图
  3. 图标
  4. 菜单
  5. 对话框
  6. 字符串
  7. 字体目录
  8. 字体
  9. 加速键
  10. 非格式化资源
  11. 消息列表
  12. 光标组
  13. 图标组
  14. 版本信息

0x09 调试技巧

消息断点

  1. 在调试器中设置条件断点
  2. 条件表达式示例:
    • [esp+8] == WM_COMMAND 命令消息
    • [esp+8] == WM_LBUTTONDOWN 鼠标左键按下

回调函数定位

  1. 通过RegisterClass找到窗口类结构
  2. 从窗口类结构中获取lpfnWndProc回调函数地址
  3. 在回调函数地址设置断点

资源修改

  1. 使用资源编辑器(如ResHacker)提取和修改资源
  2. 使用16进制编辑器直接修改PE文件中的资源数据

总结

本教程详细介绍了Win32编程的核心知识点,包括字符编码处理、窗口创建与消息机制、控件编程、对话框设计、资源文件处理等内容。掌握这些知识是进行Windows平台底层开发的基础。通过实际代码示例和调试技巧,可以帮助开发者深入理解Win32编程的精髓。

Win32编程深入教学文档 0x00 前言 Win32 API是Windows平台下最基本的API接口,学习Win32编程是理解Windows操作系统底层机制的重要途径。本教程将从底层角度深入讲解Win32编程的关键知识点。 0x01 字符编码基础 多字节字符(窄字节字符) ASCII码表:0111 1111 (0-127) 扩展ASCII码表:1111 1111 (0-255) 汉字表示:两个扩展ASCII码(2字节)拼成一个汉字 问题:可能与韩文、日文编码冲突导致乱码 Unicode字符 统一编码标准,每个符号占2字节 解决多字节字符的兼容性问题 C语言中的字符处理 字符串操作函数对比 | 多字节字符函数 | 宽字符函数 | 功能描述 | |---------------|-----------|---------| | strlen | wcslen | 获取字符串长度 | | strcpy | wcscpy | 字符串复制 | | strcat | wcscat | 字符串拼接 | | strcmp | wcscmp | 字符串比较 | | strstr | wcsstr | 字符串查找 | 0x02 Win32 API字符处理 Win32字符类型 Win32字符串指针类型 字符赋值示例 API的多版本支持 Win32 API为需要字符串参数的函数提供两个版本和一个宏: 0x03 Win32程序结构 WinMain函数 调试输出 错误处理 0x04 窗口与消息机制 消息结构体MSG 消息处理流程 用户输入触发事件 事件被封装为MSG结构 消息进入系统队列 分流到应用程序消息队列 通过消息循环从队列取出消息 判断消息类型并处理 窗口创建与消息循环 窗口回调函数 0x05 常见窗口消息解析 WM_ CREATE 触发时机:窗口创建时 wParam:未使用 lParam:指向CREATESTRUCT结构体指针 WM_ MOVE 触发时机:窗口移动时 wParam:未使用 lParam: LOWORD: x坐标 HIWORD: y坐标 WM_ SIZE 触发时机:窗口大小改变时 wParam:调整类型 SIZE_ MAXIMIZED: 最大化 SIZE_ MINIMIZED: 最小化 SIZE_ RESTORED: 恢复 lParam: LOWORD: 新宽度 HIWORD: 新高度 WM_ COMMAND 触发时机:子窗口(如按钮)事件发生时 wParam: LOWORD: 控件ID HIWORD: 通知代码 lParam:控件句柄 0x06 子窗口控件 创建按钮 按钮类型 BS_ PUSHBUTTON: 普通按钮 BS_ CHECKBOX: 复选框 BS_ RADIOBUTTON: 单选按钮 子窗口消息处理 0x07 对话框编程 对话框资源创建 在资源文件中添加Dialog资源 设置对话框属性(ID,标题等) 添加控件(按钮,文本框等) 对话框创建与显示 获取对话框控件内容 0x08 资源文件处理 图标资源 资源表结构 PE文件中的资源表是最复杂的结构之一,位于数据目录的第三项(下标2)。 资源目录结构 资源目录项结构 资源类型 Windows预定义了16种资源类型: 光标 位图 图标 菜单 对话框 字符串 字体目录 字体 加速键 非格式化资源 消息列表 光标组 图标组 版本信息 0x09 调试技巧 消息断点 在调试器中设置条件断点 条件表达式示例: [esp+8] == WM_COMMAND 命令消息 [esp+8] == WM_LBUTTONDOWN 鼠标左键按下 回调函数定位 通过RegisterClass找到窗口类结构 从窗口类结构中获取lpfnWndProc回调函数地址 在回调函数地址设置断点 资源修改 使用资源编辑器(如ResHacker)提取和修改资源 使用16进制编辑器直接修改PE文件中的资源数据 总结 本教程详细介绍了Win32编程的核心知识点,包括字符编码处理、窗口创建与消息机制、控件编程、对话框设计、资源文件处理等内容。掌握这些知识是进行Windows平台底层开发的基础。通过实际代码示例和调试技巧,可以帮助开发者深入理解Win32编程的精髓。