C# Interop - 释放在非托管代码中分配的内存

标签 c# interop

我正在调用以下 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: A heap has been corrupted

看完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 的回答带有警告:

The workaround you found is the correct one, the marshaller isn't going to try to release the memory for an IntPtr. Do note that this will only actually come to a good end if the C code returns a const char* that doesn't need to be released. You have a permanent memory leak if that's not the case.

由于 C++ 方法不返回 const,我假设我的特定函数的解决方法会导致内存泄漏。

我不能改变原来的方法所以我找到了this other question其中讨论了如何从托管代码中释放内存。但是,调用 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 专家。

最佳答案

Can anyone confirm that such a method will indeed suffer from a memory leak

只有您可以做到这一点,缺少的 const 关键字并不能保证 native 代码实际上不会返回文字。 C代码中普遍存在的错误顺便说一句。编写一个调用该函数一亿次的小测试程序。如果您没有看到任务管理器的内存使用量激增,那么您就没有问题。

if so, suggest how to free the memory from the pointer in managed code?

你就是不行,调用free()的一定是原生代码本身。以便它使用正确的堆,即由该代码使用的 C 运行时库创建的堆。底层 winapi 调用是 HeapCreate(),您没有堆句柄。从技术上讲,可以使用 GetProcessHeaps() 发现它,但您只是不知道哪个是“正确的”。从 VS2012 开始,CRT 使用 GetProcessHeap() 而不是 HeapCreate(),现在 Marshal.FreeHGlobal() 可以工作了。但是你知道这段代码没有,你必须向作者或供应商寻求更新。只要你这样做,向他询问这个函数的更有用的风格,它应该采用 char* 作为参数。


更有建设性的方法是对内存泄漏从容应对。只需调用该函数一次,程序运行时版本号不会改变。所以将它存储在一个static 变量中。丢失约 80 字节的地址空间不是您可以注意到的问题,操作系统会在您的程序终止时自动清理。

关于C# Interop - 释放在非托管代码中分配的内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35747219/

相关文章:

c# - SqlGeography 类型不匹配

.net-3.5 - .NET 3.5 ActiveX dll InteropServices.COMException

haskell - Elixir 和 Haskell 互操作性

c# - 如何在 C# 中设置参数的定义值?

c# - 当我在 VS 2010 中按 F5 时,是否可以同时在多个浏览器中进行调试?

.net - COM/Interop - 支持多个版本

COM 互操作引用计数指南

c# - 当 c# 访问 c++ dll 时尝试读取或写入 protected 内存错误

c# - 在 C# 中生成运行哈希(或校验和)?

c# - 在单独的线程上引发事件