如何在C#中释放从Rust返回的字符串的内存

本文关键字:返回 Rust 字符串 内存 释放 | 更新日期: 2023-09-27 18:29:51

我从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#外部中的参数,而是将其更改为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#管理的字符串类型。CCD_ 8和CCD_。

rustffi_get_version_free(tempPointer)运行时,它抛出一个异常,说堆栈不平衡

调用PInvoke函数'rustFfiLibrary!"rustFfiLibrary.RustFfiApi::rustfiffi_get_version_free"使堆栈不平衡。这可能是因为托管PInvoke签名与非托管目标签名不匹配。请检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。

sizeof(IntPtr)sizeof(char *)在我的系统上都是4。另外,IntPtr用于返回值;为什么它不能作为一个输入参数?

如何在C#中释放从Rust返回的字符串的内存

Rust中的

extern "C" fn表示函数使用C调用约定。C#期望P/Invoke函数在默认情况下使用stdcall调用约定。

您可以告诉C#使用C调用约定:

[DllImport("rustffilib.dll", CallingConvention = CallingConvention.Cdecl)]

或者,您可以在Rust侧使用extern "stdcall" fn