我正在调用以下VC ++方法
__declspec(dllexport) unsigned char* Get_Version_String()
来自C#的如下:
internal static class NativeMethods
{
[DllImport("my.dll"),
CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true,
CallingConvention = CallingConvention.Cdecl)]
internal static extern string Get_Version_String();
}
上面的代码位于一个面向.NET 3.5的库中。当我从3.5组装中调用它时,它工作正常;但是,当从4.5程序集调用它时,它会导致
0xC0000374:堆已损坏
阅读this question后,我按如下方式更改了方法调用:
[DllImport("my.dll",
EntryPoint = "Get_Version_String",
CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr Get_Version_String_PInvoke();
internal static string Get_Version_String()
{
IntPtr ptr = Get_Version_String_PInvoke();
string versionString = Marshal.PtrToStringAnsi(ptr);
return versionString;
}
这可以按预期工作,但Hans Passant的回答是警告:
您找到的解决方法是正确的,marshaller不会尝试释放
IntPtr
的内存。请注意,如果C代码返回不需要释放的const char*
,这实际上只会达到一个好的结果。如果情况并非如此,则会发生永久性内存泄漏。
由于C ++方法没有返回const
,我假设我的特定函数的变通方法会导致内存泄漏。
我无法更改原始方法,所以我找到了this other question,它讨论了如何从manged代码中释放内存。但是,调用Marshal.FreeHGlobal(ptr)
或Marshal.FreeCoTaskMem(ptr)
也会抛出0xC0000374: A heap has been corrupted.
任何人都可以 a)确认这种方法确实会遭受内存泄漏,并且 b)如果是,建议如何从托管代码中的指针释放内存?
C ++方法体简化如下:
unsigned char versionString[50];
__declspec(dllexport) unsigned char* Get_Version_String()
{
strcpy((char *) versionString, "Key1:[xx],Key2:[xx],Key3:[xx],Key4:[xx]");
// string manipulation
return versionString;
}
提前致谢,对不起,如果这是微不足道的话;我既不是C ++也不是Interop专家。
答案 0 :(得分:6)
任何人都可以确认这样的方法确实会遭遇内存泄漏
只有你可以这样做,缺少的 const 关键字不能保证本机代码实际上不会返回文字。 C代码中的普遍错误。写一个小的测试程序,调用该函数一亿次。如果您没有看到任务管理器爆炸的内存使用情况,那么您就不会遇到问题。
如果是这样,建议如何从托管代码中的指针释放内存?
你不能,它必须是调用free()
的本机代码本身。因此它使用正确的堆,即由该代码使用的C运行时库创建的堆。底层的winapi调用是HeapCreate(),你没有堆处理。从技术上讲,GetProcessHeaps()是可以发现的,但你不知道哪一个是"对"一。从VS2012开始,CRT使用GetProcessHeap()而不是HeapCreate(),现在Marshal.FreeHGlobal()可以工作。但是您知道这段代码并不是,您必须要求作者或供应商进行更新。只要你这样做,请问他有更多可用的功能,它应该以char *作为参数。
更具建设性的方法是大步消除内存泄漏。只需调用函数,程序运行时版本号就不会改变。因此,将其存储在静态变量中。丢失~80字节的地址空间不是您可能注意到的问题,操作系统会在您的程序终止时自动清理。