Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#互操作-释放非托管代码中分配的内存_C#_Interop - Fatal编程技术网

C#互操作-释放非托管代码中分配的内存

C#互操作-释放非托管代码中分配的内存,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, Calli

我正在调用下面的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();
}
[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;
}
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;
}
上述代码位于一个针对.NET3.5的库中。当我从3.5程序集调用它时,它运行良好;但是,当从4.5程序集调用它时,会导致

0xC0000374:堆已损坏

阅读后,我更改了方法调用,如下所示:

internal static class NativeMethods
{
    [DllImport("my.dll"),
        CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, 
        CallingConvention = CallingConvention.Cdecl)]
    internal static extern string Get_Version_String();
}
[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;
}
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;
}
这正如预期的那样有效,但Hans Passant的回答带有一个警告:

您找到的解决方法是正确的,封送拆收器不会尝试释放
IntPtr
的内存。请注意,只有当C代码返回一个不需要释放的
const char*
时,这才是一个好的结果。如果不是这样的话,你会有一个永久性的内存泄漏

<>因为C++方法没有返回<代码> const ,我假设我的特定函数的处理将导致内存泄漏。

我无法更改原始方法,因此找到了讨论如何从管理代码中释放内存的方法。但是,调用
Marshal.FreeHGlobal(ptr)
Marshal.FreeCoTaskMem(ptr)
也会抛出
0xC0000374:堆已损坏。

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

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();
}
[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;
}
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++,也不是互操作专家。 有人能确认这样一种方法确实会出现内存泄漏吗

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

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

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



一种更具建设性的方法是从容应对内存泄漏。只需调用该函数一次,程序运行时版本号不会改变。因此,将其存储在静态变量中。丢失约80字节的地址空间并不是你能注意到的问题,当你的程序终止时,操作系统会自动清理

如果可能的话,我可能会试图挑起内存泄漏。如果非托管代码可以多次执行而不会产生不必要的影响,则可以通过偶尔调用
GC.Collect()
将其打包到无休止的循环中,并观察内存消耗。好的,我可以测试它。我应该如何“偶尔”调用
GC.Collect()
?每分钟?更长的时间?不一定需要GC.Collect()。如果您正在运行32位应用程序,内存将以大约2Gb的速度耗尽。但这是一种很好的方法,可以查看内存是否提前释放,而不仅仅是等待OutOfMemoryException。调用它的频率取决于内存分配的速率。如果这很慢,那么每一分钟就足够了。然而,如果它上升得很快,你可以每隔一秒钟左右调用它。还要注意,对GC.Collect()的每次调用都不一定会导致垃圾收集。并且不要在生产性代码中调用GC.Collect()——在大多数情况下都不需要它。感谢您将其放到透视图中并提供了一个实用的解决方案。