#windows #winapi #rust #ffi
Вопрос:
Я пытаюсь научиться работать с необработанными Win32 API и следую приведенному здесь руководству, но ни за что на свете не могу понять, как передать int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
сигнатуру функции для работы. Я понимаю int WINAPI
, что это не нужно…но как мне передать все эти параметры вызовам WinAPI? Особенно помехи и 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[0] является именем программы, что является ожидаемым поведением. Кроме того, вызов args_os() дает тот же результат.
Я также попытался настроить подсистему Windows, но предыдущее поведение такое же, а не желаемое…
#![windows_subsystem = "windows"]
Я могу получить дескриптор помехи, вручную вызвав GetModuleHandle()
и передав нулевой указатель, но понятия не имею, как получить nShowCmd вручную.
Важное Примечание
Я использую ящик для окон, который я хотел бы использовать.
Любая помощь в этой ускользающей тайне была бы очень признательна!
P.S. Мое окно действительно открывается, и все работает нормально, как и ожидалось, включая работу с FFI и всех тех сумасшедших, которые там замешаны, лол. Но я просто хотел бы понять, как это делается. Можно обойтись и без nShowCmd, но мне бы очень хотелось понять, как это делается в rust. Я также не могу перезаписать fn main()
подпись функции, поэтому не знаю, как это сделать.
Ответ №1:
WinMain
является предоставляемой пользователем точкой входа в приложение Windows. Исходная точка входа в приложение, как видно из операционной системы, намного проще:
DWORD CALLBACK RawEntryPoint(void);
Теперь библиотеки языковой поддержки должны восстановить информацию о запуске и позвонить в предоставленную пользователем точку входа (подробнее см. WinMain-это обычное имя точки входа процесса Win32).:
GetModuleHandle(NULL)
дляhInstance
hPrevInstance
всегда находитсяNULL
в 32-разрядной и 64-разрядной WindowsGetCommandLine
для непространенной командной строки, переданной в программуGetStartupInfo
для получения большого количества государственной информации,wShowWindow
в том числе соответствующейnCmdShow
Если у вас установлена Visual Studio, вы можете заглянуть в exe_common.inl, чтобы посмотреть, как библиотеки поддержки C и C справляются с этим.
С ржавчиной, к сожалению, все сложнее. Несмотря на то , что компилятор и компоновщик перепрофилировали реализацию CRT MSVC, ответственную за извлечение информации, которая будет передана 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(amp;mut si) };
let cmd_show = si.wShowWindow as i32;
println!("nCmdShow: {:?}", cmd_show);
}
Теперь у вас есть доступ к значению, соответствующему nCmdShow
параметру, переданному WinMain
при компиляции для C или C (во всяком случае, примерно). В идеале вам нужно было бы посмотреть dwFlags
, содержит ли STARTF_USESHOWWINDOW
бит, и выдумать разумное значение по умолчанию, если это не так.
Тем не менее, я даже не уверен, какой цели nCmdShow
WinMain
служит переданный аргумент. Как объяснено ниже ShowWindow
, использование этого значения не имеет никакого эффекта, когда оно заполняется из информации, предоставленной вызывающим абонентом.
Обновление 2021-10-28
Начиная с версии 0.22.1, ящик для Windows поставляется с предварительно встроенными привязками, что значительно упрощает использование API Windows. Следующее реализует ту же программу, используя предварительно построенные привязки вместо генерации кода во время компиляции.
Груз.томл
[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(amp;mut si) };
let cmd_show = si.wShowWindow as i32;
println!("nCmdShow: {:?}", cmd_show);
}
Комментарии:
1. Насколько я понимаю, один из способов
nCmdShow
полезен , когда вы создаете ярлык для anexe
, вы можете выбрать, как должно начинаться окно, если оно свернуто, нормально и т. Д.2. @jos Конечно, это возможно. Но для этого вам не нужен доступ к аргументу в вашем коде. При первом вызове
ShowWindow()
флаги , которые вы передаете, игнорируются, аSTARTUPINFO
вместо них используются флаги из списка. Вы можете просто пройтиSW_SHOW
и все равно заставить систему применить настройки, сохраненные в ярлыке.
Ответ №2:
В Kernel32 есть эта функция: GetStartupInfo()
кажется, что в Windows-rs она сопоставлена bindings::Windows::Win32::System::Threading::GetStartupInfoW
.
Эта функция заполняет STARTUPINFOW
структуру, в которой между множеством полезных полей WORD wShowWindow
есть то же значение, что и в последнем аргументе WinMain()
.
Забавно то WinMain
, что в этом нет ничего волшебного, это просто функция, которую реальная функция точки входа WinMainCRTStartup
вызывает из кода инициализации CRT. Вы можете получить представление о том, как он делает свое дело, посмотрев на эквивалентный исходный код Wine. Там вы можете увидеть, что ваша идея позвонить GetModuleHandle(NULL)
, чтобы получить hInstance
это, правильная.