WinMain 是 Windows 平台下 C/C++ 程序的入口点,相当于控制台程序中的 main 函数,当一个 Windows GUI 程序启动时,操作系统会首先调用 WinMain 函数。

(图片来源网络,侵删)
WinMain 的基本定义
WinMain 函数有四种标准的声明形式,它们的主要区别在于使用的 Windows API 版本(ANSI 或 Unicode)。
最常见的声明(Unicode 版本)
int WINAPI wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow );
ANSI 版本
int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow );
为什么是 wWinMain?
WinMain: 使用char类型处理命令行参数(ANSI)。wWinMain: 使用wchar_t类型处理命令行参数(Unicode)。
现代 Windows 开发强烈推荐使用 Unicode 版本 (wWinMain),因为它能更好地支持多语言字符,在 Visual Studio 中,默认创建的项目就是 wWinMain。
参数详解
我们来逐一分解 wWinMain 的每个参数:
hInstance (Type: HINSTANCE)
- 全称: Handle to the current Instance(当前实例句柄)。
- 作用: 这是一个句柄,代表当前正在运行的程序实例,你可以把它看作是一个“指针”,但它的值不一定等于内存地址,Windows API 使用它来区分不同的程序实例。
- 重要提示: 在现代 Windows 编程中,这个参数在创建窗口时会被用到,但在程序的其他地方,通常可以使用
GetModuleHandle(NULL)来获取当前实例的句柄,所以它的作用有所减弱。
hPrevInstance (Type: HINSTANCE)
- 全称: Handle to the previous Instance(上一个实例句柄)。
- 作用: 这个参数在 32位和64位 Windows 操作系统中始终是
NULL。 - 历史: 在 16位 Windows (Windows 3.x) 中,它用来检查是否已经有另一个实例在运行,如果存在,
hPrevInstance就是那个实例的句柄;否则为NULL,由于现代 Windows 每个进程都是独立的,这个参数已经失去了意义。 - 你可以完全忽略这个参数,它永远都是
NULL。
lpCmdLine (Type: LPWSTR 或 LPSTR)
- 全称: Pointer to a Command-Line string(命令行字符串指针)。
- 作用: 它是一个指向程序的命令行参数的字符串,与
main函数的argc和argv不同,这里是一个完整的字符串。 - 示例:
- 如果你的程序运行命令是
MyApp.exe "file 1.txt" -v,lpCmdLine的值就是L"\"file 1.txt\" -v"(Unicode) 或"\"file 1.txt\" -v"(ANSI)。
- 如果你的程序运行命令是
- 注意: 你需要自己编写代码来解析这个字符串,或者使用 Windows API 提供的
CommandLineToArgvW函数来将其分割成类似argv的数组。
nCmdShow (Type: int)
- 作用: 指定了主窗口应该如何被显示。
- 常见值:
SW_SHOWNORMAL: 正常显示窗口,如果窗口是最小化或最大化,则恢复到其原始大小和位置。SW_SHOWMINIMIZED: 最小化窗口。SW_SHOWMAXIMIZED: 最大化窗口。SW_HIDE: 隐藏窗口。
- 来源: 这个值是由调用程序(用户双击图标,或在命令行中运行)决定的,你可以用它来根据启动方式调整窗口的初始状态。
WINAPI 关键字
WINAPI 是一个宏定义,在 windef.h 中通常被定义为 __stdcall。

(图片来源网络,侵删)
#define WINAPI __stdcall
__stdcall: 是一种函数调用约定,它规定了函数参数的传递顺序(从右到左)以及由谁清理栈空间(被调用函数本身),对于 Windows API 使用__stdcall是为了保证二进制兼容性,你不需要手动去写它,使用WINAPI宏是标准做法。
一个最简单的 WinMain 示例
下面是一个“什么也不做”的 WinMain 程序,它创建一个消息循环,等待用户关闭窗口,然后退出。
#include <windows.h>
// 窗口过程函数的声明(稍后定义)
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
// 1. 注册窗口类
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc; // 窗口过程函数
wc.hInstance = hInstance;
wc.lpszClassName = L"SimpleWin32Class"; // 窗口类名
// 注册窗口类
if (!RegisterClass(&wc))
{
MessageBox(NULL, L"窗口类注册失败!", L"错误", MB_ICONERROR);
return 1;
}
// 2. 创建窗口
HWND hwnd = CreateWindowEx(
0, // 扩展样式
L"SimpleWin32Class", // 窗口类名
L"我的第一个 Win32 窗口", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, CW_USEDEFAULT, // x, y 坐标
500, 400, // 宽度, 高度
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 实例句柄
NULL // 额外参数
);
if (hwnd == NULL)
{
MessageBox(NULL, L"窗口创建失败!", L"错误", 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. 当 GetMessage 返回 0 时,程序退出
return (int)msg.wParam;
}
// 窗口过程函数:负责处理发送到该窗口的所有消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
// 当窗口被销毁时(例如用户点击了 'X' 按钮)
// 发送 WM_QUIT 消息,使 GetMessage 返回 0
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
// 当窗口需要重绘时
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
// 对于其他我们不关心的消息,交给系统默认处理
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
WinMain 与 main 的对比
| 特性 | main (控制台程序) |
WinMain (GUI 程序) |
|---|---|---|
| 平台 | 跨平台 (Linux, macOS, Windows 等) | 仅限 Windows |
| 入口 | 操作系统启动程序后直接调用 | 操作系统启动程序后直接调用 |
| 参数 | int argc, char *argv[] |
HINSTANCE, LPWSTR 等 |
| 用途 | 用于编写命令行工具、服务器等 | 用于创建图形用户界面应用程序 |
| GUI | 通常不涉及,依赖控制台 | 必须通过 Windows API 创建和管理窗口 |
WinMain 是编写 Windows GUI 应用程序的起点,虽然它的结构比 main 复杂一些,并且涉及了许多 Windows 特有的概念(如句柄、消息循环、窗口过程),但理解了它的基本结构和参数,你就掌握了 Windows 程序设计的核心,现代框架(如 MFC, Qt, .NET WinForms/WPF)都封装了底层的 WinMain 和消息循环,让你能更专注于业务逻辑的开发,但理解 WinMain 的工作原理对于深入学习 Windows 编程至关重要。
