【问题标题】:More convenient way to work with strings in winapi calls在 winapi 调用中使用字符串的更方便的方法
【发布时间】:2015-06-16 20:19:31
【问题描述】:

我正在寻找在 Rust 的 winapi 调用中使用 std::String 的更方便的方法。

将 rust v 0.12.0-nigtly 与 winapi 0.1.22 和 user32-sys 0.1.1 一起使用

现在我正在使用这样的东西:

use winapi;
use user32;

pub fn get_window_title(handle: i32) -> String {
    let mut v: Vec<u16> = Vec::new();
    v.reserve(255);
    let mut p = v.as_mut_ptr();
    let len = v.len();
    let cap = v.capacity();
    let mut read_len = 0;
    unsafe {
        mem::forget(v);
        read_len = unsafe { user32::GetWindowTextW(handle as winapi::HWND, p, 255) };
        if read_len > 0 {
            return String::from_utf16_lossy(Vec::from_raw_parts(p, read_len as usize, cap).as_slice());
        } else {
            return "".to_string();
        }
    }
}

我认为,这种基于向量的内存分配相当奇怪。所以我正在寻找更简单的方法将LPCWSTR 转换为std::String

【问题讨论】:

  • 我没有答案(我不熟悉 Rust),但我猜i32 是一个 32 位整数。 HWNDs(事实上,所有句柄)都是指针,所以这可能会在 64 位 Windows 上中断;如果不是现在,那么在未来的某个时候。如果您不想直接公开 HWND 类型,则应该使用指针大小的整数。
  • i32i64 用于更轻松地通过Arc 传输全局HWND 和更轻松的线程同步。但这不是问题。
  • @DavidHeffernan:str 没有(im-)可变性保证,就像u8 没有保证一样,它只是一个类型。 It's entirely possible to have a &amp;mut str, there just aren't that many uses for itString 类型是 definitely mutable

标签: string winapi rust


【解决方案1】:

在您的情况下,您始终需要最多 255 个字节,因此您可以使用数组而不是向量。这将整个样板文件简化为 mem::uninitialized() 调用、as_mut_ptr() 调用和切片操作。

unsafe {
    let mut v: [u16; 255] = mem::uninitialized();
    let read_len = user32::GetWindowTextW(
        handle as winapi::HWND,
        v.as_mut_ptr(),
        255,
    );
    String::from_utf16_lossy(&v[0..read_len])
}

如果您想使用 Vec,有一种比销毁 vec 并重新创建它更简单的方法。你可以直接写Vec的内容,让Rust处理其他所有事情。

let mut v: Vec<u16> = Vec::with_capacity(255);
unsafe {
    let read_len = user32::GetWindowTextW(
        handle as winapi::HWND,
        v.as_mut_ptr(),
        v.capacity(),
    );
    v.set_len(read_len); // this is undefined behavior if read_len > v.capacity()
    String::from_utf16_lossy(&v)
}

作为旁注,在 Rust 中,在函数的最后一个语句上不使用 return 是惯用的,而是简单地让表达式站在那里而没有分号。在您的原始代码中,最终的 if 表达式可以写为

if read_len > 0 {
    String::from_utf16_lossy(Vec::from_raw_parts(p, read_len as usize, cap).as_slice())
} else {
    "".to_string()
}

但我从我的样本中删除了整个条件,因为没有必要以与 n 字符不同的方式处理 0 读取字符。

【讨论】:

  • 很好的答案。非常感谢你。我只是误解了文档的某些部分。顺便说一句,我的代码在 32 位版本的编译器中产生内存泄漏。我认为它在那里mem::forget(v);
  • 如果read_len0,您的代码只会造成内存泄漏,因为这样您就永远不会收回对Vec 的控制权。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 2012-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-22
相关资源
最近更新 更多