【问题标题】:How to get args from WinMain or wWinMain in Rust?如何从 Rust 中的 WinMain 或 wWinMain 获取 args?
【发布时间】:2021-07-09 19:49:21
【问题描述】:

我正在尝试学习如何使用原始 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] 是程序的名称,这是 预期的 em> 行为。此外,调用 args_os() 会得到相同的结果。

我也试过设置windows子系统,但是之前的行为是一样的,不是想要的行为……

#![windows_subsystem = "windows"]

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

重要提示

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

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

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

【问题讨论】:

    标签: windows winapi rust ffi


    【解决方案1】:

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

    DWORD CALLBACK RawEntryPoint(void);
    

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

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

    不幸的是,对于 Rust,事情变得更加复杂。尽管编译器和链接器重新利用了负责提取将传递到 WinMain 的信息的 MSVC 的 CRT 实现,但我不知道有什么方法可以从 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 位,并在不包含时编造一个合理的默认值。

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

    2021 年 10 月 28 日更新

    从 0.22.1 版本开始,windows crate 附带了pre-built bindings,这使得使用 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);
    }
    

    【讨论】:

    • 按照我的理解,nCmdShow 在创建exe 的快捷方式时很有用,您可以选择窗口应如何启动,最小化、正常等。
    • @jos 当然,这是可能的。但要使其正常工作,您不需要访问代码中的参数。第一次调用ShowWindow() 时,您传入的标志将被忽略,而使用STARTUPINFO 中的标志。您可以简单地传递SW_SHOW 并仍然让系统应用存储在快捷方式中的设置。
    【解决方案2】:

    Kernel32中有这个函数:GetStartupInfo()在windows-rs中好像映射到bindings::Windows::Win32::System::Threading::GetStartupInfoW

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

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

    【讨论】:

      猜你喜欢
      • 2010-12-20
      • 1970-01-01
      • 2016-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-29
      • 2023-02-23
      • 2022-01-28
      相关资源
      最近更新 更多