我正在使用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包装器库中。
这是一个问题; 最近虽然它像魅力一样工作,但我开始“尝试读取或写入受保护的内存区域”例外。经过深入分析,我想通了,我相信,虽然我为这个指针调用了自由方法,指针的值没有被清除,并且这填充了无人看管的堆并使我的iis工作进程抛出此异常。顺便说一下,它是一个在c库中调用此方法的网站项目。
你能帮我解决这个问题吗?
当然,这是C#代码;
[DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private extern static int ReturnString();
[DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private extern static void HeapDestroy(int ptr);
public static string GetString()
{
try
{
int i = ReturnString();
string result = String.Empty;
if (i > 0)
{
IntPtr ptr = new IntPtr(i);
result = Marshal.PtrToStringAnsi(ptr);
HeapDestroy(i);
}
return result;
}
catch (Exception e)
{
return String.Empty;
}
}
答案 0 :(得分:7)
可能是底层C代码的问题。您没有向strcat依赖的字符串添加NULL终止符(或检查malloc的NULL返回)。在这种情况下很容易损坏内存。您可以通过执行以下操作来解决此问题。
retval[0] = '\0';
strcat(retval, "SOMETEXT");
问题的另一部分是你在系统上玩弄技巧。最好正确编写并让系统正常运行代码。第一步是修复本机代码以正确返回字符串。您需要考虑的一件事是CLR(HGlobal和CoTask分配)本身可以释放某些类型的内存。因此,让我们更改函数签名以返回char*
并使用不同的分配器。
_declspec(dllexport) char* ReturnString()
{
char* retval = (char *) CoTaskMemAlloc(125);
retval[0] = '\0';
strcat(retval, "SOMETEXT");
strcat(retval, "SOMETEXT MORE");
return retval;
}
然后您可以使用以下C#签名并将IntPtr与Marshal.FreeCoTaskMem一起释放。
[DllImport("SomeDll.dll")]
public static extern IntPtr ReturnString();
虽然更好。编组时,如果CLR认为它需要释放内存,它将使用FreeCoTaskMem来执行此操作。这通常与字符串返回相关。由于您使用CoTaskMemAlloc分配了内存,因此您可以自行保存编组+释放步骤并执行以下操作
[DllImport("SomeDll.dll", CharSet=Ansi)]
public static extern String ReturnString();
答案 1 :(得分:1)
释放内存不会清除它,它只是释放它以便可以重复使用。一些调试版本将在内存中写入,以便更容易找到值,例如0xBAADFOOD
来电者应该分配内存,永远不会传回已分配的内存:
_declspec(dllexport) int ReturnString(char*buffer, int bufferSize)
{
if (bufferSize < 125) {
return 125;
} else {
strcat(buffer, "SOMETEXT");
strcat(buffer, "SOMETEXT MORE");
return 0;
}
}
答案 2 :(得分:0)
虽然DLL在与应用程序相同的堆中分配内存,但它可能使用不同的内存管理器,具体取决于它所链接的库。您需要确保使用相同的库,或者在DLL代码本身中添加代码以释放DLL分配的内存。