分配数据以传递给 FFI 调用的正确方法是什么?
What is the right way to allocate data to pass to an FFI call?
在 之后,我进一步研究了它,想再次检查我的理解。
我有一个 Windows API 被调用了两次。在第一次调用中,它 returns 实际输出参数所需的缓冲区大小。然后,使用足够大小的缓冲区第二次调用它。我目前正在使用 Vec
作为此缓冲区的数据类型(请参见下面的示例)。
代码有效,但我想知道这是否是正确的方法,还是使用 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());
}
依赖关系:
[dependencies]
advapi32-sys = "0.2"
winapi = "0.2"
widestring = "*"
理想情况下,您会使用 std::alloc::alloc
,因为您可以将所需的对齐方式指定为 layout:
的一部分
pub unsafe fn alloc(layout: Layout) -> *mut u8
主要缺点是您需要知道对齐方式,即使您释放了分配。
使用 Vec
作为一种简单的分配机制是常见的做法,但在使用它时需要小心。
- 确保你的单位是正确的——"length" 参数是 items 的数量还是 bytes 的数量?
- 如果将
Vec
分解成组件,则需要
- 追踪长度和容量。有些人使用
shrink_to_fit
来确保这两个值相同。
- 避免交叉流 - 内存由 Rust 分配,必须 由 Rust 释放。将其转换回要删除的
Vec
。
注意空 Vec
不 有 NULL 指针!:
fn main() {
let v: Vec<u8> = Vec::new();
println!("{:p}", v.as_ptr());
// => 0x1
}
对于您的具体情况,我可能建议使用 Vec
的 capacity
而不是自己跟踪第二个变量。您会注意到您在第一次调用后忘记更新 pcb_buffer
,所以我很确定代码总是会失败。这很烦人,因为它需要是一个可变引用,所以你不能完全摆脱它。
此外,您可以 reserve
space.
而不是 extend
Vec
也不能保证第一次调用所需的大小与第二次调用所需的大小相同。您 可以 进行某种循环,但是您必须担心会发生无限循环。
在
我有一个 Windows API 被调用了两次。在第一次调用中,它 returns 实际输出参数所需的缓冲区大小。然后,使用足够大小的缓冲区第二次调用它。我目前正在使用 Vec
作为此缓冲区的数据类型(请参见下面的示例)。
代码有效,但我想知道这是否是正确的方法,还是使用 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());
}
依赖关系:
[dependencies]
advapi32-sys = "0.2"
winapi = "0.2"
widestring = "*"
理想情况下,您会使用 std::alloc::alloc
,因为您可以将所需的对齐方式指定为 layout:
pub unsafe fn alloc(layout: Layout) -> *mut u8
主要缺点是您需要知道对齐方式,即使您释放了分配。
使用 Vec
作为一种简单的分配机制是常见的做法,但在使用它时需要小心。
- 确保你的单位是正确的——"length" 参数是 items 的数量还是 bytes 的数量?
- 如果将
Vec
分解成组件,则需要- 追踪长度和容量。有些人使用
shrink_to_fit
来确保这两个值相同。 - 避免交叉流 - 内存由 Rust 分配,必须 由 Rust 释放。将其转换回要删除的
Vec
。
- 追踪长度和容量。有些人使用
注意空
Vec
不 有 NULL 指针!:fn main() { let v: Vec<u8> = Vec::new(); println!("{:p}", v.as_ptr()); // => 0x1 }
对于您的具体情况,我可能建议使用 Vec
的 capacity
而不是自己跟踪第二个变量。您会注意到您在第一次调用后忘记更新 pcb_buffer
,所以我很确定代码总是会失败。这很烦人,因为它需要是一个可变引用,所以你不能完全摆脱它。
此外,您可以 reserve
space.
extend
Vec
也不能保证第一次调用所需的大小与第二次调用所需的大小相同。您 可以 进行某种循环,但是您必须担心会发生无限循环。