【发布时间】:2016-01-12 07:19:09
【问题描述】:
我从 Rust
中公开了这两个函数extern crate libc;
use std::mem;
use std::ffi::{CString, CStr};
use libc::c_char;
pub static FFI_LIB_VERSION: &'static str = env!("CARGO_PKG_VERSION"); // '
#[no_mangle]
pub extern "C" fn rustffi_get_version() -> *const c_char {
let s = CString::new(FFI_LIB_VERSION).unwrap();
let p = s.as_ptr();
mem::forget(s);
p as *const _
}
#[no_mangle]
pub extern "C" fn rustffi_get_version_free(s: *mut c_char) {
unsafe {
if s.is_null() {
return;
}
let c_str: &CStr = CStr::from_ptr(s);
let bytes_len: usize = c_str.to_bytes_with_nul().len();
let temp_vec: Vec<c_char> = Vec::from_raw_parts(s, bytes_len, bytes_len);
}
}
fn main() {}
它们由 C# 导入,如下所示
namespace rustFfiLibrary
{
public class RustFfiApi
{
[DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version")]
public static extern string rustffi_get_version();
[DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version_free")]
public static extern void rustffi_get_version_free(string s);
}
}
rustffi_get_version 返回的字符串的内存不再由 Rust 管理,因为 mem::forget 已被调用。在 C# 中,我想调用 get version 函数,获取字符串,然后将其传递回 Rust 进行内存释放,如下所示。
public class RustService
{
public static string GetVersion()
{
string temp = RustFfiApi.rustffi_get_version();
string ver = (string)temp.Clone();
RustFfiApi.rustffi_get_version_free(temp);
return ver ;
}
}
但是 C# 程序在运行 rustffi_get_version_free(temp) 时会崩溃。如何在 C# 中释放被遗忘的字符串内存?应该将什么传回 Rust 进行释放?
我没有将 string 定义为 C# extern 中的参数,而是将其更改为 pointer。
[DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version")]
public static extern System.IntPtr rustffi_get_version();
[DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version_free")]
public static extern void rustffi_get_version_free(System.IntPtr s);
public static string GetVersion()
{
System.IntPtr tempPointer = RustFfiApi.rustffi_get_version();
string tempString = Marshal.PtrToStringAnsi(tempPointer);
string ver = (string)tempString.Clone();
RustFfiApi.rustffi_get_version_free(tempPointer);
return ver ;
}
rustffi_get_version 中的IntPtr 可以成功转换为 C# 托管字符串类型。 tempString 和 ver 都不错。
当rustffi_get_version_free(tempPointer) 运行时,它会抛出异常stack unbalanced:
对 PInvoke 函数 'rustFfiLibrary!rustFfiLibrary.RustFfiApi::rustffi_get_version_free' 的调用使堆栈失衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。
sizeof(IntPtr) 和 sizeof(char *) 在我的系统上都是 4。另外,IntPtr 用于返回值;为什么不能作为输入参数?
【问题讨论】:
-
你返回的字符串一旦进入 .NET 领域就会是一个托管字符串......因此你不能只将指向它的指针传递回 Rust,因为 .NET 垃圾收集器可以移动它。我想知道您是否需要添加
MarshalAs属性来帮助解决这个问题......不幸的是我现在无法测试它(而且我还没有完成任何 C#->Rust 互操作!)。跨度> -
@simon,是的,我就是这么想的。那么,您认为我应该将 C# 中的接口函数更改为 [DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version")] public static extern const char* rustffi_get_version(); ?然后我自己将 char* 显式转换为字符串
-
这当然是一个值得探索的选择。
-
@tried,但得到另一个错误。我编辑问题以包含新信息。
标签: c# memory-management memory-leaks rust ffi