目录
-
第一部分:Windows API 简介
(图片来源网络,侵删)- 什么是 Windows API?
- 为什么学习 Windows API?
- 开发环境准备
-
第二部分:第一个 Windows 程序 - "Hello, Windows!"
- 程序结构解析:
WinMain,WNDCLASS,RegisterClass,CreateWindow,ShowWindow,UpdateWindow,Message Loop - 代码详解
- 编译与运行
- 程序结构解析:
-
第三部分:深入窗口过程
WndProc函数详解- 消息类型:
WM_PAINT,WM_DESTROY,WM_CLOSE - 处理用户输入:鼠标和键盘消息
-
第四部分:绘制图形
- 获取设备上下文 (
GetDC,BeginPaint,EndPaint) - 使用 GDI 函数绘制 (
TextOut,MoveToEx,LineTo,Rectangle) - 一个简单的画板程序
- 获取设备上下文 (
-
第五部分:控件与资源
(图片来源网络,侵删)- 创建控件 (
CreateWindowEx) - 消息处理与控件通信
- 使用资源文件 (.rc) - 简单对话框
- 创建控件 (
-
第六部分:进阶主题与学习资源
- Unicode 与 MBCS
- 现代 Windows 编程:Windows SDK 与 C++/WinRT
- 推荐书籍与在线资源
第一部分:Windows API 简介
什么是 Windows API?
Windows API (Application Programming Interface) 是微软为 Windows 操作系统提供的一套核心函数、类、消息和结构,它就像是操作系统和应用程序之间的“中间人”或“翻译官”,当你用 C/C++ 编写一个 Windows 程序时,你实际上是在调用这些 API 函数,让操作系统为你完成窗口创建、图形绘制、文件操作、网络通信等底层任务。
为什么学习 Windows API?
- 理解底层原理:学习 Windows API 能让你深刻理解 Windows 操作系统是如何管理窗口、消息、进程和线程的,这是学习更高级框架(如 MFC, Qt, .NET WinForms/WPF)的基础。
- 性能极致:直接调用 API 是最直接的方式,没有额外的抽象层开销,能获得最佳的性能。
- 功能全面:API 提供了操作系统最完整、最底层的功能,一些高级框架可能不会暴露所有细节。
- 跨平台能力:虽然 API 是 Windows 特有的,但理解事件驱动、窗口句柄等概念可以平滑过渡到其他 GUI 框架(如 macOS 的 Cocoa, Linux 的 GTK)。
开发环境准备
- 编译器:你需要一个 C/C++ 编译器,最经典和推荐的是 Visual Studio,社区版 (Community Edition) 是免费的,功能强大。
- 安装 Visual Studio:从 Visual Studio 官网 下载并安装 "Visual Studio Community"。
- 安装工作负载:在安装程序中,确保勾选 “使用 C++ 的桌面开发” 工作负载,这会自动安装编译器、Windows SDK 和必要的工具。
- SDK (Software Development Kit):Windows SDK 包含了编译 Windows 程序所需的头文件(如
windows.h)、库文件和工具,Visual Studio 安装时会自动包含最新的 SDK。
第二部分:第一个 Windows 程序 - "Hello, Windows!"
这是 Windows 编程的 "Hello, World!",它会创建一个空白窗口,并在窗口标题栏显示 "Hello, Windows!"。
程序结构解析
一个最简单的 Windows 程序由以下几个关键部分组成:

WinMain函数:程序的入口点,类似于控制台程序的main函数,它负责注册窗口类、创建窗口、进入消息循环。WNDCLASSEX结构体:定义窗口的“蓝图”或“模板”,包括窗口过程、图标、光标、背景色等。RegisterClassEx函数:将WNDCLASSEX结构体注册到操作系统,使其成为一个合法的窗口类。CreateWindowEx函数:根据已注册的窗口类,创建一个具体的窗口实例。ShowWindow和UpdateWindow函数:显示窗口,并立即发送一个WM_PAINT消息,让窗口进行首次绘制。MSG结构体和GetMessage/DispatchMessage:MSG用于存储消息。GetMessage从消息队列中获取一条消息,DispatchMessage将消息发送给窗口过程进行处理。WndProc函数:窗口过程,是窗口的“大脑”,操作系统会把各种消息(如鼠标点击、键盘输入、重绘请求)发送到这里。
代码详解
创建一个新的 C++ 控制台项目(在 Visual Studio 中,选择 "空项目" 然后添加一个 .cpp 文件),并将以下代码粘贴进去。
#include <windows.h> // 包含所有 Windows API 的头文件
// 窗口过程的函数声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// 程序入口点
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 1. 定义并注册窗口类
WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW; // 窗口大小改变时重绘
wcex.lpfnWndProc = WndProc; // 指向窗口过程的函数指针
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // 使用标准箭头光标
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // 使用默认窗口背景色
wcex.lpszClassName = "MainWindowClass"; // 窗口类名
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL, "窗口类注册失败!", "错误", MB_ICONERROR);
return 1;
}
// 2. 创建窗口
HWND hWnd = CreateWindowEx(
0, // 扩展样式
"MainWindowClass", // 窗口类名
"Hello, Windows!", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, CW_USEDEFAULT, // x, y 坐标 (由系统决定)
500, 400, // 宽度, 高度
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 应用程序实例句柄
NULL // 创建参数
);
if (!hWnd) {
MessageBox(NULL, "窗口创建失败!", "错误", MB_ICONERROR);
return 1;
}
// 3. 显示和更新窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// 4. 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); // 翻译键盘消息 (如 WM_KEYDOWN -> WM_CHAR)
DispatchMessage(&msg); // 将消息发送给 WndProc 处理
}
// 当 GetMessage 返回 0 时,程序结束
return (int)msg.wParam;
}
// 5. 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc;
switch (message) {
case WM_PAINT: {
// 当窗口需要重绘时,操作系统会发送此消息
hdc = BeginPaint(hWnd, &ps);
// TODO: 在这里进行绘制
TextOut(hdc, 50, 50, "你好,Windows API世界!", 18);
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY: {
// 当窗口被销毁时,发送此消息
PostQuitMessage(0); // 发送 WM_QUIT 消息,使 GetMessage 循环退出
break;
}
default:
// 对于我们没有处理的任何消息,都交给系统默认处理
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
编译与运行
- 在 Visual Studio 中,选择 "生成" -> "生成解决方案"。
- 成功生成后,按
Ctrl + F5运行(不调试运行),你会看到一个标题为 "Hello, Windows!" 的窗口。
第三部分:深入窗口过程
WndProc 是程序的核心,它是一个回调函数,由操作系统在特定事件发生时调用。
WndProc 函数详解
HWND hWnd: 此消息所属的窗口句柄。UINT message: 消息的标识符,如WM_PAINT,WM_LBUTTONDOWN。WPARAM wParam: 消息的附加信息 1,其含义取决于message。LPARAM lParam: 消息的附加信息 2,其含义也取决于message。
消息类型
WM_PAINT: 当窗口的某部分需要被重绘时发送(窗口被另一个窗口遮挡后又露出来),处理此消息时,你需要获取设备上下文 (HDC) 并进行绘制。WM_DESTROY: 当窗口被销毁时发送,这是清理资源的好地方,标准的做法是调用PostQuitMessage(0),这会向消息队列中插入一个WM_QUIT消息,导致GetMessage循环结束,程序退出。WM_CLOSE: 当用户点击窗口的 "X" 按钮时发送,默认情况下,DefWindowProc会处理此消息并调用DestroyWindow,从而触发WM_DESTROY,你也可以在这里弹出一个 "确认退出" 的对话框。
处理用户输入
让我们在 WndProc 中添加鼠标点击事件的处理。
// 在 WndProc 的 switch 语句中添加 case WM_LBUTTONDOWN:
case WM_LBUTTONDOWN: {
// lParam 包含鼠标的 x, y 坐标
int xPos = LOWORD(lParam);
int yPos = HIWORD(lParam);
char buffer[100];
sprintf_s(buffer, "鼠标点击位置: (%d, %d)", xPos, yPos);
MessageBox(hWnd, buffer, "鼠标点击", MB_OK);
break;
}
现在运行程序,点击窗口内的任意位置,就会弹出一个对话框显示点击坐标。
第四部分:绘制图形
绘制图形需要使用 GDI (Graphics Device Interface)。
获取设备上下文
设备上下文 (HDC) 是一个 GDI 对象,包含了绘制窗口所需的所有信息(如选中的画笔、字体、颜色等),获取 HDC 的主要方式有两种:
BeginPaint/EndPaint: 只用于响应WM_PAINT消息,它会自动使无效区域变为有效。GetDC/ReleaseDC: 用于在非WM_PAINT消息中绘制(在鼠标移动时实时绘制线条)。必须成对调用,否则会泄露资源。
使用 GDI 函数
在 WM_PAINT 消息中,我们可以使用各种 GDI 函数来绘制。
case WM_PAINT: {
hdc = BeginPaint(hWnd, &ps);
// 1. 绘制文本
TextOut(hdc, 50, 50, "你好,Windows API世界!", 18);
// 2. 创建和选择画笔
HPEN hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0)); // 红色实线画笔,宽度为3
HPEN hOldPen = (HPEN)SelectObject(hdc, hPen); // 选择新画笔,并保存旧的
// 3. 绘制线条
MoveToEx(hdc, 50, 100, NULL); // 移动到 (50, 100)
LineTo(hdc, 250, 200); // 画线到 (250, 200)
// 4. 创建和选择画刷
HBRUSH hBrush = CreateSolidBrush(RGB(0, 0, 255)); // 蓝色实心画刷
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
// 5. 绘制矩形
Rectangle(hdc, 50, 250, 250, 350);
// 6. 恢复原始对象并删除创建的对象
SelectObject(hdc, hOldPen);
SelectObject(hdc, hOldBrush);
DeleteObject(hPen);
DeleteObject(hBrush);
EndPaint(hWnd, &ps);
break;
}
一个简单的画板程序
这个程序允许你按住鼠标左键并拖动来画线。
// 在全局变量中添加一个标志位
bool g_isDrawing = false;
int g_lastX = 0, g_lastY = 0;
// 在 WndProc 中添加消息处理
case WM_LBUTTONDOWN: {
g_isDrawing = true;
g_lastX = LOWORD(lParam);
g_lastY = HIWORD(lParam);
break;
}
case WM_MOUSEMOVE: {
if (g_isDrawing) {
hdc = GetDC(hWnd); // 在非 WM_PAINT 中使用 GetDC
HPEN hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
MoveToEx(hdc, g_lastX, g_lastY, NULL);
LineTo(hdc, LOWORD(lParam), HIWORD(lParam));
SelectObject(hdc, hOldPen);
DeleteObject(hPen);
ReleaseDC(hWnd, hdc); // 记得释放 DC
g_lastX = LOWORD(lParam);
g_lastY = HIWORD(lParam);
}
break;
}
case WM_LBUTTONUP: {
g_isDrawing = false;
break;
}
第五部分:控件与资源
纯 API 创建控件很繁琐,但它是理解 GUI 工作原理的最佳方式。
创建控件
使用 CreateWindowEx 函数可以创建标准控件,第一个参数通常是控件的类名(如 "BUTTON", "EDIT")。
// 在 WinMain 中,创建窗口后,添加以下代码来创建一个按钮
HWND hButton = CreateWindowEx(
0, // 扩展样式
"BUTTON", // 控件类名
"点击我", // 按钮文本
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // 样式
10, 10, 100, 30, // 位置和大小
hWnd, // 父窗口句柄
(HMENU)1, // 控件ID (一个唯一的值)
hInstance, // 实例句柄
NULL // 参数
);
消息处理与控件通信
控件通过向父窗口发送消息来与父窗口通信,我们需要在 WndProc 中处理这些消息。
WM_COMMAND: 当控件被操作(如按钮被点击、菜单被选择)时,会向父窗口发送此消息。wParam的低 16 位是控件的 ID,高 16 位是通知码(如BN_CLICKED表示按钮被点击)。
// 在 WndProc 的 switch 语句中添加
case WM_COMMAND: {
int wmId = LOWORD(wParam); // 获取控件ID
// 分析菜单选择:
switch (wmId) {
case 1: // 我们为按钮设置的ID
MessageBox(hWnd, "按钮被点击了!", "提示", MB_OK);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
}
使用资源文件 (.rc)
硬编码控件和字符串不方便,资源文件(.rc)可以让你用类似脚本的语法定义资源(图标、位图、对话框、菜单等)。
- 在 Visual Studio 中,右键项目 -> "添加" -> "资源..." -> "对话框",创建一个新对话框。
- 在对话框编辑器中添加一个静态文本框和编辑框。
- 保存后,Visual Studio 会自动生成一个
.rc文件和一个对应的.h头文件(如resource.h)。 - 在代码中,你可以使用
DialogBox函数来加载并显示这个对话框。
第六部分:进阶主题与学习资源
Unicode 与 MBCS
现代 Windows 应用程序应使用 Unicode。windows.h 中定义了 TCHAR 宏和 TEXT 宏,让你的代码可以轻松地在 Unicode 和 MBCS (多字节字符集) 之间切换。
char->TCHARstrlen->_tcslen"Hello"->TEXT("Hello")
现代 Windows 编程
直接使用 C 风格的 Windows API 虽然经典,但非常繁琐,微软一直在提供更现代的 C++ 方式:
- Windows SDK (C++/WinRT): 官方推荐的现代 C++ API,基于 C++17 的投影技术,它提供了类型安全、异步操作等现代 C++ 特性,是开发 UWP 和 Win32 应用的未来方向。
- ATL (Active Template Library): 一个轻量级的 C++ 模板库,用于简化 COM 编程和窗口创建。
- MFC (Microsoft Foundation Classes): 一个更重量级的 C++ 类库,封装了大部分 Windows API,提供了文档/视图架构等高级特性。
对于初学者,掌握纯 API 是基础;对于大型项目,C++/WinRT 或 MFC 是更高效的选择。
推荐书籍与在线资源
- 书籍:
- 《Windows 程序设计》(Programming Windows, Charles Petzold): 圣经级别的著作,必读!它详细讲解了 Win32 API 的方方面面,虽然使用的是 C 语言,但原理是永恒的。
- 《Windows 核心编程》(Windows via C/C++, Jeffrey Richter): 更深入地讲解了 Windows 内部机制,如进程、线程、内存管理等,适合进阶。
- 在线资源:
- Microsoft Docs: 最权威的 API 文档,搜索任何 API 函数,都能找到详细的说明、参数和示例代码。
- The Old New Thing: Raymond Chen 的博客,充满了关于 Windows 历史和内部机制的精彩故事和深入解释。
- GitHub: 搜索 "win32api example" 可以找到大量开源示例项目。
这份教程为你打开了 Windows API 的大门,从创建第一个窗口开始,逐步深入到消息处理、图形绘制和控件交互,你已经掌握了 Windows 编程的核心思想,继续探索,你会发现 Windows 系统的强大和魅力所在。
