如何释放 C# 中从 Rust 返回的字符串的内存?
How to free the memory of a string returned from Rust in C#?
我从 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
编辑的字符串 return 的内存不再由 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 ;
}
}
但是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:
A call to PInvoke function 'rustFfiLibrary!rustFfiLibrary.RustFfiApi::rustffi_get_version_free' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
sizeof(IntPtr)
和 sizeof(char *)
在我的系统上都是 4。另外,IntPtr
适用于 return 值;为什么它不能用作输入参数?
extern "C" fn
在 Rust 中意味着该函数使用 C 调用约定。
C# 期望 P/Invoke 函数默认使用 stdcall 调用约定。
您可以告诉 C# 使用 C 调用约定:
[DllImport("rustffilib.dll", CallingConvention = CallingConvention.Cdecl)]
或者,您可以在 Rust 端使用 extern "stdcall" fn
。
我从 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
编辑的字符串 return 的内存不再由 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 ;
}
}
但是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:
A call to PInvoke function 'rustFfiLibrary!rustFfiLibrary.RustFfiApi::rustffi_get_version_free' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
sizeof(IntPtr)
和 sizeof(char *)
在我的系统上都是 4。另外,IntPtr
适用于 return 值;为什么它不能用作输入参数?
extern "C" fn
在 Rust 中意味着该函数使用 C 调用约定。
C# 期望 P/Invoke 函数默认使用 stdcall 调用约定。
您可以告诉 C# 使用 C 调用约定:
[DllImport("rustffilib.dll", CallingConvention = CallingConvention.Cdecl)]
或者,您可以在 Rust 端使用 extern "stdcall" fn
。