【问题标题】:Memory Leak between C# and C++ data marshalingC# 和 C++ 数据封送处理之间的内存泄漏
【发布时间】:2021-09-05 02:38:30
【问题描述】:

这是我描述流程的示例代码

C#

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct CppStruct
{
    public int n;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string data;

}

C++ 结构

struct CppData
{
    int N;
    char Data[16];
};

C# 将委托传递给 C++

public delegate void Get(ref int count, out IntPtr[] o); // I want to get an array of data

在 C++ 中

typedef void(__stdcall* GetPtr)(int* count, void** o); //fptr

GetPtrcall CSharp = the delegate passed from C#

// 使用这个callCSharp回调C#

void* ptr = nullptr;
int count = 0; 
callCSharp(&count, &ptr);

现在是关键点, 进入C#委托时

private static void FromCpp(ref int count, out IntPtr[] o)
{
    var d = new CppStruct();
    d.n = 1;
    d.data = "data";
    CppStruct[] ds = new CppCsharMapping[3] { d, d, d }; // Intended to put three elements as an array.
    count = 3; // Let C++ knows, there will be 3 elements back.
    int size = Marshal.SizeOf(d);
    IntPtr[] pntArr = new IntPtr[3];
    pntArr[0] = Marshal.AllocHGlobal(size);
    pntArr[1] = Marshal.AllocHGlobal(size);
    pntArr[2] = Marshal.AllocHGlobal(size);
    for (int i = 0; i < 3; i++)
    {
        Marshal.StructureToPtr(ds[i], pntArr[i], true);
    }

    o = pntArr; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! this line cause leak.
    gPntArr = pntArr; //I used a static variable gPntArr to save pntArr for free this memory block after the return.
    return; 
}

// 从 Cpp 触发的另一个方法中类似下面的内容。确保 Cpp 读回数据。

void Free()
{
    foreach(var m in gPntArr)
    {
        Marshal.FreeHGlobal(m);
    }
}

现在C#返回后,C++可以无任何错误地读回数据。

CppData** back = (CppData**)ptr;
for (int i = 0; i < count; i++, back++)
{
// Print out the value
(*back)->N
(*back)->Data
}

但是我发现通过调用while(true){callCSharp(&amp;ptr); Free();},内存不断增加,然后我发现o = pntArr;是导致这种情况的行。

但我不知道如何正确释放内存。

我找不到实现目标的方法,Cpp 调用 C# 委托来取回一个数组(使用 void* 是因为我可能需要来自 C# 的不同类型的数据,这是我要求单一类型数组的示例),所以上面的示例是我正在尝试的,也许它是错误的,但它有效。

我可以知道为什么这条线会导致泄漏吗?或者任何我可以改进或使用其他方法的东西

【问题讨论】:

  • 看起来pntArr是每次调用FromCpp时新分配的内存,所以也许你应该将它控制在全局List&lt;Intptr[] &gt; gPntArrList 或类似的范围内,以便在FromCpp()中执行gPntArrList .Add(pntArr),然后foreach(var gPntArr in gPntArrList ) foreach(var m in gPntArr) { Marshal.FreeHGlobal(m); } int Free()

标签: c# c++ interop marshalling


【解决方案1】:

问题似乎是您通过正常的编组方法将数组传回 C++,但由于这是回调的一部分,编组器不知道释放此内存。

我建议自定义编组整个数组:

IntPtr _gPntArr;
int _arrSize;

private static void FromCpp(ref int count, out IntPtr o)
{
    var d = new CppStruct();
    d.n = 1;
    d.data = "data";
    CppStruct[] ds = new CppCsharMapping[3] { d, d, d }; // Intended to put three elements as an array.
    count = ds.Length; // Let C++ knows, there will be 3 elements back.

    _arrSize = ds.Length  * Marshal.SizeOf<CppCsharMapping>();
    _gPntArr = Marshal.AllocHGlobal(_arrSize);
    int size = Marshal.SizeOf(d);
    for (int i = 0; i < _arrSize; i += Marshal.SizeOf<CppCsharMapping>())
    {
        var ptr = Marshal.AllocHGlobal(size)
        Marshal.WriteIntPtr(IntPtr.Add(_gPntArr, i), ptr);
        Marshal.StructureToPtr(ds[i], ptr, false);
    }

    o = _gPntArr;
}

void Free()
{
    if(_gPntArr == IntPtr.Zero)
        return;
    for (int i = 0; i < _arrSize; i += Marshal.SizeOf<CppCsharMapping>())
    {
        Marshal.FreeHGlobal(Marshal.ReadIntPtr(IntPtr.Add(_gPntArr, i)));
    }
    Marshal.FreeHGlobal(_gPntArr);
    _gPntArr = IntPtr.Zero;
}

【讨论】:

    猜你喜欢
    • 2021-02-21
    • 1970-01-01
    • 2012-03-09
    • 1970-01-01
    • 1970-01-01
    • 2016-01-27
    • 2010-11-11
    • 2017-02-18
    • 1970-01-01
    相关资源
    最近更新 更多