C#Interop-释放在非托管代码中分配的内存 [英] C# Interop - Releasing memory allocated in unmanaged code

查看:274
本文介绍了C#Interop-释放在非托管代码中分配的内存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在调用以下VC ++方法

I'm calling the following VC++ method

__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程序集调用它时,会导致

The above code is in a library which targets .NET 3.5. When I call this from a 3.5 assembly, it works fine; when calling it from a 4.5 assembly, however, it results in

0xC0000374:堆已损坏

0xC0000374: A heap has been corrupted

在阅读这个问题,我将方法调用更改如下:

After reading this question, I changed my method calls as follows:

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

This works as expected, but the answer from Hans Passant comes with a warning:

您找到的解决方法是正确的,封送程序不会尝试释放IntPtr的内存.请注意,只有在C代码返回不需要释放的const char*时,这实际上才能达到目的.如果不是这种情况,则可能会导致永久性内存泄漏.

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,因此我假设特定功能的解决方法将导致内存泄漏.

Since the C++ method does not return const, I'm going on the assumption that the workaround for my specific function will result in a memory leak.

我无法更改原始方法,所以找到了

I cannot change the original method so I found this other question which discusses how to free memory from manged code. However, calling either Marshal.FreeHGlobal(ptr) or Marshal.FreeCoTaskMem(ptr) also throw 0xC0000374: A heap has been corrupted.

任何人都可以
a)确认这种方法确实会遭受内存泄漏,并且
b)如果是,建议如何从托管代码的指针中释放内存?

Can anyone
a) confirm that such a method will indeed suffer from a memory leak, and
b) if so, suggest how to free the memory from the pointer in managed code?

C ++方法主体简化如下:

The C++ method body is, simplified, as follows:

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专家.

Thanks in advance, and sorry if this is trivial; I'm neither a C++ nor an Interop expert.

推荐答案

谁能确认这种方法确实会遭受内存泄漏

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

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

Only you can do that, the missing const keyword is not a guarantee that the native code does not in fact return a literal. Pervasive mistake in C code btw. Write a little test program that calls the function a hundred million times. If you don't see the memory usage explode with Task Manager then you don't have a problem.

如果是,建议如何从托管代码的指针中释放内存?

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 *作为参数.

You just can't, it must be the native code itself that calls free(). So that it uses the correct heap, the one that was created by the C runtime library used by that code. Underlying winapi call is HeapCreate(), you don't have the heap handle. Technically it is discoverable with GetProcessHeaps() but you just don't know which one is the "right" one. Starting with VS2012, the CRT uses GetProcessHeap() instead of HeapCreate(), now Marshal.FreeHGlobal() can work. But you know that this code doesn't, you'll have to ask the author or vendor for an update. As long as you do that, ask him for a more usable flavor of this function, it should take a char* as an argument instead.

一种更具建设性的方法是逐步解决内存泄漏问题.只需调用函数一次,程序运行时版本号就不会更改.因此,将其存储在 static 变量中.丢失〜80个字节的地址空间并不是您所注意到的问题,当程序终止时,操作系统会自动清理.

A more constructive approach is to just take the memory leak in stride. Simply call the function once, the version number is not going to change while your program is running. So store it in a static variable. Losing ~80 bytes of address space is not a problem you can ever notice, the OS automatically cleans up when your program terminates.

这篇关于C#Interop-释放在非托管代码中分配的内存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆