Windows API 编程教程:从入门到实践
目录
- 什么是 Windows API?
- 开发环境搭建
- 第一个 Windows 程序:Hello, World!
- 核心概念详解
- 入口点函数
- 窗口注册
- 窗口创建
- 消息循环
- 窗口过程
- 常用 API 函数和结构体
- 窗口相关
- 绘图相关
- 文件操作相关
- 一个完整的项目:简单计算器
- 进阶学习路径
- 总结与资源
什么是 Windows API?
Windows API (Application Programming Interface) 是微软为 Windows 操作系统提供的一套函数、类、结构体、消息和常量的集合,它就像是 Windows 操作系统与你的应用程序之间的“信使”和“工具箱”。

- 功能:通过调用这些 API,你的程序可以创建窗口、绘制图形、处理用户输入(如鼠标点击、键盘敲击)、访问文件系统、管理内存、与其他程序通信等几乎所有与操作系统交互的操作。
- 本质:API 就是操作系统暴露给开发者的一组预编译的函数库,你的 C/C++ 代码在编译时,会链接到这些库(如
kernel32.lib,user32.lib,gdi32.lib),在运行时,程序会调用操作系统内部的实现来完成特定任务。
为什么学习 Windows API?
- 理解底层:让你明白 Windows 程序是如何工作的,而不是仅仅依赖 MFC、Qt、.NET 等高级框架。
- 强大灵活:可以实现对 UI 和系统行为的精细控制,这是高级框架难以企及的。
- 历史基础:许多 Windows 上的技术(如 MFC, ATL, WPF, WinRT)都构建在 Win32 API 之上,理解 API 是学习这些技术的基础。
- 系统级编程:对于开发驱动、工具软件、性能敏感型应用等至关重要。
开发环境搭建
最经典和推荐的工具组合是 Visual Studio + Windows SDK。
-
安装 Visual Studio:
- 前往 Visual Studio 官网 下载安装程序。
- 安装时,选择 “使用 C++ 的桌面开发” 工作负载,这个选项会自动包含你需要的 C++ 编译器和 Windows SDK。
- 如果已经安装,可以通过“修改”来添加此工作负载。
-
Windows SDK:
(图片来源网络,侵删)如上所述,安装 Visual C++ 桌面开发时会自动包含最新的 Windows SDK,你无需单独下载。
环境验证: 安装完成后,打开 Visual Studio,创建一个新的 “Windows 桌面应用程序” 项目(C++),如果能够成功编译并运行一个默认的窗口程序,说明环境已经搭建成功。
第一个 Windows 程序:Hello, World!
我们将创建一个最简单的窗口,并在窗口上显示文字。
步骤:

- 打开 Visual Studio,创建一个新的 “Windows 桌面应用程序” 项目,命名为
Win32HelloWorld。 - 打开
Win32HelloWorld.cpp文件,清空其内容,然后粘贴以下代码。
#include <windows.h> // 包含所有 Windows API 的头文件
// 窗口过程函数的声明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// 程序的入口点,不同于标准的 main
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 1. 注册窗口类
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc; // 窗口过程函数地址
wc.hInstance = hInstance; // 当前实例句柄
wc.lpszClassName = L"MainWindow"; // 窗口类名
// 注册窗口类
RegisterClass(&wc);
// 2. 创建窗口
HWND hwnd = CreateWindowEx(
0, // 可选的扩展样式
L"MainWindow", // 注册的窗口类名
L"Hello, Windows API!", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, CW_USEDEFAULT, // 窗口位置 (x, y)
500, 300, // 窗口大小 (宽度, 高度)
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 实例句柄
NULL // 附加创建数据
);
if (hwnd == NULL) {
return 0;
}
// 3. 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 4. 消息循环
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); // 翻译键盘消息
DispatchMessage(&msg); // 将消息发送到窗口过程
}
return 0;
}
// 5. 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
// 当窗口需要重绘时,操作系统会发送此消息
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// 在这里进行绘制
TextOut(hdc, 50, 50, L"Hello, World!", 13);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY: {
// 当窗口被销毁时,发送此消息
PostQuitMessage(0); // 发送一个 WM_QUIT 消息,使 GetMessage 返回 0
return 0;
}
}
// 对于我们没有处理的消息,调用默认的窗口过程
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
代码解析:
#include <windows.h>:一切的开始,包含了所有定义。WinMain:Windows GUI 程序的入口点,而不是main。WNDCLASS:定义窗口的“蓝图”,包括窗口如何处理消息、图标、背景等。RegisterClass:向操作系统注册这个“蓝图”。CreateWindowEx:根据注册的“蓝图”创建一个具体的窗口实例。ShowWindow和UpdateWindow:让窗口显示出来。GetMessage/TranslateMessage/DispatchMessage:这是程序的核心,它不断地从消息队列中获取消息,翻译(如键盘输入),然后分发给对应的窗口过程函数进行处理。WindowProc:每个窗口的“大脑”,操作系统会根据窗口句柄hwnd将消息(如鼠标点击、键盘按下、重绘)发送到这里。switch语句根据不同的消息码(uMsg)执行不同的操作。WM_PAINT:当窗口第一次显示、被其他窗口遮挡后重新显示、或被用户改变大小时,操作系统会发送此消息,告诉窗口:“你需要重绘自己了!”。WM_DESTROY:当用户点击窗口的关闭按钮时,窗口会收到此消息,我们调用PostQuitMessage(0)来通知消息循环该退出了。DefWindowProc:非常重要!对于所有你没有在WindowProc中处理的消息,都必须把它传递给这个默认函数,否则窗口的基本功能(如关闭按钮)将无法工作。
运行这个程序,你将看到一个标题为 "Hello, Windows API!" 的窗口,窗口中央有 "Hello, World!" 字样。
核心概念详解
入口点函数
WinMain(HINSTANCE hInstance, ...): 传统 GUI 程序的入口。main(): 控制台程序的入口。wWinMain(): 宽字符版本的WinMain。
窗口注册
WNDCLASS/WNDCLASSEX: 定义窗口类的属性。WNDCLASSEX是WNDCLASS的扩展版本,包含了更多功能(如小图标、大图标)。- 关键成员:
lpfnWndProc: 指向窗口过程函数的指针。这是最重要的成员。hInstance: 程序的实例句柄。lpszClassName: 窗口类的唯一名称,用于CreateWindow。hIcon,hCursor,hbrBackground: 定义窗口的外观。
窗口创建
CreateWindowEx: 创建窗口。- 返回值: 成功返回窗口句柄,失败返回
NULL。句柄 是 Windows 中用来标识一个资源(如窗口、图标、画笔)的整数,是你在 API 调用中传递的核心参数。
消息循环
GetMessage: 从线程的消息队列中取出一条消息,如果队列为空,它会等待,如果收到WM_QUIT消息,它返回0,循环结束。TranslateMessage: 将虚拟键码消息(如WM_KEYDOWN)转换为字符消息(WM_CHAR)。DispatchMessage: 将消息发送给目标窗口的WindowProc函数去处理。
窗口过程
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam):hwnd: 接收消息的窗口句柄。uMsg: 消息标识符(如WM_PAINT,WM_LBUTTONDOWN)。wParam,LPARAM: 消息的附加参数,它们的含义取决于uMsg,对于鼠标点击消息,wParam包含了按键状态,lParam包含了鼠标坐标。
常用 API 函数和结构体
窗口相关
ShowWindow(hwnd, nCmdShow): 显示或隐藏窗口。MoveWindow(hwnd, x, y, width, height, bRepaint): 移动并调整窗口大小。SetWindowText(hwnd, lpszString): 设置窗口标题。GetDlgItem(hDlg, nIDDlgItem): 在对话框中获取一个子控件的句柄。EnableWindow(hwnd, bEnable): 启用或禁用窗口。SetFocus(hwnd): 将焦点设置到指定窗口。
绘图相关
- 设备上下文: 可以理解为 Windows 提供给你的“画布”。
HDC hdc = BeginPaint(hwnd, &ps);: 在处理WM_PAINT时获取绘图 DC。HDC hdc = GetDC(hwnd);: 获取窗口的 DC,用于非WM_PAINT场景。ReleaseDC(hwnd, hdc);: 释放 DC。
- 绘图函数:
TextOut(hdc, x, y, lpString, cchString): 输出文本。Rectangle(hdc, x1, y1, x2, y2): 画矩形。Ellipse(hdc, x1, y1, x2, y2): 画椭圆。MoveToEx(hdc, x, y, NULL): 移动画笔。LineTo(hdc, x, y): 画线。SetTextColor(hdc, color): 设置文本颜色。SetBkMode(hdc, mode): 设置背景模式(透明或不透明)。
文件操作相关
CreateFile: 创建或打开文件、管道等。ReadFile: 从文件或设备中读取数据。WriteFile: 向文件或设备中写入数据。CloseHandle: 关闭一个对象句柄(如文件句柄)。GetFileSize: 获取文件大小。
一个完整的项目:简单计算器
我们将创建一个带界面的计算器,来巩固所学知识。
目标: 创建一个窗口,包含一个显示框和数字按钮,实现简单的加法运算。
步骤:
-
创建窗口和控件: 在
WM_CREATE消息中创建子控件(按钮和静态文本框)。WM_CREATE在窗口创建后立即发送一次。// 在 WNDCLASS 中定义 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = L"CalculatorWindow"; // 在 WindowProc 的 switch 中添加 case WM_CREATE: { // 创建显示框 (静态文本) CreateWindowW(L"STATIC", L"0", WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 10, 200, 30, hwnd, (HMENU)1, hInstance, NULL); // 创建按钮 '1' CreateWindowW(L"BUTTON", L"1", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 50, 50, 30, hwnd, (HMENU)101, hInstance, NULL); // ... 创建其他按钮 '2', '+', '=' 等 // 注意:每个控件都需要一个唯一的ID (HMENU) return 0; } -
处理按钮点击: 当用户点击按钮时,会发送
WM_COMMAND消息。wParam的低16位是控件的ID。case WM_COMMAND: { int controlId = LOWORD(wParam); switch (controlId) { case 101: // 按钮 '1' 的ID // 在显示框中追加 '1' // 1. 获取显示框的句柄 HWND hDisplay = GetDlgItem(hwnd, 1); // 2. 获取当前文本 WCHAR szText[256]; GetWindowTextW(hDisplay, szText, 256); // 3. 追加新文本 wcscat_s(szText, L"1"); // 4. 设置新文本 SetWindowTextW(hDisplay, szText); break; // ... 处理其他按钮 } return 0; } -
实现计算逻辑: 你需要定义几个变量来存储操作数和运算符,并在
WM_COMMAND中根据点击的按钮更新它们。// 在 WindowProc 外部定义(全局或静态) double g_operand1 = 0; double g_operand2 = 0; WCHAR g_operator = L'\0'; // 在 WM_COMMAND 中 case 102: // 按钮 '2' 的ID // ... 类似 '1' break; case 201: // 按钮 '+' 的ID g_operator = L'+'; // 将显示框的值存入 g_operand1 HWND hDisplay = GetDlgItem(hwnd, 1); WCHAR szText[256]; GetWindowTextW(hDisplay, szText, 256); g_operand1 = _wtof(szText); // 清空显示框 SetWindowTextW(hDisplay, L""); break; case 301: // 按钮 '=' 的ID g_operand2 = _wtof(GetDlgItemText(hwnd, 1)); double result = 0; if (g_operator == L'+') { result = g_operand1 + g_operand2; } // 显示结果 WCHAR szResult[256]; swprintf_s(szResult, L"%.2f", result); SetWindowTextW(GetDlgItem(hwnd, 1), szResult); break;
这个项目会涉及很多细节,比如处理小数点、清空、错误处理等,但它完美地展示了如何将多个 API 调用组合起来,构建一个有交互功能的应用程序。
进阶学习路径
掌握了基础后,你可以探索以下方向:
- 对话框: 学习使用
DialogBox和DialogBoxParam创建模态对话框,以及CreateDialog创建非模态对话框,对话框的编程模型略有不同,通常使用资源文件(.rc)来定义界面。 - 资源文件 (
.rc): 学习使用 Visual Studio 的资源编辑器或手动编写.rc文件来创建菜单、图标、对话框、字符串表等,使界面与代码分离。 - 通用控件: 学习使用
Rich Edit控件、Tree View、List View、Toolbar、Status Bar等更现代、更复杂的控件。 - 图形设备接口: 深入学习 GDI,如画笔、画刷、字体、位图操作等,创建更精美的图形界面。
- Windows 消息: 研究更多系统消息,如
WM_TIMER(定时器),WM_MOUSEMOVE(鼠标移动),WM_KEYDOWN(键盘按下) 等。 - 多线程: 学习使用
CreateThread和同步对象(如Mutex,Event,CriticalSection)来创建多线程应用程序。 - COM (Component Object Model): 这是 Windows 的核心组件技术,学习 COM 可以理解 ActiveX、OLE 等技术,也是许多现代 Windows API 的基础。
总结与资源
Windows API 编程是一条通往 Windows 系统核心的强大路径,虽然它的代码量比使用高级框架要多,并且需要手动管理很多细节,但它带来的理解深度和控制力是无与伦比的,从创建一个窗口开始,逐步添加控件、处理消息、实现逻辑,这个过程本身就是一种极佳的学习体验。
推荐资源:
- 必读经典:
- 《Windows 程序设计》(Charles Petzold 著): 被誉为 Windows API 编程的“圣经”,内容详尽,讲解清晰,是所有学习者的首选,虽然有些版本稍旧,但核心概念永不过时。
- 在线文档:
- Microsoft Docs - Windows API: 最权威、最及时的官方文档,查找任何函数、结构体或消息的详细信息,都应该来这里。
- 工具:
- Visual Studio: 强大的 IDE,集成了调试器、资源编辑器等。
- Spy++: Visual Studio 自带的工具,可以用来查看系统中的窗口、进程和消息,是调试 UI 问题的利器。
- Dependency Walker: 虽然已有些过时,但仍是查看程序依赖哪些 DLL 的好工具。
祝你学习愉快,在 Windows API 的世界里探索无穷的乐趣!
