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

Unity中调用C++ DLL时Marshal.FreeHGlobal()失败,如何保证内存安全?

解决Unity C#调用自定义C++ DLL的内存安全问题

问题根源

你遇到的free(): invalid pointer错误,核心原因是内存分配和释放的分配器不匹配:

  • C++函数里用new[]分配内存,对应的释放必须用delete[];
  • Marshal.FreeHGlobal()是用来释放由Marshal.AllocHGlobal()分配的内存,依赖Windows系统堆分配器,和C++标准库的堆不是同一个,直接调用必然触发内存错误。

方案一:在DLL中提供配套的释放函数(推荐)

最稳妥的方式是让分配内存的C++代码负责提供释放逻辑,在DLL中新增一个释放函数,C#调用该函数来释放内存。

修改后的C++代码

// 原解码函数保持不变
extern "C" __declspec(dllexport) unsigned char* MyCppFunction(const unsigned char* messagePtr,
                                  int messageSize, int* decodedFrameLength) {
    std::vector<uint8_t> decoded_img(33554432);
    int decoded_len = 0;

    decoder_->DecodeFrame(
        reinterpret_cast<const unsigned char*>(messagePtr), messageSize,
        reinterpret_cast<unsigned char*>(decoded_img.data()), &decoded_len,
        33554432);

    *decodedFrameLength = decoded_len;
    unsigned char* outputData = new unsigned char[decoded_len];
    std::memcpy(outputData, decoded_img.data(), decoded_len);

    return outputData;
}

// 新增的内存释放函数
extern "C" __declspec(dllexport) void FreeDecodedData(unsigned char* data) {
    delete[] data;
}

修改后的C#代码

using System.Runtime.InteropServices;
using UnityEngine;

public class YourScript : MonoBehaviour
{
    [DllImport("你的DLL文件名.dll")]
    private static extern IntPtr MyCppFunction(byte[] messagePtr, int messageSize, out int decodedFrameLength);

    [DllImport("你的DLL文件名.dll")]
    private static extern void FreeDecodedData(IntPtr data);

    void OnMessage(Message message) {
        byte[] byteArray = message.payload.ToByteArray();
        int decodedFrameLength;
        IntPtr output = MyCppFunction(byteArray, byteArray.Length, out decodedFrameLength);

        byte[] resultByteArray = new byte[decodedFrameLength];
        Marshal.Copy(output, resultByteArray, 0, decodedFrameLength);
        
        // 调用DLL提供的释放函数
        FreeDecodedData(output);

        Debug.Log($"decoded frame length was: {decodedFrameLength} and resultByteArray length was: {resultByteArray.Length}");
        if (resultByteArray.Length > 0) {
            // 处理解码后的数据
        }
    }
}

方案二:使用跨语言兼容的分配器

如果不想新增释放函数,可以让C++使用和.NET互操作兼容的分配器(比如CoTaskMemAlloc),这样C#可以用Marshal.FreeCoTaskMem()直接释放。

修改后的C++代码

#include <objbase.h> // 引入CoTaskMemAlloc的头文件

extern "C" __declspec(dllexport) unsigned char* MyCppFunction(const unsigned char* messagePtr,
                                  int messageSize, int* decodedFrameLength) {
    std::vector<uint8_t> decoded_img(33554432);
    int decoded_len = 0;

    decoder_->DecodeFrame(
        reinterpret_cast<const unsigned char*>(messagePtr), messageSize,
        reinterpret_cast<unsigned char*>(decoded_img.data()), &decoded_len,
        33554432);

    *decodedFrameLength = decoded_len;
    // 用CoTaskMemAlloc分配内存,兼容.NET的Marshal释放
    unsigned char* outputData = static_cast<unsigned char*>(CoTaskMemAlloc(decoded_len));
    std::memcpy(outputData, decoded_img.data(), decoded_len);

    return outputData;
}

修改后的C#代码

void OnMessage(Message message) {
    byte[] byteArray = message.payload.ToByteArray();
    int decodedFrameLength;
    IntPtr output = MyCppFunction(byteArray, byteArray.Length, out decodedFrameLength);

    byte[] resultByteArray = new byte[decodedFrameLength];
    Marshal.Copy(output, resultByteArray, 0, decodedFrameLength);
    
    // 用Marshal.FreeCoTaskMem释放CoTaskMemAlloc分配的内存
    Marshal.FreeCoTaskMem(output);

    Debug.Log($"decoded frame length was: {decodedFrameLength} and resultByteArray length was: {resultByteArray.Length}");
    if (resultByteArray.Length > 0) {
        // 处理解码后的数据
    }
}

注意事项

  • 必须严格遵循谁分配谁释放的原则,或者使用双方都兼容的分配器;
  • 不要混用不同分配器的分配和释放函数,否则必然导致内存泄漏或崩溃;
  • 若使用C++/CLI(混合模式),还可以用gcnew直接分配托管数组,但需要修改DLL编译模式,适合更复杂的交互场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 19:02:07