我有这两个函数从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#中,我想调用获取版本的函数,获取字符串,然后像下面这样将其传回给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 ;
}
}
但是,当运行
rustffi_get_version_free(temp)
时,C# 程序崩溃了。如何在 C# 中释放被遗忘的字符串内存?需要传递什么值回 Rust 进行 deallocation?与其在 C# extern 中将参数定义为
string
,我将其改为 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)
时,会抛出一个异常,其中包含“堆栈未平衡”的信息:
在我的系统上,PInvoke函数'rustFfiLibrary!rustFfiLibrary.RustFfiApi :: rustffi_get_version_free'的调用未平衡堆栈。这很可能是因为托管PInvoke签名与非托管目标签名不匹配。请检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。
sizeof(IntPtr)
和sizeof(char *)
都是4。此外,IntPtr
可用于返回值;为什么它不能作为输入参数?
MarshalAs
属性来解决这个问题...不幸的是,我现在无法测试它(我还没有做过C#->Rust互操作!)。 - Simon Whitehead