Unity向Rust库传字符串时收到随机固定字节的问题排查
Unity向Rust传递字符串接收异常的问题排查与修复
核心问题
当前代码中Unity向Rust传递字符串时,Rust收到固定随机字节,原因是参数传递方式错误:
- Rust的
is_license_active函数期望接收*const c_char类型,即指向UTF-8编码、\0结尾的C风格字符串的指针。 - 但Unity端直接传递
byte[]数组时,CLR会将托管数组对象的内部指针传给Rust,而非数组中字符串字节的首地址。Rust读取该地址的内存,自然得到的是托管对象的无效数据,表现为重启Unity前固定的随机字节。
修复方案
1. 修正Unity端函数参数的封送规则
将DllImport中的参数类型改为string,并通过MarshalAs(UnmanagedType.LPUTF8Str)指定自动封送为UTF-8编码的C风格字符串,无需手动添加\0终结符。
2. 正确处理Rust返回的字符串指针
- Rust使用
CString::into_raw()创建的字符串指针,需要在Unity端使用后手动释放,避免内存泄漏,因此需要在Rust中新增一个释放字符串的导出函数。 - Unity端结构体中用
IntPtr存储原始指针,手动转换为字符串后调用释放函数回收内存。
完整修复代码
Rust端代码(含内存释放函数)
use std::os::raw::c_char; use std::ffi::{CStr, CString}; #[repr(u8)] pub enum LicenseState { ERROR = 0, INVALID = 1, INACTIVE = 2, ACTIVATED = 3 } #[repr(C)] pub struct LicenseResponse { pub state: u8, pub msg: *mut c_char } #[no_mangle] pub extern "C" fn is_license_active(s: *const c_char) -> LicenseResponse { let license_key; unsafe { license_key = match s.as_ref() { None => "null".to_string(), Some(_) => { match CStr::from_ptr(s).to_str() { Ok(key) => format!("Received: {}", key), Err(e) => format!("Invalid UTF-8: {}", e), } } }; } LicenseResponse { state: 0, msg: CString::new(format!("Key: {};", license_key)) .expect("Failed to create CString") .into_raw() } } // 导出用于释放字符串的函数 #[no_mangle] pub extern "C" fn free_license_response_msg(msg: *mut c_char) { unsafe { if !msg.is_null() { // 重新包装为CString会自动释放内存 let _ = CString::from_raw(msg); } } }
Unity端代码
using System; using System.Runtime.InteropServices; using UnityEngine; public static class LicensingWrapper { [StructLayout(LayoutKind.Sequential)] public struct LicenseResponse { public byte state; // 存储Rust返回的原始字符串指针 public IntPtr msg; } [DllImport("license_validation", CallingConvention = CallingConvention.Cdecl)] private static extern LicenseResponse is_license_active( // 指定将C#字符串封送为UTF-8编码的C风格字符串 [MarshalAs(UnmanagedType.LPUTF8Str)] string licenseKey ); [DllImport("license_validation", CallingConvention = CallingConvention.Cdecl)] private static extern void free_license_response_msg(IntPtr msgPtr); public static void IsLicenseActive(string licenseKey) { if (string.IsNullOrEmpty(licenseKey)) return; Debug.Log($"Checking key: {licenseKey}"); var response = is_license_active(licenseKey); // 将Rust返回的UTF-8指针转换为C#字符串 string responseMsg = Marshal.PtrToStringUTF8(response.msg); Debug.Log($"Response state: {(int)response.state}; response message: {responseMsg}"); // 释放Rust分配的内存,避免泄漏 if (response.msg != IntPtr.Zero) { free_license_response_msg(response.msg); } } }
额外说明
- 无需手动构造带
\0的byte[]:MarshalAs(UnmanagedType.LPUTF8Str)会自动处理字符串的编码和终结符。 - 必须释放Rust返回的字符串:
into_raw()会转移CString的内存所有权到Unity,若不释放会造成内存泄漏。
内容的提问来源于stack exchange,提问作者alexander fedorov
相关产品推荐
相关产品推荐

