Winmain函数如何作为C语言程序入口?

99ANYc3cd6
预计阅读时长 19 分钟
位置: 首页 C语言 正文

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

winmain c语言
(图片来源网络,侵删)

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: LPWSTRLPSTR)

  • 全称: Pointer to a Command-Line string(命令行字符串指针)。
  • 作用: 它是一个指向程序的命令行参数的字符串,与 main 函数的 argcargv 不同,这里是一个完整的字符串。
  • 示例:
    • 如果你的程序运行命令是 MyApp.exe "file 1.txt" -vlpCmdLine 的值就是 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

winmain c语言
(图片来源网络,侵删)
#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);
    }
}

WinMainmain 的对比

特性 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 编程至关重要。

-- 展开阅读全文 --
头像
C语言如何调用Mplayer播放音频视频?
« 上一篇 04-16
dede首页修改步骤是什么?
下一篇 » 04-16

相关文章

取消
微信二维码
支付宝二维码

目录[+]