如何在 Win32 函数成功后从 PWSTR 检索字符串?

How do I retrieve a string from a PWSTR after a Win32 function succeeds?

我想使用 windows-rs crate 在 std::String 中获取我的用户名。

use bindings::Windows::Win32::{
    System::WindowsProgramming::GetUserNameW,
    Foundation::PWSTR,
};

fn main() {
    let mut pcbbuffer: u32 = 255;
    let mut helper: u16 = 0;
    let lpbuffer = PWSTR(&mut helper);
    println!("lpbuffer: {:?}\npcbbuffer: {:?}", lpbuffer, pcbbuffer);
    unsafe {
        let success = GetUserNameW(lpbuffer, &mut pcbbuffer);
        println!("GetUserNameW succeeded: {:?}\nlpbuffer: {:?}\npcbbuffer: {:?}", success.as_bool(), lpbuffer, pcbbuffer);
    }
}

产生输出:

lpbuffer: PWSTR(0xca20f5f76e)
pcbbuffer: 255
GetUserNameW succeeded: true 
lpbuffer: PWSTR(0x7200650073)
pcbbuffer: 5

用户名为“user”,即 4 + 1 终止字符 = 5,这很好。我还看到 GetUserNameW 函数成功并且指向字符串的指针发生了变化。

下一步是什么?

发布的代码纯属巧合。它表现出惊人的缓冲区溢出,这几乎不是您希望在 Rust 代码中看到的。具体来说,您正在获取单个 u16 值的地址,并将其传递给 API,告诉它指向的内存大小为 255 个元素。

需要解决的问题:您必须先分配一个足够大的缓冲区来容纳 API 的输出。

将 UTF-16 编码的字符串转换为具有其本机编码的 Rust String 可以使用多种不同的方式完成,例如 String::from_utf16_lossy().

下面的代码大致勾勒出了方法:

fn main() {
    let mut cb_buffer = 257_u32;

    // Create a buffer of the required size
    let mut buffer = Vec::<u16>::with_capacity(cb_buffer as usize);
    // Construct a `PWSTR` by taking the address to the first element in the buffer
    let lp_buffer = PWSTR(buffer.as_mut_ptr());

    let result = unsafe { GetUserNameW(lp_buffer, &mut cb_buffer) };

    // If the API returned success, and more than 0 characters were written
    if result.as_bool() && cb_buffer > 0 {
        // Construct a slice over the valid data
        let buffer = unsafe { slice::from_raw_parts(lp_buffer.0, cb_buffer as usize - 1) };

        // And convert from UTF-16 to Rust's native encoding
        let user_name = String::from_utf16_lossy(buffer);

        println!("User name: {}", user_name);
    }
}