1、课程目标
- 理解Windows消息驱动的编程模型
- 掌握消息循环的工作原理
- 学会窗口过程的编写
- 掌握常用消息的处理方法
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、课后作业
- 基础练习:创建一个简单的Win32窗口程序
- 消息处理:处理键盘和鼠标消息
- 消息发送:实现跨进程发送消息
- 窗口子类化:拦截并修改已有窗口的行为