-1

I'm struggling with calling the wide-char version of GetUserName. The UTF-8 call works, though there might be a better way to do this:

#[cfg(windows)] extern crate advapi32;
#[cfg(windows)] extern crate winapi;

use std::ffi::CString;

fn get_user_name() -> String {
    let mut sz: u32 = 64;
    unsafe {
        let mut username = CString::from_vec_unchecked(vec![0; sz as usize]);
        let ptr = username.into_raw();
        advapi32::GetUserNameA(ptr as *mut i8, &mut sz as *mut u32);
        CString::from_raw(ptr).to_str().unwrap().to_owned()
    }
}

Then I tried the UTF-16 version. This works but looks wrong as I have to create this blank string for OsStr::new():

fn get_user_name() -> String {
    // for windows
    use std::ffi::OsStr;
    let mut sz: u32 = 64;
    let mut username = OsStr::new("                            ")
        .encode_wide()
        .collect::<Vec<u16>>();
    unsafe {
        advapi32::GetUserNameW(username.as_mut_ptr() as *mut u16, &mut sz as *mut u32);
    }
    String::from_utf16(&username[..sz as usize]).unwrap()
}

I also tried using OsString::with_capacity(), but this crashes:

fn get_user_name() -> String {
    // for windows
    use std::ffi::OsStr;
    let mut sz: u32 = 64;
    let mut username = OsString::with_capacity(sz as usize)
        .as_os_str()
        .encode_wide()
        .collect::<Vec<u16>>();
    unsafe {
        advapi32::GetUserNameW(username.as_mut_ptr() as *mut u16, &mut sz as *mut u32);
    }
    String::from_utf16(&username[..sz as usize]).unwrap()
}

There are other answers on Stack Overflow where Rust creates a string to pass into a function (e.g. MessageBoxW), but none where the size is determined by Windows and Rust has to allocate space to be populated later.

What is the correct way of calling a wide char function on Windows where Rust has to preallocate a buffer and then convert back (eventually) to a standard UTF-8 string?

gilbertbw
  • 634
  • 2
  • 9
  • 27
Delta_Fore
  • 3,079
  • 4
  • 26
  • 46
  • 2
    I'd pass the buffers directly from `Vec` / `Vec` allocations (or arrays on the stack...); convert them to strings **after** they were filled. – Stefan Nov 28 '17 at 11:50
  • 2
    @Ronnie Please post the answer as a proper answer, thanks. – kennytm Nov 28 '17 at 13:19
  • 1
    **You** asked [Calling the GetUserName WinAPI function with a mutable string doesn't populate the string](https://stackoverflow.com/q/44709780/155423), thus one would expect you to remember the answer. – Shepmaster Nov 28 '17 at 15:09
  • @Shepmaster For some reason didn't appear in the search. Please delete this question if you wish. Thanks – Delta_Fore Nov 28 '17 at 16:56

1 Answers1

0

From comments, using a Vec directly is the simplest approach.

fn get_user_name() -> String {
    // for windows
    let mut sz: u32 = 64;
    let mut username: Vec<u16> = vec![0; sz as usize];
    unsafe {
        advapi32::GetUserNameW(username.as_mut_ptr() as *mut u16, &mut sz as *mut u32);
    }
    String::from_utf16(&username[..sz as usize]).unwrap()
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Delta_Fore
  • 3,079
  • 4
  • 26
  • 46