核心概念:Windows API 是什么?
当你用 C 语言在 Windows 上写程序时,你实际上是在调用由 Windows 操作系统本身提供的一系列函数、宏和数据结构,这个庞大的函数集合就是 Windows API。

(图片来源网络,侵删)
- 作用:它就像是操作系统和你的应用程序之间的“翻译官”或“中间人”,你的程序告诉 Windows:“我想要创建一个窗口”、“我想要在窗口上画一条线”、“当用户点击鼠标时请通知我”,Windows API 就负责把这些请求翻译成系统能理解的指令,并返回结果。
- 特点:
- 原生:这是 Windows 最底层的接口,功能最强大,也最复杂。
- C 语言风格:Windows API 主要用 C 语言编写,其函数命名、参数传递等风格非常符合 C 语言的规范。
- 头文件和库:使用 Windows API 需要包含特定的头文件(如
windows.h),并链接到对应的库文件(如kernel32.lib,user32.lib)。
第一个 Windows 程序:一个“空”窗口
一个最基础的 Windows GUI 程序,通常需要以下几个核心部分:
- 程序入口点:不同于控制台程序的
main(),GUI 程序的入口是WinMain()。 - 窗口注册:告诉 Windows 你要创建的窗口是什么样的(类名、图标、窗口过程函数等)。
- 窗口创建:根据注册的窗口类,实际创建一个窗口句柄。
- 消息循环:一个无限循环,用于获取和分发来自用户(键盘、鼠标)或系统的事件消息。
- 窗口过程:一个回调函数,专门处理窗口收到的各种消息(如绘制、点击、关闭等)。
详细步骤与代码解析
下面我们一步步来构建一个经典的 Windows 窗口程序。
包含头文件和链接库
#include <windows.h> // 这是所有 Windows API 的核心头文件 // 省略省略...
编译时,你需要链接到 Windows 的核心库,在 Visual Studio 中通常会自动处理,如果你用命令行编译(如 MinGW 的 GCC),需要加上:
gcc your_file.c -o your_program.exe -mwindows
-mwindows 参数会自动链接上所需的 GUI 库。
程序入口点:WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// ... 程序的主要逻辑将在这里 ...
return 0;
}
HINSTANCE hInstance:当前实例的句柄,可以理解为程序的“身份证号”。HINSTANCE hPrevInstance:在 32/64 位 Windows 中,这个参数总是NULL。LPSTR lpCmdLine:命令行参数字符串。int nCmdShow:指定窗口如何显示(如正常显示、最大化、最小化)。
窗口过程函数:WndProc
这是一个回调函数,Windows 会在特定事件发生时调用它。

(图片来源网络,侵删)
// 窗口过程函数的声明
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// 函数实现
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY: // 当窗口被销毁时(例如点击关闭按钮)
PostQuitMessage(0); // 向消息队列发送一个 WM_QUIT 消息,结束消息循环
return 0;
case WM_PAINT: // 当窗口需要重绘时(例如窗口被遮挡后又显示)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps); // 开始绘制
// 在这里可以添加绘制代码,比如画文字
TextOut(hdc, 50, 50, L"Hello, Windows API!", 19);
EndPaint(hwnd, &ps); // 结束绘制
}
return 0;
}
// 对于我们没有处理的消息,交给系统默认处理
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HWND hwnd:接收消息的窗口句柄。UINT uMsg:消息本身(如WM_DESTROY,WM_PAINT)。WPARAM wParam/LPARAM lParam:消息的附加参数。
注册窗口类
在创建窗口之前,必须先“注册”一个窗口类,告诉 Windows 这个窗口的“模板”是什么样的。
// 窗口类的名称
const wchar_t CLASS_NAME[] = L"Sample Window Class";
// ... 在 WinMain 函数内部 ...
// 1. 注册窗口类
WNDCLASS wc = { };
wc.lpfnWndProc = WndProc; // 指向我们刚才定义的窗口过程函数
wc.hInstance = hInstance; // 当前实例句柄
wc.lpszClassName = CLASS_NAME; // 窗口类名
// 加载一个图标(可选)
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
// 加载一个光标(可选)
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// 设置窗口的背景色(可选)
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
// 注册窗口类
RegisterClass(&wc);
创建窗口
有了窗口类模板,就可以创建具体的窗口了。
// ... 在 WinMain 函数内部,注册窗口类之后 ...
// 2. 创建窗口
HWND hwnd = CreateWindowEx(
0, // 可选的窗口样式
CLASS_NAME, // 窗口类名
L"Hello Windows API", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
// 窗口位置和大小
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 实例句柄
NULL // 附加创建参数
);
if (hwnd == NULL) {
return 0; // 创建失败
}
// 3. 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
消息循环
这是程序运行的核心,它会不断地从消息队列中取出消息,并分发给对应的窗口过程函数处理。
// ... 在 WinMain 函数内部,创建并显示窗口之后 ...
// 4. 消息循环
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); // 翻译键盘消息(如将 WM_KEYDOWN 转为 WM_CHAR)
DispatchMessage(&msg); // 将消息发送给 WndProc 函数处理
}
// 当 GetMessage 返回 0 时,通常是收到了 WM_QUIT 消息,程序退出
return (int)msg.wParam;
完整代码示例
将以上所有部分组合起来,就是一个完整的、可以运行的 Windows 窗口程序。

(图片来源网络,侵删)
#include <windows.h>
// 窗口过程函数的声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 1. 注册窗口类
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClass(&wc);
// 2. 创建窗口
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
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 (int)msg.wParam;
}
// 5. 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// TextOut 使用宽字符,所以字符串前要加 L
TextOut(hdc, 50, 50, L"Hello, Windows API!", 19);
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
如何编译和运行?
- Visual Studio: 创建一个新的 "Windows 桌面应用程序" 项目,将代码粘贴进去即可。
- MinGW (GCC): 保存为
main.c,然后在命令行运行:gcc main.c -o my_win_app.exe -mwindows ./my_win_app.exe
运行后,你将看到一个标题为 "Learn to Program Windows" 的窗口,窗口中央有 "Hello, Windows API!" 的文字。
进阶学习方向
掌握了这个基础框架后,你可以探索更广阔的 Windows 编程世界:
- 控件:学习创建按钮、编辑框、列表框等标准 Windows 控件。
- 资源文件:学习使用
.rc文件来管理图标、光标、菜单、对话框等资源,而不是用代码硬编码。 - 文档/视图架构:这是 MFC (Microsoft Foundation Classes) 框架的核心思想,用于构建更复杂的应用程序。
- COM (Component Object Model):Windows 的组件技术,用于创建可复用的软件组件(DirectX 就基于 COM)。
- .NET 互操作:如果你的 C 程序需要调用 .NET 的库,可以使用 P/Invoke (Platform Invoke) 技术。
- 现代 Windows UI (WinUI 3):微软推出的新一代 UI 框架,用于替代旧的 Win32 API,提供更现代的控件和 XAML 布局,它仍然底层调用 Win32,但提供了更高层次的抽象。
替代方案:MFC 和第三方库
直接使用 Win32 API 虽然强大,但代码量较大,也比较繁琐,社区和微软本身提供了一些封装方案:
- MFC (Microsoft Foundation Classes):微软官方的 C++ 类库,对 Win32 API 进行了 C++ 风格的面向对象封装,曾经是 Windows C++ 开发的主流。
- Qt:一个跨平台的 C++ GUI 框架,非常强大和流行,虽然它也支持 Windows,但它的设计理念是“一次编写,到处编译”。
- wxWidgets:另一个跨平台的 C++ GUI 工具包,模仿了 MFC 的一些设计。
- Dear ImGui:一个即时模式 (immediate mode) 的 GUI 库,非常适合在游戏、科学可视化、工具开发中快速创建调试界面。
对于纯 C 语言开发者来说,直接使用 Win32 API 是最直接、最根本的学习路径,理解了它,再去学习其他框架的原理也会事半功倍。
