I am trying to set the Windows background in Rust using the winapi crate and SystemParametersInfo
, but it sets the background to black. In C++, that usually means that pvParam
isn't passed correctly or it has the wrong type.
What's wrong?
#[cfg(windows)]
extern crate winapi;
use winapi::ctypes::c_void;
use winapi::um::winuser::{SystemParametersInfoA, SPIF_UPDATEINIFILE, SPI_SETDESKWALLPAPER};
fn main() {
let mut image_path = "Path to Image";
let image_path_c_ptr: *mut c_void = &mut image_path as *mut _ as *mut c_void;
unsafe {
SystemParametersInfoA(
SPI_SETDESKWALLPAPER,
0,
image_path_c_ptr,
SPIF_UPDATEINIFILE,
);
}
}
Rust strings are not C strings. You should instead use CString
to interface with C code:
use std::ffi::CString;
// use ...
fn main() {
let mut image_path = CString::new("Path to Image").unwrap();
unsafe {
SystemParametersInfoA(
SPI_SETDESKWALLPAPER,
0,
image_path.as_ptr() as *mut c_void,
SPIF_UPDATEINIFILE,
);
}
}
To elaborate: image_path
is a &str
(a fat pointer). By taking a mutable reference to it you are getting a &mut &str
. You then pass it to C, which will dereference the pointer and get a &str
.
But C code does not know how to deal with a Rust type: it is only aware of C strings and instead expects a pointer to the first byte. It also expects the string to be NUL
terminated, which Rust strings are not. Thus it makes no sense to pass a Rust &str
to C code in this case and this is exactly the reason CStr
and CString
exist.