WinMain 是 Windows 程序的入口点,相当于控制台程序中的 main 函数,当一个 Windows 桌面应用程序被加载到内存中并由操作系统执行时,操作系统会首先查找并调用 WinMain 函数。

(图片来源网络,侵删)
WinMain 的基本结构
WinMain 函数有固定的签名,它接收四个参数,并返回一个 int 类型的值。
int WINAPI WinMain( HINSTANCE hInstance, // 当前实例的句柄 HINSTANCE hPrevInstance, // 旧版Windows的遗留,总为NULL LPSTR lpCmdLine, // 命令行参数 int nCmdShow // 窗口的显示方式 );
参数详解
HINSTANCE hInstance
- 类型:
HINSTANCE(Handle to an INSTANCE,实例句柄) - 含义: 这是一个句柄,它唯一标识了当前正在运行的程序实例,在 Windows 中,句柄是一个指向操作系统内部数据结构的指针,你的程序通过这个句柄来请求操作系统服务,当你需要加载资源(如图标、位图)时,就需要用到这个句柄。
- 类比: 可以把它想象成你程序的“身份证号”。
HINSTANCE hPrevInstance
- 类型:
HINSTANCE - 含义: 在 16 位 Windows 环境下,这个参数用于判断是否已经有另一个实例正在运行,但在 32 位和 64 位的 Windows 中,每个进程都有自己独立的地址空间,所以这个参数总是
NULL。 - 建议: 在现代 Windows 编程中,你可以完全忽略这个参数。
LPSTR lpCmdLine
- 类型:
LPSTR(Long Pointer to a STRing,指向字符串的长指针) - 含义: 这是一个指向命令行参数的字符串,它包含了用户在启动程序时在程序名后面输入的所有内容。
- 注意: 它不包含程序自身的名称,并且参数之间通常用空格分隔,如果你需要解析复杂的命令行(例如带引号的参数),最好使用 Windows API 提供的
CommandLineToArgvW函数,而不是自己手动分割。
int nCmdShow
- 类型:
int - 含义: 这个参数指定了主窗口应该如何被显示,它是一个预定义的常量,
SW_SHOW: 正常显示窗口。SW_HIDE: 隐藏窗口。SW_MINIMIZE: 最小化窗口。SW_MAXIMIZE: 最大化窗口。
- 用途: 当你从快捷方式启动程序时,可以设置其“运行方式”为“最大化”或“最小化”,
nCmdShow的值就会相应地改变,你的程序应该尊重这个设置。
WinMain vs. main
| 特性 | main |
WinMain |
|---|---|---|
| 用途 | 控制台应用程序 (命令行程序) | Windows 桌面 GUI 应用程序 |
| 入口点 | C/C++ 标准规定 | Windows 操作系统规定 |
| 参数 | int argc, char *argv[] |
HINSTANCE, HINSTANCE, LPSTR, int |
| 返回值 | int,返回给父进程或操作系统 |
int,返回给操作系统 |
| GUI | 通常没有图形界面,使用 printf 等函数 |
必须使用 Windows API 创建和管理窗口 |
一个最简单的 WinMain 示例
这个示例创建一个窗口,显示一个消息框,然后退出,这是理解 Windows 程序工作流程的经典入门程序。
#include <windows.h> // 必须包含 Windows API 头文件
// 窗口过程函数的声明 (LRESULT 是返回值类型,HWND 是窗口句柄等)
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 1. 注册窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WindowProc; // 窗口过程函数的地址
wc.hInstance = hInstance; // 实例句柄
wc.lpszClassName = "MyWindowClass"; // 窗口类名
// 如果注册失败
if (!RegisterClass(&wc)) {
MessageBox(NULL, "窗口类注册失败!", "错误", MB_ICONERROR);
return 1;
}
// 2. 创建窗口
HWND hwnd = CreateWindow(
"MyWindowClass", // 窗口类名
"我的第一个 WinMain 程序", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, CW_USEDEFAULT, // 初始位置 (x, y)
500, 300, // 窗口大小 (width, height)
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 实例句柄
NULL // 附加数据
);
if (hwnd == NULL) {
MessageBox(NULL, "窗口创建失败!", "错误", MB_ICONERROR);
return 1;
}
// 3. 显示和更新窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 4. 消息循环
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); // 翻译键盘消息
DispatchMessage(&msg); // 将消息发送到窗口过程函数
}
// 5. 程序结束,返回消息码
return (int)msg.wParam;
}
// 窗口过程函数:负责处理发送到该窗口的所有消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY: // 当窗口被销毁时发送此消息
PostQuitMessage(0); // 发送一个 WM_QUIT 消息,使 GetMessage 返回 0
break;
// 对于其他我们不关心的消息,交给系统默认处理
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
代码流程解析:
- 注册窗口类: 在创建窗口之前,必须先告诉 Windows 你的窗口长什么样、有什么行为。
WNDCLASS结构体就是用来定义这些信息的,最重要的就是lpfnWndProc,它指定了一个函数地址,所有发给这个窗口的消息(如鼠标点击、键盘按下、窗口移动等)都会由这个函数来处理。 - 创建窗口:
CreateWindow函数根据注册的窗口类信息,在内存中创建一个窗口实例,如果成功,它会返回一个窗口句柄HWND,后续所有对这个窗口的操作(如显示、绘制、销毁)都通过这个句柄来完成。 - 显示窗口:
ShowWindow和UpdateWindow负责将刚刚创建的窗口真正显示在屏幕上。 - 消息循环: 这是 Windows GUI 程序的核心。
GetMessage会一直等待,直到有消息(如鼠标点击、键盘输入)被放入应用程序的消息队列,一旦获取到消息,程序就会通过TranslateMessage和DispatchMessage将它“派发”到对应的窗口过程函数 (WindowProc) 中进行处理。 - 窗口过程:
WindowProc是一个回调函数,由系统调用,它通过switch语句判断消息类型并执行相应操作,当用户点击窗口的关闭按钮时,系统会向你的窗口发送一个WM_DESTROY消息,在WM_DESTROY的处理分支中,我们调用PostQuitMessage(0),这会向消息队列中放入一个WM_QUIT消息,从而使GetMessage循环结束,程序退出。
现代开发中的 WinMain
虽然 WinMain 是经典的 Windows 程序入口,但在现代 Windows 开发中,尤其是在使用微软的现代桌面应用程序开发框架时,你可能会看到 main 函数作为入口点。
- C++/WinRT: 这是微软推荐的用于编写 UWP 和 Windows App SDK 应用的新 C++ 语言投影,它使用标准的
main函数作为入口点。 - Windows App SDK (Project Reunion): 同样,它也使用
main函数。
在这些框架中,main 函数内部会初始化 Windows 运行时环境,然后启动一个类似消息循环的事件泵,并最终调用你的 UI 代码,框架在底层处理了 WinMain 的所有细节,让你可以更专注于业务逻辑。

(图片来源网络,侵删)
WinMain是传统 Windows GUI 应用程序的入口点。- 它接收四个特定于 Windows 的参数,用于获取程序实例、命令行和窗口显示方式。
- 一个典型的 WinMain 程序流程是:注册窗口类 -> 创建窗口 -> 显示窗口 -> 进入消息循环。
- 对于学习 Windows API 的工作原理,
WinMain是一个不可或缺的起点。 - 在使用现代框架(如 C++/WinRT)时,入口点可能变为标准的
main,但底层机制依然是基于 Windows 消息循环的。

(图片来源网络,侵删)
