Jos*_*ada 6 windows winapi ffi rust
我正在尝试学习如何使用原始 Win32 API,并且正在按照此处的教程进行操作,但我无法弄清楚如何传递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) {}
Run Code Online (Sandbox Code Playgroud)
进入 Rust 程序,也许是这样的:
fn main(/* args, such as hInstance, nShowCmd here /*) {
}
Run Code Online (Sandbox Code Playgroud)
或者,更可能的方式:
fn main() {
std::env::/* hopefully something like args() /*;
}
Run Code Online (Sandbox Code Playgroud)
我尝试获取 args,但这只是传递了我用来生成程序的命令行参数,就像 args[0] 是程序的名称一样,这是预期的行为。此外,调用 args_os() 也会得到相同的结果。
我也尝试过设置 Windows 子系统,但之前的行为是相同的,而不是所需的行为......
#![windows_subsystem = "windows"]
Run Code Online (Sandbox Code Playgroud)
我可以通过手动调用并传入空指针来获取 hInstance 句柄GetModuleHandle(),但不知道如何手动获取 nShowCmd 。
我正在使用windows crate,这就是我想要使用的。
对于这个难以捉摸的谜团的任何帮助将不胜感激!
PS 我的窗口确实打开了,一切都正常,正如预期的那样,包括与 FFI 合作,以及其中涉及的所有疯狂内容,哈哈。但我只是想了解这是如何完成的。没有 nShowCmd 也可以过得去,但我真的很想能够理解 Rust 是如何做到这一点的。我也无法覆盖fn main()函数签名,所以不知道如何去做。
WinMain是用户提供的 Windows 应用程序入口点。操作系统看到的原始应用程序入口点要简单得多:
DWORD CALLBACK RawEntryPoint(void);
Run Code Online (Sandbox Code Playgroud)
现在由语言支持库来恢复启动信息并调用用户提供的入口点(有关详细信息,请参阅WinMain 只是 Win32 进程入口点的常规名称):
GetModuleHandle(NULL)为了hInstancehPrevInstance始终NULL在 32 位和 64 位 Windows 中GetCommandLine对于传递给程序的未解析的命令行GetStartupInfo获取大量状态信息,包括wShowWindow对应的nCmdShow如果您安装了 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,)
}
Run Code Online (Sandbox Code Playgroud)
// 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);
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以访问与编译 C 或 C++ 时nCmdShow传入的参数相对应的值WinMain(至少是粗略的)。理想情况下,您需要查看是否dwFlags包含该STARTF_USESHOWWINDOW位,并在不包含时创建合理的默认值。
也就是说,我什至不确定这个nCmdShow论点的目的是什么WinMain。正如下面所解释的ShowWindow,当该值是根据调用者提供的信息填充时,使用该值不会产生任何效果。
从版本 0.22.1 开始,Windows crate 附带了预构建的绑定,使得使用 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"]
Run Code Online (Sandbox Code Playgroud)
主程序.rs
[package]
name = "startup_info"
version = "0.0.0"
edition = "2021"
[dependencies.windows]
version = "0.22.1"
features = ["Win32_Foundation", "Win32_System_Threading"]
Run Code Online (Sandbox Code Playgroud)
Kernel32中有这个函数:GetStartupInfo()在windows-rs中似乎映射到bindings::Windows::Win32::System::Threading::GetStartupInfoW.
该函数填充一个STARTUPINFOW结构,该结构在许多有用字段之间WORD wShowWindow具有与 中最后一个参数相同的值WinMain()。
有趣的WinMain是,它没有什么神奇之处,它只是真正的入口点函数,WinMainCRTStartup从 CRT 初始化代码中调用的函数。通过查看等效的Wine 源代码,您可以了解它是如何工作的。在那里您可以看到您打电话GetModuleHandle(NULL)获取的想法hInstance是正确的。