每个 Windows 程序都有一个类似 main 函数的入口函数,名字叫做 WinMain 或者 wWinMain , 其声明如下:

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);

该函数有四个参数:

  • hInstance 参数的含义是一个实例句柄,或者是一个模块句柄。该参数代表被加载到内存的可执行程序(exe文件)。某些 Windows API 可能需要该参数。如:加载图标或者加载位图等等。

  • hPrevInstance 该参数无意义,它在过去的16位操作系统中被使用,现在永远为0。

  • pCmdLine 该参数是程序传入的命令行参数,和 main 函数中的 argv 参数含义类似,只不过现在的字符集是 Unicode。

  • nCmdShow 该参数是一个整数,代表应用程序主窗口的显示状态,是最小化、最大化还是正常显示。

这个函数返回一个整形值,这个返回值没有被操作系统使用,但是你可以通过改返回值判断程序的一些运行状态,或者将此值传递给另一个程序。

WINAPI 是一种调用约定。这个约定确保函数从调用者那里接收参数的相关规则,例如,参数在栈中保存的顺序等等。在写代码的时候一定不要忘记该约定的标识。

WinMain 和 wWinMain 默认是等价的,除非命令行参数是一个 ANSI 字符串,Unicode 版本永远是首选。如果是 Unicode 版本,你可以传递一个 ANSI 字符串,反之则不行。

你可以通过 GetCommandLine 函数获取命令行参数,这个函数返回一个单一的字符串。如果你想获取参数的数组样式,可以通过 CommandLineToArgvW 函数完成。

编译器是怎样知道调用 wWinMain 还是调用标准的 main 函数?

实际情况下,在微软的 C 运行时库(CRT)中提供了一个 main 函数实现,其内部会调用 WinMain 或者 wWinMain。

CRT 中 main 函数的内部在调用 wWinMain 之前做了一些额外的工作,例如初始化一些静态成员或者其它 C 函数的初始化操作等等。虽然你可以手动指定不同的链接不同的入口函数,但是仍然推荐你使用 CRT 默认提供的入口点函数,否则 CRT 内部的一些代码将被会跳过,有可能会导致一些异常的结果。

下面是一个空的 WinMain 函数。

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR lpCmdLine, INT nCmdShow)
{
    return 0;
}

上面已经介绍了一些 Windows 开发的基本知识,接下来就准备创建我们的第一个 Windows 程序。