简体   繁体   English

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

[英]How to free the memory of a string returned from Rust in C#?

I have these two functions exposed from Rust 我从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() {}

They are imported by C# as below 它们由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);

    }
}

The memory of the string returned from rustffi_get_version is not managed by Rust anymore as mem::forget has been called. rustffi_get_version返回的字符串的内存不再由Rust管理,因为已经调用了mem::forget In C#, I want to call the get version function, get the string, and then pass it back to Rust for memory deallocation like below. 在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 ;
    }
}

But the C# program crashes when it runs rustffi_get_version_free(temp) . 但是当它运行rustffi_get_version_free(temp)时,C#程序崩溃了。 How to free the forgotten string memory in C#? 如何在C#中释放被遗忘的字符串内存? What should be passed back to Rust for deallocation? 什么应该传递回Rust进行解除分配?

Instead of defining string as the argument in the C# extern, I changed it to pointer . 我没有将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 ;
}

The IntPtr from rustffi_get_version can be successfully converted to a C# managed string type. 来自rustffi_get_versionIntPtr可以成功转换为C#托管字符串类型。 tempString and ver are good. tempStringver都很好。

When rustffi_get_version_free(tempPointer) runs, it throws an exception saying stack unbalanced : rustffi_get_version_free(tempPointer)运行时,它会抛出一个异常,表示堆栈不平衡

A call to PInvoke function 'rustFfiLibrary!rustFfiLibrary.RustFfiApi::rustffi_get_version_free' has unbalanced the stack. 调用PInvoke函数'rustFfiLibrary!rustFfiLibrary.RustFfiApi :: rustffi_get_version_free'使堆栈失去平衡。 This is likely because the managed PInvoke signature does not match the unmanaged target signature. 这很可能是因为托管PInvoke签名与非托管目标签名不匹配。 Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature. 检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。

sizeof(IntPtr) and sizeof(char *) are both 4 on my system. sizeof(IntPtr)sizeof(char *)在我的系统上都是4。 Plus, IntPtr works for return value; 另外, IntPtr工作价格为返回值; why doesn't it work as an input parameter? 为什么它不能作为输入参数?

extern "C" fn in Rust means the function uses the C calling convention. Rust中的extern "C" fn意味着该函数使用C调用约定。 C# expects P/Invoke functions to use the stdcall calling convention by default. C#期望P / Invoke函数默认使用stdcall调用约定。

You can tell C# to use the C calling convention: 您可以告诉C#使用C调用约定:

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

Alternatively, you could use extern "stdcall" fn on the Rust side. 或者,您可以在Rust端使用extern "stdcall" fn

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM