C# 空闲内存不清除内存块

C# 空闲内存不清除内存块,c#,c,pinvoke,dllimport,C#,C,Pinvoke,Dllimport,我正在使用DllImport从我自己的.net类调用c包装器库中的方法。c dll中的此方法创建字符串变量并返回字符串的指针 像这样的东西 _declspec(dllexport) int ReturnString() { char* retval = (char *) malloc(125); strcat(retval, "SOMETEXT"); strcat(retval, "SOMETEXT MORE"); return (int)retval; } 然后我使用Marshall

我正在使用DllImport从我自己的.net类调用c包装器库中的方法。c dll中的此方法创建字符串变量并返回字符串的指针

像这样的东西

_declspec(dllexport) int ReturnString()
{
 char* retval = (char *) malloc(125);
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return (int)retval;
}
然后我使用Marshall.PtrToStringAnsi(ptr)读取字符串。在获得字符串的副本后,我只需调用另一个c方法HeapDestroy,它位于调用free(ptr)的c包装器库中

问题是这样的; 最近,当它像一个魔咒一样工作时,我开始出现“尝试读取或写入受保护内存区域”异常。在深入分析之后,我发现,我相信,尽管我为这个指针调用了free方法,但指针的值并没有被清除,这会在无人参与的情况下填充堆,并使我的iis工作进程抛出这个异常。顺便说一下,它是一个在c库中调用此方法的网站项目

你能帮我解决这个问题吗

当然,这是C代码


可能的问题是底层的C代码。您没有向strcat依赖的字符串添加NULL终止符(或检查malloc返回的NULL)。在这种情况下,很容易损坏内存。您可以通过执行以下操作来修复此问题

retval[0] = '\0';
strcat(retval, "SOMETEXT");
问题的另一部分是,您正在系统上玩把戏。正确地编写代码并让系统在正确运行的代码上工作要好得多。第一步是修复本机代码以正确返回字符串。你需要考虑的是,只有某些类型的内存可以被CLR(HGLUP和CONTRORATION分配)自然释放。因此,让我们更改函数签名以返回
char*
,并使用不同的分配器

_declspec(dllexport) char* ReturnString()
{
 char* retval = (char *) CoTaskMemAlloc(125);
 retval[0] = '\0';
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return retval;
}
然后,您可以使用以下C#签名并使用Marshal.FreeCoTaskMem释放IntPtr

[DllImport("SomeDll.dll")]
public static extern IntPtr ReturnString();
不过更好。编组时,如果CLR认为需要释放内存,它将使用FreeCoTaskMem来释放内存。这通常与字符串返回相关。由于使用CoTaskMemAlloc分配了内存,因此可以保存编组+释放步骤并执行以下操作

[DllImport("SomeDll.dll", CharSet=Ansi)]
public static extern String ReturnString();

可能的问题是底层的C代码。您没有向strcat依赖的字符串添加NULL终止符(或检查malloc返回的NULL)。在这种情况下,很容易损坏内存。您可以通过执行以下操作来修复此问题

retval[0] = '\0';
strcat(retval, "SOMETEXT");
问题的另一部分是,您正在系统上玩把戏。正确地编写代码并让系统在正确运行的代码上工作要好得多。第一步是修复本机代码以正确返回字符串。你需要考虑的是,只有某些类型的内存可以被CLR(HGLUP和CONTRORATION分配)自然释放。因此,让我们更改函数签名以返回
char*
,并使用不同的分配器

_declspec(dllexport) char* ReturnString()
{
 char* retval = (char *) CoTaskMemAlloc(125);
 retval[0] = '\0';
 strcat(retval, "SOMETEXT");
 strcat(retval, "SOMETEXT MORE");
 return retval;
}
然后,您可以使用以下C#签名并使用Marshal.FreeCoTaskMem释放IntPtr

[DllImport("SomeDll.dll")]
public static extern IntPtr ReturnString();
不过更好。编组时,如果CLR认为需要释放内存,它将使用FreeCoTaskMem来释放内存。这通常与字符串返回相关。由于使用CoTaskMemAlloc分配了内存,因此可以保存编组+释放步骤并执行以下操作

[DllImport("SomeDll.dll", CharSet=Ansi)]
public static extern String ReturnString();

释放内存并不能清除它,它只是释放内存以便重新使用。一些调试构建将写入内存,以便您更容易发现0xBAADFOOD等值的问题

调用者应分配内存,切勿传回已分配的内存:

_declspec(dllexport) int ReturnString(char*buffer, int bufferSize)
{
    if (bufferSize < 125) {
        return 125;
    } else {
        strcat(buffer, "SOMETEXT");
        strcat(buffer, "SOMETEXT MORE");
        return 0;
    }
}
\u declspec(dllexport)int ReturnString(char*buffer,int bufferSize)
{
如果(缓冲区大小<125){
返回125;
}否则{
strcat(缓冲区,“SOMETEXT”);
strcat(缓冲区,“SOMETEXT MORE”);
返回0;
}
}

释放内存并不能清除它,它只是释放内存以便可以重复使用。一些调试构建将写入内存,以便您更容易发现0xBAADFOOD等值的问题

调用者应分配内存,切勿传回已分配的内存:

_declspec(dllexport) int ReturnString(char*buffer, int bufferSize)
{
    if (bufferSize < 125) {
        return 125;
    } else {
        strcat(buffer, "SOMETEXT");
        strcat(buffer, "SOMETEXT MORE");
        return 0;
    }
}
\u declspec(dllexport)int ReturnString(char*buffer,int bufferSize)
{
如果(缓冲区大小<125){
返回125;
}否则{
strcat(缓冲区,“SOMETEXT”);
strcat(缓冲区,“SOMETEXT MORE”);
返回0;
}
}

尽管DLL在与应用程序相同的堆中分配内存,但它可能使用不同的内存管理器,具体取决于它所链接的库。您需要确保使用的是相同的库,或者在DLL代码本身中添加代码以释放DLL分配的内存。

尽管DLL在与应用程序相同的堆中分配内存,但它可能使用不同的内存管理器,具体取决于它链接的库。您需要确保使用的是相同的库,或者在DLL代码本身中添加代码以释放DLL分配的内存。

您可以发布您正在使用的C代码吗?您可以发布您正在使用的C代码吗?是的,这就是为什么添加了HeapDestroy方法,其中包含要调用的简单空闲(ptr)方法。我误解了你的问题。我认为您已经围绕DLL编写了一个包装器,因此DLL正在执行alloc,而您的包装器代码正在执行free。我建议您使用更安全的操作来处理有界字符串。将buffersize设置为常量,并将其传递给对分配的字符串datat进行操作的函数。使用strncat等等。@justinhj,实际上我的示例是有界字符串,我的实际字符串变量是无界的。无论如何,谢谢你的提示。是的,这就是为什么添加了HeapDestroy方法,其中包含一个简单的免费(ptr)方法来调用.Cool。我误解了你的问题。我认为您已经围绕DLL编写了一个包装器,因此DLL正在执行alloc,而您的包装器代码正在执行free。我建议您使用更安全的操作来处理有界字符串。将buffersize设置为常量,并将其传递给functi