Reputation: 3271
This appears to partially work but I cannot get the string value to print
pub fn test() {
let mut buf: Vec<u16> = vec![0; 64];
let mut sz: DWORD = 0;
unsafe {
advapi32::GetUserNameW(buf.as_mut_ptr(), &mut sz);
}
let str1 = OsString::from_wide(&buf).into_string().unwrap();
println!("Here: {} {}", sz, str1);
}
Prints:
Here: 10
When I expect it to also print
Here: 10 <username>
As a test, the C version
TCHAR buf[100];
DWORD sz;
GetUserName(buf, &sz);
seems to populate buf
fine.
Upvotes: 1
Views: 2211
Reputation: 431529
GetUserName
You should re-read the API documentation for GetUserName
to recall how the arguments work:
lpnSize [in, out]
On input, this variable specifies the size of the
lpBuffer
buffer, inTCHAR
s. On output, the variable receives the number ofTCHAR
s copied to the buffer, including the terminating null character. IflpBuffer
is too small, the function fails andGetLastError
returnsERROR_INSUFFICIENT_BUFFER
. This parameter receives the required buffer size, including the terminating null character.
TL;DR:
This has a fixed-size stack-allocated array of 100 TCHAR
s.
This code is broken and unsafe because sz
is uninitialized. This allows the API to write an undefined number of characters to a buffer that's only 100 long. If the username is over 100 characters, you've just introduced a security hole into your program.
The Rust code is broken in a much better way. sz
is set to zero, which means "you may write zero entries of data", so it writes zero entries. Thus, the Vec
buffer is full of zeros and the resulting string is empty. The buffer is reported too small to receive the username, so GetUserNameW
sets sz
to the number of characters that the buffer needs to have allocated.
One "fix" would be to set sz
to the length of your array. However, this is likely to have over- or under-allocated the buffer.
If you are ok with a truncated string (and I'm not sure if TCHAR
strings can be split arbitrarily, I know UTF-8 cannot), then it would be better to use a fixed-size array like the C code.
If you want to more appropriately allocate memory to call this type of WinAPI function, see What is the right way to allocate data to pass to an FFI call?.
extern crate advapi32;
extern crate winapi;
use std::ptr;
fn get_user_name() -> String {
unsafe {
let mut size = 0;
let retval = advapi32::GetUserNameW(ptr::null_mut(), &mut size);
assert_eq!(retval, 0, "Should have failed");
let mut username = Vec::with_capacity(size as usize);
let retval = advapi32::GetUserNameW(username.as_mut_ptr(), &mut size);
assert_ne!(retval, 0, "Perform better error handling");
assert!((size as usize) <= username.capacity());
username.set_len(size as usize);
// Beware: This leaves the trailing NUL character in the final string,
// you may want to remove it!
String::from_utf16(&username).unwrap()
}
}
fn main() {
println!("{:?}", get_user_name()); // "IEUser\u{0}"
}
Upvotes: 10