011-Win32 编程基础入门及实例

Win32 编程基础入门及实例

Win32 API(应用程序编程接口)是用于开发 Windows 操作系统应用程序的核心接口集。尽管现代 Windows 开发已经有了许多更高级的框架和库,如 .NET Framework、WPF、UWP 等,但 Win32 API 仍然是这些技术的基础,并且在需要高性能、低级别系统访问的场景中依然不可替代。本文将介绍 Win32 编程的基础知识,并通过实例展示如何开发 Win32 应用程序。

1. Win32 编程概述

1.1 什么是 Win32 API

Win32 API 是 Windows 操作系统提供的一组 C 语言函数和数据结构,用于开发 Windows 应用程序。它包括以下几个主要部分:

用户界面:窗口管理、消息处理、对话框、控件等
图形设备接口 (GDI):绘图、颜色管理、字体等
系统服务:文件操作、内存管理、进程和线程等
网络:Socket 编程、网络协议等
安全:访问控制、加密等

1.2 Win32 应用程序的基本结构

一个典型的 Win32 应用程序通常包含以下几个核心部分:

WinMain 函数:应用程序入口点
窗口过程 (Window Procedure):处理窗口消息
消息循环:从消息队列获取和分派消息
资源:图标、菜单、对话框等

1.3 开发环境搭建

开发 Win32 应用程序需要以下工具:

Visual Studio:微软官方 IDE,提供完整的 Win32 开发支持
Windows SDK:包含头文件、库文件、工具和文档

2. 创建第一个 Win32 应用程序

2.1 最小化的 Win32 程序

我们先来看一个简单的 Win32 程序框架:

cpp

#include <windows.h>

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            
            // 在窗口中心绘制文本
            RECT rect;
            GetClientRect(hwnd, &rect);
            DrawText(hdc, L"Hello, Win32!", -1, &rect, 
                DT_SINGLELINE | DT_CENTER | DT_VCENTER);
            
            EndPaint(hwnd, &ps);
        }
        return 0;
    }
    
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// 程序入口点
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // 注册窗口类
    const wchar_t CLASS_NAME[] = L"Sample Window Class";
    
    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    
    RegisterClass(&wc);
    
    // 创建窗口
    HWND hwnd = CreateWindowEx(
        0,                          // 扩展样式
        CLASS_NAME,                 // 窗口类名
        L"My First Win32 Window",   // 窗口标题
        WS_OVERLAPPEDWINDOW,        // 窗口样式
        
        // 位置和大小
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        
        NULL,       // 父窗口    
        NULL,       // 菜单
        hInstance,  // 实例句柄
        NULL        // 额外参数
    );
    
    if (hwnd == NULL) {
        return 0;
    }
    
    // 显示窗口
    ShowWindow(hwnd, nCmdShow);
    
    // 消息循环
    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    return (int)msg.wParam;
}

2.2 关键部分解析

2.2.1 窗口过程 (WindowProc)

窗口过程是一个回调函数,Windows 系统通过它向应用程序发送消息。每个窗口都有一个关联的窗口过程,用于处理该窗口接收到的所有消息。

cpp

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

参数说明:

hwnd:窗口句柄,标识消息的目标窗口
uMsg:消息标识符(如 WM_PAINT、WM_DESTROY 等)
wParamlParam:消息特定的参数

2.2.2 WinMain 函数

WinMain 是 Windows 应用程序的入口点,相当于控制台程序中的 main 函数。

cpp

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

参数说明:

hInstance:当前应用程序实例的句柄
hPrevInstance:在现代 Windows 中总是 NULL(历史遗留参数)
lpCmdLine:命令行参数
nCmdShow:窗口的显示方式(最大化、最小化等)

2.2.3 消息循环

消息循环负责从系统消息队列获取消息并分派给适当的窗口过程:

cpp

MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

每个 Win32 应用程序必须有这样一个消息循环,用于处理系统发送的输入、绘图等消息。

3. Win32 应用程序的基础组件

3.1 窗口和消息处理

Windows 操作系统是以消息驱动的,各种用户交互和系统事件都会生成消息,发送到相应的窗口过程进行处理。

3.1.1 常见窗口消息

以下是一些常见的窗口消息:

cpp

case WM_CREATE:    // 窗口创建
    // 初始化窗口
    return 0;
    
case WM_CLOSE:     // 窗口关闭
    // 询问用户是否确定关闭
    if (MessageBox(hwnd, L"确定要关闭窗口吗?", L"提示", MB_OKCANCEL) == IDOK) {
        DestroyWindow(hwnd);
    }
    return 0;
    
case WM_DESTROY:   // 窗口销毁
    PostQuitMessage(0);
    return 0;
    
case WM_PAINT:     // 窗口需要重绘
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        // 绘制代码
        EndPaint(hwnd, &ps);
    }
    return 0;
    
case WM_COMMAND:   // 菜单、按钮等命令
    // 菜单ID通过LOWORD(wParam)获取
    switch (LOWORD(wParam)) {
    case ID_FILE_EXIT:
        DestroyWindow(hwnd);
        return 0;
    }
    break;
    
case WM_SIZE:      // 窗口大小改变
    // 新窗口宽度和高度
    int width = LOWORD(lParam);
    int height = HIWORD(lParam);
    // 处理窗口大小改变
    return 0;
3.1.2 子类化控件

子类化是一种重写控件默认行为的技术,通过替换控件的窗口过程来实现:

cpp

// 原始按钮窗口过程
WNDPROC g_OldButtonProc = NULL;

// 新的按钮窗口过程
LRESULT CALLBACK NewButtonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_LBUTTONDOWN:
        // 自定义处理左键按下
        MessageBox(NULL, L"按钮被点击!", L"子类化示例", MB_OK);
        break;
    }
    
    // 调用原始窗口过程处理其他消息
    return CallWindowProc(g_OldButtonProc, hwnd, uMsg, wParam, lParam);
}

// 在创建按钮后子类化
HWND hwndButton = CreateWindow(
    L"BUTTON", L"点击我", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    50, 50, 100, 30, hwndParent, (HMENU)ID_BUTTON, hInstance, NULL
);

// 保存原始窗口过程并设置新的窗口过程
g_OldButtonProc = (WNDPROC)SetWindowLongPtr(hwndButton, GWLP_WNDPROC, (LONG_PTR)NewButtonProc);

3.2 GDI 绘图基础

GDI (Graphics Device Interface) 是 Windows 中的绘图子系统,提供了在窗口或设备上绘制图形和文本的功能。

3.2.1 基本绘图

cpp

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    
    // 设置绘图模式和颜色
    SetBkMode(hdc, TRANSPARENT);
    SetTextColor(hdc, RGB(0, 0, 255));  // 蓝色文本
    
    // 创建画笔和画刷
    HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));  // 红色画笔
    HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0));    // 绿色画刷
    
    // 保存原画笔和画刷
    HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
    HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
    
    // 绘制矩形
    Rectangle(hdc, 50, 50, 200, 150);
    
    // 绘制椭圆
    Ellipse(hdc, 250, 50, 400, 150);
    
    // 绘制线条
    MoveToEx(hdc, 50, 200, NULL);
    LineTo(hdc, 400, 200);
    
    // 恢复原画笔和画刷
    SelectObject(hdc, hOldPen);
    SelectObject(hdc, hOldBrush);
    
    // 释放资源
    DeleteObject(hPen);
    DeleteObject(hBrush);
    
    // 绘制文本
    RECT rect = {50, 250, 400, 300};
    DrawText(hdc, L"Hello, GDI!", -1, &rect, DT_CENTER);
    
    EndPaint(hwnd, &ps);
    return 0;
}
3.2.2 双缓冲绘图

为了避免绘图时闪烁,可以使用双缓冲技术:

cpp

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    
    // 获取客户区尺寸
    RECT clientRect;
    GetClientRect(hwnd, &clientRect);
    int width = clientRect.right - clientRect.left;
    int height = clientRect.bottom - clientRect.top;
    
    // 创建内存兼容DC和位图
    HDC memDC = CreateCompatibleDC(hdc);
    HBITMAP memBitmap = CreateCompatibleBitmap(hdc, width, height);
    HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);
    
    // 在内存DC中绘图
    HBRUSH whiteBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
    FillRect(memDC, &clientRect, whiteBrush);
    
    // ... 其他绘图代码 ...
    
    // 将内存DC的内容复制到窗口DC
    BitBlt(hdc, 0, 0, width, height, memDC, 0, 0, SRCCOPY);
    
    // 清理资源
    SelectObject(memDC, oldBitmap);
    DeleteObject(memBitmap);
    DeleteDC(memDC);
    
    EndPaint(hwnd, &ps);
    return 0;
}

3.3 常用控件

Windows 提供了许多预定义的控件,如按钮、编辑框、列表框等。

3.3.1 创建和使用控件

cpp

// 创建按钮
HWND hwndButton = CreateWindow(
    L"BUTTON",            // 预定义的控件类名
    L"点击我",            // 按钮文本
    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,  // 样式
    50, 50,               // 位置 (x,y)
    100, 30,              // 大小 (width,height)
    hwnd,                 // 父窗口句柄
    (HMENU)ID_BUTTON,     // 控件ID
    hInstance,            // 应用程序实例句柄
    NULL                  // 附加数据
);

// 创建静态文本
HWND hwndStatic = CreateWindow(
    L"STATIC",
    L"这是静态文本",
    WS_CHILD | WS_VISIBLE | SS_LEFT,
    50, 100, 200, 20,
    hwnd, (HMENU)ID_STATIC, hInstance, NULL
);

// 创建编辑框
HWND hwndEdit = CreateWindow(
    L"EDIT",
    L"",
    WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
    50, 150, 200, 25,
    hwnd, (HMENU)ID_EDIT, hInstance, NULL
);

// 创建复选框
HWND hwndCheckbox = CreateWindow(
    L"BUTTON",
    L"复选框",
    WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
    50, 200, 100, 30,
    hwnd, (HMENU)ID_CHECKBOX, hInstance, NULL
);

// 设置控件文本
SetWindowText(hwndStatic, L"新的静态文本");

// 获取控件文本
wchar_t buffer[256];
GetWindowText(hwndEdit, buffer, 256);

// 发送消息到控件
SendMessage(hwndCheckbox, BM_SETCHECK, BST_CHECKED, 0);
3.3.2 控件消息处理

cpp

case WM_COMMAND:
    switch (LOWORD(wParam)) {
    case ID_BUTTON:
        if (HIWORD(wParam) == BN_CLICKED) {
            // 按钮被点击
            MessageBox(hwnd, L"按钮被点击了!", L"提示", MB_OK);
        }
        break;
        
    case ID_EDIT:
        if (HIWORD(wParam) == EN_CHANGE) {
            // 编辑框内容变化
            wchar_t buffer[256];
            GetWindowText((HWND)lParam, buffer, 256);
            // 处理编辑框内容
        }
        break;
        
    case ID_CHECKBOX:
        if (HIWORD(wParam) == BN_CLICKED) {
            // 复选框状态改变
            BOOL checked = (SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED);
            // 处理复选框状态
        }
        break;
    }
    break;

3.4 菜单和对话框

3.4.1 创建和使用菜单

cpp

// 创建菜单
HMENU hMenu = CreateMenu();
HMENU hSubMenu = CreatePopupMenu();

// 添加菜单项
AppendMenu(hSubMenu, MF_STRING, ID_FILE_NEW, L"新建(&N)");
AppendMenu(hSubMenu, MF_STRING, ID_FILE_OPEN, L"打开(&O)");
AppendMenu(hSubMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, L"退出(&X)");

// 添加子菜单到主菜单
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, L"文件(&F)");

// 设置窗口菜单
SetMenu(hwnd, hMenu);

// 处理菜单命令
case WM_COMMAND:
    switch (LOWORD(wParam)) {
    case ID_FILE_NEW:
        // 处理"新建"菜单
        break;
    case ID_FILE_OPEN:
        // 显示打开文件对话框
        {
            wchar_t filename[MAX_PATH] = {0};
            
            OPENFILENAME ofn = {0};
            ofn.lStructSize = sizeof(ofn);
            ofn.hwndOwner = hwnd;
            ofn.lpstrFilter = L"文本文件(*.txt)*.txt所有文件(*.*)*.*";
            ofn.lpstrFile = filename;
            ofn.nMaxFile = MAX_PATH;
            ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
            
            if (GetOpenFileName(&ofn)) {
                // 处理选中的文件
            }
        }
        break;
    case ID_FILE_EXIT:
        DestroyWindow(hwnd);
        break;
    }
    break;
3.4.2 创建模态对话框

首先在资源脚本 (.rc) 文件中定义对话框模板:

apache

// 在 Resource.rc 文件中
IDD_DIALOG1 DIALOGEX 0, 0, 309, 176
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "示例对话框"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,198,155,50,14
    PUSHBUTTON      "取消",IDCANCEL,252,155,50,14
    EDITTEXT        IDC_EDIT1,84,23,140,14,ES_AUTOHSCROLL
    LTEXT           "请输入姓名:",IDC_STATIC,20,25,57,8
END

然后创建对话框过程函数:

cpp

// 对话框过程函数
INT_PTR 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:
            {
                // 获取编辑框文本
                wchar_t buffer[256];
                GetDlgItemText(hwndDlg, IDC_EDIT1, buffer, 256);
                
                // 处理输入的文本
                MessageBox(hwndDlg, buffer, L"你输入的是", MB_OK);
                
                // 关闭对话框并返回IDOK
                EndDialog(hwndDlg, IDOK);
                return TRUE;
            }
            
        case IDCANCEL:
            // 关闭对话框并返回IDCANCEL
            EndDialog(hwndDlg, IDCANCEL);
            return TRUE;
        }
        break;
    }
    
    return FALSE;
}

// 在需要显示对话框的地方调用
INT_PTR result = DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, DialogProc);
if (result == IDOK) {
    // 用户点击了确定
} else if (result == IDCANCEL) {
    // 用户点击了取消
}
3.4.3 创建非模态对话框

cpp

HWND g_hwndDialog = NULL;

// 显示非模态对话框
void ShowModelessDialog(HWND hwndParent) {
    if (g_hwndDialog == NULL) {
        g_hwndDialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hwndParent, DialogProc);
        ShowWindow(g_hwndDialog, SW_SHOW);
    } else {
        // 对话框已经存在,激活它
        SetForegroundWindow(g_hwndDialog);
    }
}

// 非模态对话框过程函数
INT_PTR 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:
            {
                wchar_t buffer[256];
                GetDlgItemText(hwndDlg, IDC_EDIT1, buffer, 256);
                MessageBox(hwndDlg, buffer, L"你输入的是", MB_OK);
                
                // 关闭对话框,但不调用EndDialog
                DestroyWindow(hwndDlg);
                return TRUE;
            }
            
        case IDCANCEL:
            DestroyWindow(hwndDlg);
            return TRUE;
        }
        break;
        
    case WM_DESTROY:
        g_hwndDialog = NULL;  // 重置对话框句柄
        return TRUE;
    }
    
    return FALSE;
}

4. 进阶主题

4.1 多线程编程

Win32 API 提供了创建和管理线程的函数:

cpp

// 线程函数
DWORD WINAPI ThreadProc(LPVOID lpParameter) {
    // 线程代码
    
    for (int i = 0; i < 10; i++) {
        // 处理数据
        Sleep(1000);  // 休眠1秒
    }
    
    return 0;  // 线程返回值
}

// 创建线程
DWORD threadId;
HANDLE hThread = CreateThread(
    NULL,               // 默认安全属性
    0,                  // 默认堆栈大小
    ThreadProc,         // 线程函数
    NULL,               // 传递给线程函数的参数
    0,                  // 立即运行线程
    &threadId           // 接收线程ID
);

if (hThread == NULL) {
    // 创建线程失败
}

// 等待线程结束
WaitForSingleObject(hThread, INFINITE);

// 获取线程返回值
DWORD exitCode;
GetExitCodeThread(hThread, &exitCode);

// 关闭线程句柄
CloseHandle(hThread);
4.1.1 线程同步

cpp

// 创建互斥体
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);

// 在线程中使用互斥体
DWORD WINAPI ThreadProc(LPVOID lpParameter) {
    for (int i = 0; i < 10; i++) {
        // 等待获取互斥体
        WaitForSingleObject(hMutex, INFINITE);
        
        // 临界区代码(访问共享资源)
        
        // 释放互斥体
        ReleaseMutex(hMutex);
        
        Sleep(100);
    }
    return 0;
}

// 其他同步对象
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);  // 手动重置事件
HANDLE hSemaphore = CreateSemaphore(NULL, 2, 2, NULL);  // 信号量(初始计数2,最大计数2)
CRITICAL_SECTION criticalSection;  // 临界区

// 初始化临界区
InitializeCriticalSection(&criticalSection);

// 使用临界区
EnterCriticalSection(&criticalSection);
// 访问共享资源
LeaveCriticalSection(&criticalSection);

// 删除临界区
DeleteCriticalSection(&criticalSection);

4.2 文件和注册表操作

4.2.1 文件操作

cpp

// 创建或打开文件
HANDLE hFile = CreateFile(
    L"example.txt",                   // 文件名
    GENERIC_READ | GENERIC_WRITE,     // 访问模式
    0,                                // 共享模式(0表示独占)
    NULL,                             // 安全属性
    CREATE_ALWAYS,                    // 创建处理
    FILE_ATTRIBUTE_NORMAL,            // 文件属性
    NULL                              // 模板文件句柄
);

if (hFile == INVALID_HANDLE_VALUE) {
    // 打开文件失败
}

// 写入文件
const char* data = "Hello, Win32!";
DWORD bytesWritten;
WriteFile(
    hFile,              // 文件句柄
    data,               // 要写入的数据缓冲区
    strlen(data),       // 要写入的字节数
    &bytesWritten,      // 接收实际写入的字节数
    NULL                // 重叠结构(用于异步I/O)
);

// 移动文件指针到开头
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);

// 读取文件
char buffer[100];
DWORD bytesRead;
ReadFile(
    hFile,              // 文件句柄
    buffer,             // 接收数据的缓冲区
    sizeof(buffer),     // 要读取的字节数
    &bytesRead,         // 接收实际读取的字节数
    NULL                // 重叠结构
);

// 确保字符串以null结尾
buffer[bytesRead] = '';

// 关闭文件句柄
CloseHandle(hFile);
4.2.2 注册表操作

cpp

// 打开注册表键
HKEY hKey;
LONG result = RegOpenKeyEx(
    HKEY_CURRENT_USER,           // 注册表根键
    L"Software\MyApp",          // 子键路径
    0,                          // 保留,必须为0
    KEY_READ | KEY_WRITE,       // 访问权限
    &hKey                       // 接收键句柄
);

if (result != ERROR_SUCCESS) {
    // 打开失败,尝试创建
    result = RegCreateKeyEx(
        HKEY_CURRENT_USER,
        L"Software\MyApp",
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        KEY_READ | KEY_WRITE,
        NULL,
        &hKey,
        NULL
    );
    
    if (result != ERROR_SUCCESS) {
        // 创建失败
    }
}

// 写入字符串值
result = RegSetValueEx(
    hKey,                        // 键句柄
    L"StringValue",              // 值名称
    0,                          // 保留,必须为0
    REG_SZ,                     // 数据类型
    (BYTE*)L"Hello World",      // 数据
    sizeof(L"Hello World")      // 数据大小(包括结尾null)
);

// 写入DWORD值
DWORD dwValue = 12345;
result = RegSetValueEx(
    hKey,
    L"DWordValue",
    0,
    REG_DWORD,
    (BYTE*)&dwValue,
    sizeof(dwValue)
);

// 读取字符串值
wchar_t szBuffer[256];
DWORD dwBufferSize = sizeof(szBuffer);
DWORD dwType;
result = RegQueryValueEx(
    hKey,
    L"StringValue",
    NULL,
    &dwType,
    (BYTE*)szBuffer,
    &dwBufferSize
);

if (result == ERROR_SUCCESS && dwType == REG_SZ) {
    // 使用szBuffer
}

// 读取DWORD值
DWORD dwReadValue;
DWORD dwValueSize = sizeof(dwReadValue);
result = RegQueryValueEx(
    hKey,
    L"DWordValue",
    NULL,
    &dwType,
    (BYTE*)&dwReadValue,
    &dwValueSize
);

if (result == ERROR_SUCCESS && dwType == REG_DWORD) {
    // 使用dwReadValue
}

// 删除值
RegDeleteValue(hKey, L"StringValue");

// 关闭注册表键
RegCloseKey(hKey);

// 删除整个键(包括所有子键和值)
RegDeleteKey(HKEY_CURRENT_USER, L"Software\MyApp");

4.3 资源使用

4.3.1 图标和光标

cpp

// 加载图标
HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON));
HICON hSmallIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_MYICON), 
                                   IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);

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

// 加载光标
HCURSOR hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYCURSOR));

// 设置窗口类光标
wc.hCursor = hCursor;

// 动态改变光标
SetCursor(hCursor);
4.3.2 位图操作

cpp

// 加载位图
HBITMAP hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_MYBITMAP));

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    
    // 创建内存DC
    HDC memDC = CreateCompatibleDC(hdc);
    
    // 选择位图到内存DC
    HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, hBitmap);
    
    // 获取位图信息
    BITMAP bm;
    GetObject(hBitmap, sizeof(BITMAP), &bm);
    
    // 绘制位图
    BitBlt(hdc, 10, 10, bm.bmWidth, bm.bmHeight, memDC, 0, 0, SRCCOPY);
    
    // 或者使用透明绘制
    TransparentBlt(hdc, 10, 10, bm.bmWidth, bm.bmHeight, 
                memDC, 0, 0, bm.bmWidth, bm.bmHeight, RGB(255, 0, 255));
    
    // 恢复原位图并清理资源
    SelectObject(memDC, oldBitmap);
    DeleteDC(memDC);
    
    EndPaint(hwnd, &ps);
    return 0;
}
4.3.3 字符串资源

cpp

// 在资源文件中定义字符串
// STRINGTABLE
// BEGIN
//     IDS_APP_TITLE    "My Application"
//     IDS_HELLO        "Hello, World!"
// END

// 加载字符串资源
wchar_t buffer[256];
LoadString(hInstance, IDS_HELLO, buffer, 256);

5. 实例项目:简单的绘图应用

下面我们将创建一个简单的绘图应用,演示 Win32 编程的核心概念。

5.1 完整源代码

cpp

#include <windows.h>
#include <vector>

// 绘图点
struct Point {
    int x;
    int y;
};

// 绘图线条
struct Line {
    std::vector<Point> points;
    COLORREF color;
    int thickness;
};

// 全局变量
std::vector<Line> g_lines;  // 所有绘制的线条
bool g_isDrawing = false;   // 是否正在绘制
COLORREF g_currentColor = RGB(0, 0, 0);  // 当前颜色
int g_currentThickness = 2;  // 当前线条粗细

// 窗口过程
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

// 绘制所有线条
void DrawLines(HDC hdc) {
    for (const auto& line : g_lines) {
        if (line.points.empty()) continue;
        
        // 创建画笔
        HPEN hPen = CreatePen(PS_SOLID, line.thickness, line.color);
        HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
        
        // 移动到起点
        MoveToEx(hdc, line.points[0].x, line.points[0].y, NULL);
        
        // 连接所有点
        for (size_t i = 1; i < line.points.size(); i++) {
            LineTo(hdc, line.points[i].x, line.points[i].y);
        }
        
        // 清理资源
        SelectObject(hdc, hOldPen);
        DeleteObject(hPen);
    }
}

// WinMain函数 - 应用程序入口点
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // 注册窗口类
    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"DrawingApp";
    wc.hCursor = LoadCursor(NULL, IDC_CROSS);  // 使用十字光标
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    
    RegisterClass(&wc);
    
    // 创建菜单
    HMENU hMenu = CreateMenu();
    HMENU hColorMenu = CreatePopupMenu();
    
    AppendMenu(hColorMenu, MF_STRING, 1001, L"黑色");
    AppendMenu(hColorMenu, MF_STRING, 1002, L"红色");
    AppendMenu(hColorMenu, MF_STRING, 1003, L"绿色");
    AppendMenu(hColorMenu, MF_STRING, 1004, L"蓝色");
    
    AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hColorMenu, L"颜色");
    AppendMenu(hMenu, MF_STRING, 1005, L"清除画布");
    
    // 创建窗口
    HWND hwnd = CreateWindowEx(
        0,
        L"DrawingApp",
        L"简单绘图应用",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, hMenu, hInstance, NULL
    );
    
    if (hwnd == NULL) {
        return 0;
    }
    
    // 显示窗口
    ShowWindow(hwnd, nCmdShow);
    
    // 消息循环
    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    return (int)msg.wParam;
}

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            switch (wmId) {
            case 1001:  // 黑色
                g_currentColor = RGB(0, 0, 0);
                break;
            case 1002:  // 红色
                g_currentColor = RGB(255, 0, 0);
                break;
            case 1003:  // 绿色
                g_currentColor = RGB(0, 255, 0);
                break;
            case 1004:  // 蓝色
                g_currentColor = RGB(0, 0, 255);
                break;
            case 1005:  // 清除画布
                g_lines.clear();
                InvalidateRect(hwnd, NULL, TRUE);
                break;
            }
        }
        break;
        
    case WM_LBUTTONDOWN:
        {
            // 开始绘制
            g_isDrawing = true;
            
            // 创建新线条
            Line newLine;
            newLine.color = g_currentColor;
            newLine.thickness = g_currentThickness;
            
            // 添加起始点
            Point pt;
            pt.x = GET_X_LPARAM(lParam);
            pt.y = GET_Y_LPARAM(lParam);
            newLine.points.push_back(pt);
            
            g_lines.push_back(newLine);
        }
        break;
        
    case WM_MOUSEMOVE:
        if (g_isDrawing && !g_lines.empty()) {
            // 添加新点
            Point pt;
            pt.x = GET_X_LPARAM(lParam);
            pt.y = GET_Y_LPARAM(lParam);
            g_lines.back().points.push_back(pt);
            
            // 仅重绘需要更新的区域
            RECT updateRect;
            auto& lastPt = g_lines.back().points[g_lines.back().points.size() - 2];
            
            // 计算重绘矩形,考虑线条粗细
            int offset = g_currentThickness + 2;
            updateRect.left = min(lastPt.x, pt.x) - offset;
            updateRect.top = min(lastPt.y, pt.y) - offset;
            updateRect.right = max(lastPt.x, pt.x) + offset;
            updateRect.bottom = max(lastPt.y, pt.y) + offset;
            
            InvalidateRect(hwnd, &updateRect, FALSE);
        }
        break;
        
    case WM_LBUTTONUP:
        // 结束绘制
        g_isDrawing = false;
        break;
        
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            
            // 使用双缓冲绘制
            RECT clientRect;
            GetClientRect(hwnd, &clientRect);
            int width = clientRect.right - clientRect.left;
            int height = clientRect.bottom - clientRect.top;
            
            HDC memDC = CreateCompatibleDC(hdc);
            HBITMAP memBitmap = CreateCompatibleBitmap(hdc, width, height);
            HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);
            
            // 清除背景
            HBRUSH whiteBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
            FillRect(memDC, &clientRect, whiteBrush);
            
            // 绘制所有线条
            DrawLines(memDC);
            
            // 复制到屏幕
            BitBlt(hdc, 0, 0, width, height, memDC, 0, 0, SRCCOPY);
            
            // 清理资源
            SelectObject(memDC, oldBitmap);
            DeleteObject(memBitmap);
            DeleteDC(memDC);
            
            EndPaint(hwnd, &ps);
        }
        break;
        
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
        
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    
    return 0;
}

5.2 主要功能

此简单绘图应用程序实现了以下功能:

用鼠标绘制自由曲线
选择绘图颜色(黑、红、绿、蓝)
清除画布
使用双缓冲消除闪烁

5.3 功能增强

可以考虑添加以下功能进一步增强应用程序:

保存和打开绘图文件
添加更多绘图工具(直线、矩形、椭圆等)
调整线条粗细
添加文本输入工具
实现撤销/重做功能

6. 总结与最佳实践

6.1 Win32 编程最佳实践

资源管理

总是配对创建和释放资源(GDI对象、句柄等)
使用 RAII(资源获取即初始化)模式管理资源生命周期

UI 响应性

避免在主线程中执行耗时操作
使用多线程处理复杂计算和I/O操作

绘图优化

使用双缓冲避免闪烁
仅重绘需要更新的区域

代码组织

将消息处理分解为小函数
使用面向对象的方法组织代码(即使使用C语言)

错误处理

检查API调用的返回值
使用 GetLastError() 获取详细错误信息

国际化

使用 Unicode 字符集(wchar_t)
从资源加载字符串,便于本地化

6.2 进一步学习资源

书籍

“Programming Windows” by Charles Petzold
“Windows Via C/C++” by Jeffrey Richter

在线资源

Microsoft Win32 API 文档
MSDN 示例代码库
Windows 开发者中心

开源项目

ReactOS(开源Windows实现)
Wine(Windows API兼容层)

6.3 现代替代方案

虽然 Win32 API 仍然是 Windows 开发的基础,但对于许多应用程序类型,以下现代框架可能是更好的选择:

WPF (Windows Presentation Foundation)

基于 .NET 的现代 UI 框架
使用 XAML 描述界面
强大的数据绑定和样式系统

UWP (Universal Windows Platform)

跨 Windows 设备的现代应用平台
更安全的沙盒环境
响应式设计和触摸优化

Win32 的 C++ 包装器

C++/WinRT
ATL (Active Template Library)
MFC (Microsoft Foundation Classes)

跨平台框架

Qt
Electron
Flutter for Windows

Win32 API 的优势在于直接访问底层 Windows 功能、最佳性能和最小依赖性,适合系统级编程和对性能有极高要求的应用程序。


Win32 编程虽然看起来复杂,但掌握其核心概念后,可以为您的 Windows 开发技能打下坚实的基础。无论您选择使用哪种现代框架,理解底层的 Win32 机制都会对深入理解 Windows 平台的工作原理大有帮助。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容