You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:16:00