如何从 Rust 中的 WinMain 或 wWinMain 获取参数?

How to get args from WinMain or wWinMain in Rust?

我正在尝试学习如何使用原始 Win32 API,并且正在按照教程 here 进行操作,但我无法弄清楚如何通过 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) 函数签名起作用。我知道 int WINAPI 不是必需的...但是我如何让所有这些参数传递给 WinAPI 调用?特别是 hInstance 和 nCmdShow?

我的目标

获取hInstance和nShowCmd
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {}

进入 Rust 程序,可能类似于:

fn main(/* args, such as hInstance, nShowCmd here /*) {
}

或者,更可能的方式:

fn main() {
    std::env::/* hopefully something like args() /*;
}

我试过的

我试过获取 args,但这只是传递了我用来生成程序的命令行参数,就像 args[0] 是程序的名称,即 预期行为。此外,调用 args_os() 会得到相同的结果。

我也试过设置 windows 子系统,但之前的行为是一样的,不是期望的行为...

#![windows_subsystem = "windows"]

am能够通过手动调用GetModuleHandle()并传入一个空指针来获取hInstance句柄,但是有不知道如何手动获取 nShowCmd。

重要提示

我正在使用 windows crate,这是我想要使用的。

任何对这个难以捉摸的谜团的帮助都会非常感激!

P.S。我的 window 确实打开了,并且一切正常,正如预期的那样,包括与 FFI 合作,以及那里涉及的所有疯狂行为,哈哈。但我只想了解这是如何完成的。没有 nShowCmd 也能过关,但我真的很想了解这是如何在 Rust 中完成的。我也无法覆盖 fn main() 函数签名,所以不知道该怎么做。

Kernel32中有这个函数:GetStartupInfo() that in windows-rs seems to be mapped to bindings::Windows::Win32::System::Threading::GetStartupInfoW.

此函数填充一个 STARTUPINFOW 结构,在许多有用的字段之间,WORD wShowWindowWinMain() 中的最后一个参数具有相同的值。

有趣的是WinMain里面没有什么神奇的东西,它只是真正的入口点函数WinMainCRTStartup从CRT初始化代码中调用的函数。您可以通过查看等效的 Wine source code 来了解它是如何做的。在那里你可以看到你调用 GetModuleHandle(NULL) 来获得 hInstance 的想法是正确的。

WinMain 是用户提供的 Windows 应用程序入口点。 OS 看到的原始应用程序入口点要简单得多:

DWORD CALLBACK RawEntryPoint(void);

现在由语言支持库来恢复启动信息并调用用户提供的入口点(有关详细信息,请参阅 WinMain is just the conventional name for the Win32 process entry point):

如果您安装了 Visual Studio,您可以查看 exe_common.inl 以了解 C 和 C++ 支持库如何处理此问题。

不幸的是,对于 Rust,事情变得更加复杂。尽管编译器和链接器重新利用 MSVC 的 CRT 实现来负责提取将传递给 WinMain 的信息,但我不知道有什么方法可以从 Rust 中获取它。

您将不得不手动恢复该信息。获取 nCmdShow 参数有点复杂,所以让我们在这里说明一下:

// build.rs
// Using windows-rs 0.17.2; version 0.10.0 and later should be just fine
fn main() {
    windows::build!(Windows::Win32::System::Threading::GetStartupInfoW,)
}
// src/main.rs
mod bindings {
    windows::include_bindings!();
}

use bindings::Windows::Win32::System::Threading::{GetStartupInfoW, STARTUPINFOW};

fn main() {
    let mut si = STARTUPINFOW {
        cb: std::mem::size_of::<STARTUPINFOW>() as u32,
        ..Default::default()
    };
    unsafe { GetStartupInfoW(&mut si) };
    let cmd_show = si.wShowWindow as i32;

    println!("nCmdShow: {:?}", cmd_show);
}

有了它,您现在可以访问与在为 C 或 C++ 编译时传递给 WinMainnCmdShow 参数对应的值(无论如何,大致)。理想情况下,您需要查看 dwFlags 是否包含 STARTF_USESHOWWINDOW 位,并在不存在时制作一个合理的默认值。

就是说,我什至不确定 nCmdShow 参数传递给 WinMain 的目的是什么。正如 ShowWindow 中所解释的,当它是从调用者提供的信息中填充时,使用该值没有任何效果。

2021-10-28更新

windows crate comes with pre-built bindings 版本 0.22.1 开始,使用 Windows API 变得更加容易。下面使用预构建绑定代替编译时代码生成来实现相同的程序。

Cargo.toml

[package]
name = "startup_info"
version = "0.0.0"
edition = "2021"

[dependencies.windows]
version = "0.22.1"
features = ["Win32_Foundation", "Win32_System_Threading"]

main.rs

use windows::Win32::System::Threading::{GetStartupInfoW, STARTUPINFOW};

fn main() {
    let mut si = STARTUPINFOW {
        cb: std::mem::size_of::<STARTUPINFOW>() as u32,
        ..Default::default()
    };
    unsafe { GetStartupInfoW(&mut si) };
    let cmd_show = si.wShowWindow as i32;

    println!("nCmdShow: {:?}", cmd_show);
}