分配数据传递给 FFI 调用的正确方法是什么?

Nor*_*ert 5 ffi rust

讨论/学习了从 Rust 调用 Windows-API 的 FFI 的正确方法之后,我进一步研究了它,并想仔细检查我的理解。

我有一个被调用两次的 Windows API。在第一次调用中,它返回实际输出参数所需的缓冲区大小。然后,使用足够大小的缓冲区第二次调用它。我目前正在使用 aVec作为此缓冲区的数据类型(请参见下面的示例)。

该代码可以工作,但我想知道这是否是正确的方法,或者是否最好利用一个函数,例如alloc::heap::allocate直接保留一些内存,然后用于 transmute将 FFI 的结果转换回来。同样,我的代码可以工作,但我试图在幕后了解一下。

extern crate advapi32;
extern crate winapi;
extern crate widestring;
use widestring::WideCString;
use std::io::Error as IOError;
use winapi::winnt;

fn main() {
    let mut lp_buffer: Vec<winnt::WCHAR> = Vec::new();
    let mut pcb_buffer: winapi::DWORD = 0;

    let rtrn_bool = unsafe {
        advapi32::GetUserNameW(lp_buffer.as_mut_ptr(),
                               &mut pcb_buffer )
    };

    if rtrn_bool == 0 {

        match IOError::last_os_error().raw_os_error() {
            Some(122) => {
                // Resizing the buffers sizes so that the data fits in after 2nd 
                lp_buffer.resize(pcb_buffer as usize, 0 as winnt::WCHAR);
            } // This error is to be expected
            Some(e) => panic!("Unknown OS error {}", e),
            None => panic!("That should not happen"),
        }
    }


    let rtrn_bool2 = unsafe {
        advapi32::GetUserNameW(lp_buffer.as_mut_ptr(), 
                               &mut pcb_buffer )
    };

    if rtrn_bool2 == 0 {
        match IOError::last_os_error().raw_os_error() {
            Some(e) => panic!("Unknown OS error {}", e),
            None => panic!("That should not happen"),
        }
    }

    let widestr: WideCString = unsafe { WideCString::from_ptr_str(lp_buffer.as_ptr()) };

    println!("The owner of the file is {:?}", widestr.to_string_lossy());
}
Run Code Online (Sandbox Code Playgroud)

依赖项:

[dependencies]
advapi32-sys = "0.2"
winapi = "0.2"
widestring = "*"
Run Code Online (Sandbox Code Playgroud)

She*_*ter 4

理想情况下,您会使用std::alloc::alloc,因为您可以将所需的对齐方式指定为布局的一部分:

\n\n
pub unsafe fn alloc(layout: Layout) -> *mut u8\n
Run Code Online (Sandbox Code Playgroud)\n\n

主要缺点是您需要知道对齐方式,即使您释放了分配也是如此。

\n\n

使用 a 作为简单的分配机制是常见的做法Vec,但在使用它时需要小心。

\n\n
    \n
  1. 确保您的单位正确 \xe2\x80\x94 是“长度”参数,是项目数还是字节数?
  2. \n
  3. 如果将其分解Vec为组成部分,则需要\n\n
      \n
    1. 跟踪长度容量。有些人用来shrink_to_fit确保这两个值相同。
    2. \n
    3. 避免交叉流- 内存是由 Rust 分配的,并且必须由 Rust 释放。将其转换回Vec要删除的 a 。
    4. \n
  4. \n
  5. 请注意,空指针Vec没有NULL 指针!

    \n\n
    fn main() {\n    let v: Vec<u8> = Vec::new();\n    println!("{:p}", v.as_ptr());\n    // => 0x1\n}\n
    Run Code Online (Sandbox Code Playgroud)
  6. \n
\n\n
\n\n

对于您的具体情况,我可能建议使用capacityofVec而不是自己跟踪第二个变量。您会注意到,您pcb_buffer在第一次调用后忘记了更新,因此我非常确定代码总是会失败。这很烦人,因为它需要是一个可变的引用,所以你不能完全摆脱它。

\n\n

此外,您可以只使用空格,而不是extending 。Vecreserve

\n\n

也不能保证第一次调用期间所需的大小与第二次调用期间所需的大小相同。您可以执行某种循环,但是您必须担心会发生无限循环。

\n