Win32编程

1、消息机制

1、课程目标

  1. 理解Windows消息驱动的编程模型
  2. 掌握消息循环的工作原理
  3. 学会窗口过程的编写
  4. 掌握常用消息的处理方法

2、名词解释

术语 英文 说明
消息 Message 系统与应用程序通信的基本单元
消息队列 Message Queue 存储消息的先进先出队列
窗口过程 Window Procedure 处理窗口消息的回调函数
消息循环 Message Loop 获取和分发消息的循环

3、使用工具

工具 用途
Spy++ 查看窗口消息
Visual Studio Win32项目开发

4、技术原理

4.1、消息结构

typedef struct tagMSG {
    HWND   hwnd;      // 目标窗口
    UINT   message;   // 消息类型
    WPARAM wParam;    // 参数
    LPARAM lParam;    // 参数
    DWORD  time;      // 时间戳
    POINT  pt;        // 鼠标位置
} MSG;

4.2、常用消息

消息 说明
WM_CREATE 窗口创建
WM_DESTROY 窗口销毁
WM_PAINT 窗口重绘
WM_COMMAND 菜单/控件命令
WM_KEYDOWN 键盘按下
WM_LBUTTONDOWN 鼠标左键按下

5、代码实现

5.1、示例1:基本Win32程序框架

#include <windows.h>

// 窗口过程
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_CREATE:
        // 窗口创建时初始化
        return 0;
        
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 10, 10, TEXT("Hello, Win32!"), 13);
        EndPaint(hWnd, &ps);
        return 0;
    }
    
    case WM_DESTROY:
        PostQuitMessage(0);  // 发送WM_QUIT
        return 0;
    }
    
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow) {
    // 1. 注册窗口类
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = TEXT("MyWindowClass");
    RegisterClassEx(&wc);
    
    // 2. 创建窗口
    HWND hWnd = CreateWindowEx(0, TEXT("MyWindowClass"), TEXT("My Window"),
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
        NULL, NULL, hInstance, NULL);
    
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    
    // 3. 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);  // 转换键盘消息
        DispatchMessage(&msg);   // 分发给窗口过程
    }
    
    return (int)msg.wParam;
}

5.2、示例2:消息处理

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static int clickCount = 0;
    
    switch (msg) {
    case WM_LBUTTONDOWN: {
        // 鼠标左键点击
        int x = LOWORD(lParam);
        int y = HIWORD(lParam);
        TCHAR buf[64];
        wsprintf(buf, TEXT("Click at (%d, %d), Count: %d"), x, y, ++clickCount);
        SetWindowText(hWnd, buf);
        return 0;
    }
    
    case WM_KEYDOWN:
        // 键盘按下
        if (wParam == VK_ESCAPE) {
            DestroyWindow(hWnd);
        }
        return 0;
        
    case WM_COMMAND:
        // 菜单/按钮命令
        switch (LOWORD(wParam)) {
        case 1001:  // 按钮ID
            MessageBox(hWnd, TEXT("Button clicked!"), TEXT("Info"), MB_OK);
            break;
        }
        return 0;
    }
    
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

5.3、示例3:发送消息

#include <windows.h>

void MessageDemo() {
    HWND hWnd = FindWindow(NULL, TEXT("Notepad"));
    
    if (hWnd) {
        // SendMessage - 同步,等待处理完成
        SendMessage(hWnd, WM_CLOSE, 0, 0);
        
        // PostMessage - 异步,立即返回
        PostMessage(hWnd, WM_KEYDOWN, VK_RETURN, 0);
    }
    
    // 发送自定义消息
    #define WM_MYAPP (WM_USER + 100)
    PostMessage(hWnd, WM_MYAPP, 123, 456);
}

5.4、示例4:子类化窗口

// 原始窗口过程
WNDPROC g_OldWndProc = NULL;

LRESULT CALLBACK SubclassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    // 拦截并处理特定消息
    if (msg == WM_KEYDOWN && wParam == VK_TAB) {
        MessageBox(hWnd, TEXT("Tab pressed!"), TEXT("Hook"), MB_OK);
        return 0;  // 吃掉消息
    }
    
    // 调用原始窗口过程
    return CallWindowProc(g_OldWndProc, hWnd, msg, wParam, lParam);
}

void SubclassWindow(HWND hWnd) {
    g_OldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, 
                                              (LONG_PTR)SubclassProc);
}

6、课后作业

  1. 基础练习:创建一个简单的Win32窗口程序
  2. 消息处理:处理键盘和鼠标消息
  3. 消息发送:实现跨进程发送消息
  4. 窗口子类化:拦截并修改已有窗口的行为