Win32编程

1、消息机制

1、课程目标

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

2、名词解释

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

3、使用工具

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

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. 窗口子类化:拦截并修改已有窗口的行为