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

Unity下C++原生插件与C#字节数组返回值大小不匹配问题咨询

解决Unity C#调用C++插件返回vector时数组大小不匹配的问题

嘿,这个问题我之前帮人排查过好几次,核心原因是C++的vector<unsigned char>和C#的byte[]在跨语言交互时的内存结构完全不兼容。你现在的写法里,C#的DllImport会把C++返回的vector对象当成纯字节数组来解析,但vector本身在内存里是个包含三个核心字段的结构体(指向数据的指针、当前元素数、容量),不是直接的原始字节数据,所以C#错误地把这些内部结构的字节甚至后面的未知内存都当成了数据,才会出现数组大小完全离谱的情况。

下面给你两种靠谱的解决方法,都是跨语言交互的常规操作:

方法一:C++返回C风格数组+单独提供长度接口

这种方式清晰直接,明确传递数据指针和长度:

C++端修改代码:

// 用全局vector暂存数据(如果频繁调用,记得加线程锁或者考虑其他内存管理方式,这里先简化)
std::vector<unsigned char> g_cpp_output_data;

// 先调用这个函数填充数据
void FillCPPOutput() {
    g_cpp_output_data.resize(16777216);
    // 这里把你的数据填充到g_cpp_output_data里
}

// 返回数据的原始指针
unsigned char* GetCPPOutputData() {
    return g_cpp_output_data.data();
}

// 返回数据的实际长度
int GetCPPOutputLength() {
    return static_cast<int>(g_cpp_output_data.size());
}

C#端调用代码:

[DllImport("MySharedObj")]
private static extern void FillCPPOutput();

[DllImport("MySharedObj")]
private static extern IntPtr GetCPPOutputData();

[DllImport("MySharedObj")]
private static extern int GetCPPOutputLength();

// 实际调用的逻辑
byte[] GetCppResult() {
    // 先让C++填充好数据
    FillCPPOutput();
    // 获取数据长度和指针
    int dataLength = GetCPPOutputLength();
    IntPtr dataPtr = GetCPPOutputData();
    // 把非托管内存的数据拷贝到C#的byte数组里
    byte[] result = new byte[dataLength];
    System.Runtime.InteropServices.Marshal.Copy(dataPtr, result, 0, dataLength);
    return result;
}

⚠️ 注意:如果你的插件会被多线程调用,一定要给全局vector加锁;另外如果数据会频繁更新,最好在C++端加一个释放内存的函数,避免内存泄漏。

方法二:C#预先分配数组,让C++直接填充

这种方式不需要全局变量,内存由C#管理,更安全,也符合Unity的内存模型:

C++端修改代码:

// 传入C#分配的数组指针和最大长度,返回实际写入的数据长度
int FillCPPOutput(unsigned char* buffer, int max_buffer_length) {
    std::vector<unsigned char> some_vector(16777216);
    // 这里填充你的数据到some_vector中
    int actual_length = static_cast<int>(some_vector.size());
    // 如果C#分配的数组够大,就拷贝数据进去
    if (max_buffer_length >= actual_length) {
        memcpy(buffer, some_vector.data(), actual_length);
    }
    // 返回实际需要的长度,方便C#确认
    return actual_length;
}

C#端调用代码:

[DllImport("MySharedObj")]
private static extern int FillCPPOutput(IntPtr buffer, int maxLength);

// 实际调用逻辑
byte[] GetCppResult() {
    // 先调用一次,传入空指针获取实际需要的数组长度
    int actualLength = FillCPPOutput(IntPtr.Zero, 0);
    // 分配对应大小的byte数组
    byte[] result = new byte[actualLength];
    // 固定数组内存,获取指针传给C++
    fixed (byte* ptr = result) {
        FillCPPOutput((IntPtr)ptr, actualLength);
    }
    return result;
}

这种方法的好处是内存完全由C#掌控,不用操心C++端的全局变量泄漏问题,也更灵活。

再唠唠为啥原来的写法不行

C++的vector对象在内存里的结构大概是这样的(不同编译器可能略有差异,但核心是这三个字段):

struct VectorImpl {
    unsigned char* data_ptr;  // 指向实际数据的指针
    size_t element_count;     // 当前元素数量
    size_t capacity;          // 已分配的内存容量
};

当你直接返回这个vector对象时,C#的DllImport会把整个结构体的字节(比如64位系统下是24字节)当成byte[]的内容,然后还会错误地读取结构体后面的未知内存,所以才会得到170409961这种完全不符合预期的数组长度。

总结一下:跨语言交互时,千万别直接返回C++的复杂对象(vector、string、自定义类都不行),一定要用C风格的原始类型和明确的内存传递约定,这样才能避免这种诡异的内存解析错误。

内容的提问来源于stack exchange,提问作者user8469759

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:53:15