Rust WINAPI outputting HMODULE value - winapi

I am trying to write a simle rust console application that calls into WINAPI.
I will ommit the obvious use and extern crate parts of my code. Here it is:
fn win32_string(value : &str ) -> Vec<u16> {
OsStr::new( value ).encode_wide().chain( once( 0 ) ).collect()
}
fn main() {
println!("===== Rust Windows experiment #1 =====");
let module_name = win32_string("ntdll.dll");
let h_instance: HMODULE;
unsafe {
h_instance = GetModuleHandleW(module_name.as_ptr());
}
println!("Value of h_instance: {:#?}", h_instance);
}
I am building it against the target triple:
[build]
target = "i686-pc-windows-msvc"
As you can see I am targeting to build a 32 bit application. Now the output of my program is the following:
===== Rust Windows experiment #1 =====
Value of h_instance: 0x00007ffb61c40000
It is showing a 64 bit address. How can this happen? Am I writing the HMODULE value wrongly to the console, or what am I doing wrong?

With help from rodrigo: The build tag is supposed to be included in the .cargo\config file instead of the cargo.toml file, in order for the compiler to pick it up. :) After changing that the issue is now fixed. :)

Related

How to get args from WinMain or wWinMain in Rust?

I am trying to learn how to work with raw Win32 API's and am following the tutorial here, but cannot, for the life of me, figure out how to pass int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) function signature to work. I do understand the int WINAPI isn't needed...but how do I get all of those parameters to pass to WinAPI calls? Especially hInstance and nCmdShow?
My Goal
Get hInstance and nShowCmd from
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {}
into a Rust program, perhaps something like:
fn main(/* args, such as hInstance, nShowCmd here /*) {
}
or, the more likely way:
fn main() {
std::env::/* hopefully something like args() /*;
}
What I've tried
I've tried getting the args, but this just passes the command-line arguments that I used to spawn the program, just as args[0] being the name of the program, which is the expected behavior. Also, calling args_os() gives the same result.
I've also tried to set the windows subsystem, but the previous behavior is the same, not the desired behavior...
#![windows_subsystem = "windows"]
I am able to get the hInstance handle by manually calling GetModuleHandle() and passing in a null pointer, but have no idea how to get the nShowCmd manually.
Important Note
I am using the windows crate, which is what I would like to use.
Any help to this eluding mystery would be much appreciated!
P.S. My window does open, and everything works fine, as expected, including working with FFI, and all the crazies involved there, lol. But I just would like to understand how this is done. One can get by without the nShowCmd, but I would really like to be able to understand how this is done in rust. I also cannot overwrite the fn main() function signature, so not sure how to go about it.
WinMain is the user-provided entry point of a Windows application. The raw application entry point as seen by the OS is far simpler:
DWORD CALLBACK RawEntryPoint(void);
It is now up to language support libraries to recover the startup information and call into the user-provided entry point (see WinMain is just the conventional name for the Win32 process entry point for details):
GetModuleHandle(NULL) for hInstance
hPrevInstance is always NULL in 32-bit and 64-bit Windows
GetCommandLine for the unparsed command line passed to the program
GetStartupInfo for lots of state information, including the wShowWindow that corresponds to nCmdShow
If you have Visual Studio installed you can have a look inside exe_common.inl to see how the C and C++ support libraries go about this.
With Rust things are unfortunately more complex. Even though the compiler and linker repurpose MSVC's CRT implementation responsible for extracting the information that would be passed into WinMain, I'm not aware of a way to get a hold of this from Rust.
You're going to have to recover that information manually. Getting to the nCmdShow parameter is a bit more involved, so let's illustrate that here:
// 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);
}
With that you now have access to a value that corresponds to the nCmdShow parameter passed into WinMain when compiling for C or C++ (roughly, anyway). Ideally you would need to see whether dwFlags contains the STARTF_USESHOWWINDOW bit, and fabricate a reasonable default when it doesn't.
That said, I'm not even sure what purpose the nCmdShow argument passed into WinMain serves. As explained under ShowWindow, using that value doesn't have any effect when it is populated from caller-provided information.
Update 2021-10-28
Starting with version 0.22.1 the windows crate comes with pre-built bindings, making it a lot easier to consume the Windows API. The following implements the same program using the pre-built bindings in place of compile-time code generation.
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);
}
There is this function in Kernel32: GetStartupInfo() that in windows-rs seems to be mapped to bindings::Windows::Win32::System::Threading::GetStartupInfoW.
This function fills a STARTUPINFOW structure that has, between a lot of useful fields, WORD wShowWindow that has the same value as the last argument in WinMain().
The funny thing about WinMain is that there is nothing magic in it, it is just the function that the real entry point function, WinMainCRTStartup calls from the CRT initialization code. You can get an idea of how it does its thing by looking at the equivalent Wine source code. There you can see that your idea of calling GetModuleHandle(NULL) to get the hInstance is the right one.

How to understand IN DEPTH gcc compiler?

Background of this question : I am trying to understand how compilers work. I learn many new things : scanner, parser, AST, IR, optimisation, frontend, backend,LL(1), ... I made gradual progress and it is very interesting. Now, I would like to do some practical works.
From a programmer point of view, I know why typedef struct { int x; mytype* next; } mytype; does not compile and I know the correct syntax typedef struct mystruct { int x; struct mystruct* next; } mytype; but I would like to know where the problem happens EXACTLY during compilation. I am using gcc, I would like to know how is it posible to use gcc developper options -fdump-... to answer this question.
The first step of the GCC compiler work is parser
c-parser.c
It parse your c or c++ or some else code into gimple representation:
Parse -> Gimplify -> Tree -> SSA -> Optimize -> Generate -> RTL -> Optimize RTL Generate -> ASM
Errors can be found, for example, in terminal, or in IDE in error output like next:
gcc yourcode.c
yourcode.c:2:25: error: unknown type name 'mytype'
typedef struct { int x; mytype* next; } mytype;
^~~~~~
You also can look at how it works via a
link
Sorry for my English.

Create a popup in Rust using Windows APIs

I would like to create a dialog box or popup in Rust in a similar fashion to PowerShell.
I'm struggling to find documentation on the Windows APIs for this. The closest I've found so far is the CreateDialogA macro (Rust winapi equivalent).
I've found some things on it such as Creating a New Dialog Box (C++), but most of it is not applicable to a Rust project.
Perhaps dialoge template is relevant?
main.rs
extern crate winapi;
use std::ptr::null_mut as NULL;
use winapi::um::winuser;
fn main() {
let l_msg: Vec<u16> = "Wassa wassa wassup\0".encode_utf16().collect();
let l_title: Vec<u16> = "\u{20BF}itconnect\0".encode_utf16().collect();
unsafe {
winuser::MessageBoxW(NULL(), l_msg.as_ptr(), l_title.as_ptr(), winuser::MB_OK | winuser::MB_ICONINFORMATION);
}
}
This uses the MessageBoxW function.
The argument winuser::MB_OK can be winuser::MB_OK, winuser::MB_OKCANCEL, winuser::MB_ABORTRETRYIGNORE, winuser::MB_YESNOCANCEL, winuser::MB_YESNO, winuser::MB_RETRYCANCEL or winuser::MB_CANCELTRYCONTINUE.
The argument winuser::MB_ICONINFORMATION can be winuser::MB_ICONHAND, winuser::MB_ICONQUESTION, winuser:: MB_ICONEXCLAMATION or winuser::MB_ICONASTERISK.
Cargo.toml should include:
[dependencies.winapi]
version = "0.3"
features = ["winuser"]

Statically linking on 32 bit Windows incorrectly adds a leading underscore

I am trying to build a Rust library which can use the Opus codec. When compiling for x86_64-pc-windows-msvc everything works as expected. However when I compile for i686-pc-windows-msvc I get errors about unresolved externals. e.g.
error LNK2001: unresolved external symbol _opus_encoder_create
Obviously this is failing because it's looking for the wrong name! There shouldn't be a leading underscore there. My Rust import looks like:
extern "C" {
pub fn opus_encoder_create(fs:i32, chan:i32, app:i32, err:*mut i32) -> *mut OpusEncoder;
}
It looks like Rust is automatically inserting the underscore at the start. Running dumpbin on both the 32 bit and 64 bit lib files (built in Visual Studio) gets me:
32 bit:
> 7202A opus_encoder_create
64 bit:
> 7202A opus_encoder_create
No underscores in sight!
What am I doing wrong? How do I properly import and call these functions from Rust?
The Rust build script which imports the dependencies will need some logic to do different work for different platforms. e.g.
extern crate target_build_utils;
use target_build_utils::TargetInfo;
fn main() {
let arch = TargetInfo::new().expect("could not get target info").target_arch();
match platform {
"x86" => { /* import 32 bit dependencies */
"x86_64" => { /* import 64 bit dependencies */
}
}
My original script took the following approach (caution, this does not work):
fn main() {
add_deps();
}
#[cfg(target_arch = "x86")]
fn add_deps() {
/* import 32 bit dependencies */
}
#[cfg(target_arch = "x86_64")]
fn add_deps() {
/* import 64 bit dependencies */
}
However these cfg attributes are for the platform the build script is building for, not the platform the actual code itself is building for (this is actually fairly obvious in retrospect)!

Why does Rust export its whole standard library when building a DLL?

I'm currently trying to write a dynamic library with Rust which will be loaded from a existing program. I need to export a few functions with specific names and calling conventions. Everything works, but as soon as I use anything from the standard library:
The DLL size balloons to over 3MiB (Not exactly pretty, but I could live with that)
The whole standard library gets exported from the DLL. Here is a lists with all exports: http://pastebin.com/LsG1u96C (5100 functions)
Am I missing some compiler switch?
I compile the following code with rustc without any options:
#![crate_type = "dylib"]
#![feature(std_misc)]
use std::ffi::CString;
#[link(name = "user32")]
#[allow(non_snake_case)]
extern "stdcall" {
fn MessageBoxA(hWnd: u32, lpText: *const i8, lpCaption: *const i8, uType: u32) -> u32;
}
#[no_mangle]
#[allow(non_snake_case)]
pub unsafe extern "stdcall" fn _AddLuaState(lua_state_ptr: u32)
{
let info_str = format!("Lua State Created: {}!", lua_state_ptr);
let info_cstring = CString::new(info_str).unwrap();
let caption = CString::new("Hello from my Rust Library!").unwrap();
MessageBoxA(0, info_cstring.as_ptr(), caption.as_ptr(), 0);
}
_AddLuaState#4 is the only function that should be exported.
This is on a Windows 8.1 machine with rustc 1.0.0-nightly (522d09dfe 2015-02-19) (x86)
Update: It looks like when compiling a dynamically linked file with rustc -C prefer-dynamic, the DLL size shrinks to 60kiB and there are only 3 extra exports (http://pastebin.com/G0AYZrpF) which all look quite reasonable. But I'd still prefer a statically linked library.
Recently the new crate type "cdylib" has been added that likely better fits your use-case. Replace the first line of your source file with:
#![crate_type = "cdylib"]
When using the Cargo package manager instead of directly calling rustc update Cargo.toml to contain the following lines:
[lib]
crate-type = ["cdylib"]
For more details have a look at Rust pull request #33553.
In my test it decreased the size of the following simple "Hello World" DLL from 650k (dylib) down to 8k (cdylib). Also the number of exported symbols is decreased massively.
#[no_mangle]
pub extern fn hello_rust() -> *const u8 {
"Hello, world!\0".as_ptr()
}

Resources