Long-winded ffi conversion on windows

Hello,

I have started using some ffi on windows to see how it all works. It was all going reasonably well, until I wanted to construct a string to send to the the OS. In this case below, a string is being assigned to a windows structure, that will later be passed into a windows function call:

let class_name_os = OsStr::new(class_name).encode_wide().chain(Some(0)
.into_iter()).collect::<Vec<_>>().as_ptr();
wc.lpszClassName = class_name_os;

I found the conversion “incantation” above on the Web after trying to work out the correct way myself, with no obvous anwser. I note that is quite long and tedious for what it does. class_name_os is a windows style utf16 zero terminated string. I was hoping, and expected a single function to do the same work but cannot seem to find one.

Is there another way to do this? If not, is there a proposal to make this a bit more utilitarian, if not is there a rationale I can read about why not.

Thanks,

Jason.

If you need to convert a string to a Vec<u16> it might be best to use your own function, like so:

fn to_wide(s: &str) -> Vec<u16> {
    use std::iter;
    s.encode_utf16() // Make a UTF-16 iterator
     .chain(iter::once(0)) // Append a null
     .collect() // Collect the iterator into a vector
}

The only thing that adds complexity is adding the null. A simpler function without it would look less magical:

fn to_wide(s: &str) -> Vec<u16> {
    s.encode_utf16().collect()
}

Alas many WinApi functions require the null so you need some way to ensure it’s there.

I see what is required, but I don’t understand why the standard ffi libraries doesn’t seem provide a to_nul_string() helper sort of method. It seems to be a desparitly common thing to want to do. Otherwise eveyone and his dog will have to implement it themselfs.

std does provide such a helper. It’s called CString. But as you already know, Windows is expecting 16-bit strings instead of 8-bit ones so that type doesn’t help in your situation.

There is also an encode_wide method that’s available on Windows targets that might be useful for you.

There’s also the widestring crate that seems reasonably popular.

Anecdotally, the README for winapi (the de-facto standard crate for doing Windows FFI in Rust) uses the same method of converting the string as in @chrisd’s post:

let wide: Vec<u16> = OsStr::new(msg).encode_wide().chain(once(0)).collect();

My question, more directly perhaps then, is why doesn’t the trait std::os::windows::ffi::OsStrExt define a function called something like:

to_wchar_with_nul(&self) -> &[WCHAR]

to do exactly this job? Would it not be helpful to have such a function, or am I miss- understanding something?

The issue is that Rust’s standard library likes to be as small as possible and to leave much up to crates on crates.io. As this is a very Windows specific issue, it’s harder to argue for better Windows string support to be built-in. Though it’s not impossible.

But in the mean time there are crates for this. widestring looks to be the most popular but I can’t vouch for it myself.

I feel it is a little bit of a thin argument in this paricular case. However, I can copy & paste the conversion functions into projects that need it, or alternativly use the widestring create.

Thank you all for the explanations.

let class_name_os = OsStr::new(class_name).encode_wide().chain(Some(0)
.into_iter()).collect::<Vec<_>>().as_ptr();
wc.lpszClassName = class_name_os;

This is wrong as you never store the Vec in a variable so it gets dropped and the pointer is left dangling.

1 Like