C#和C++数据封送处理之间的内存泄漏



这是我描述流的示例代码

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(&ptr); Free();},内存不断增加,然后我发现o=pntArr是导致这种情况的原因。

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

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

我能知道这条线路为什么会漏水吗?或者任何我可以改进或使用另一种方法的东西

问题似乎是通过正常的编组方法将数组传回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;
}

最新更新