当前答案不完整,所以我想在一个地方发布完整的解决方案。返回IntPtr而不是string根本不解决任何问题,因为你仍然必须释放在C脚本中分配的本地内存。最好的解决方案是在托管端分配字节缓冲区,并将内存传递给C脚本,在那里写入字符串到该缓冲器而不分配内存。
从C返回一个字符串就像这样:
uint32_t __stdcall foo( char* lpBuffer, uint32_t uSize)
{
const char szReturnString[] = "Hello World";
const uint32_t uiStringLength = strlen(szReturnString);
if (uSize >= (uiStringLength + 1))
{
strcpy(lpBuffer, szReturnString);
return uiStringLength;
}
else
{
return uiStringLength + 1;
}
}
C#代码:
[DllImport(_dllLocation, CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Ansi)]
private static extern uint foo(IntPtr lpBuffer, uint uiSize);
private static string foo()
{
IntPtr lpBuffer = Marshal.AllocHGlobal(1);
uint uiRequiredSize = foo(lpBuffer, 1);
if (uiRequiredSize > 1)
{
lpBuffer = Marshal.ReAllocHGlobal(lpBuffer, (IntPtr)uiRequiredSize);
foo(lpBuffer, uiRequiredSize);
}
string str = Marshal.PtrToStringAnsi(lpBuffer);
Marshal.FreeHGlobal(lpBuffer);
lpBuffer = IntPtr.Zero;
Console.WriteLine("GetString return string : [" + str + "]");
return str;
}
使用 StringBuilder 可以更简单地在 C# 中管理内存分配/释放:
[DllImport("TestDLL.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern uint foo(StringBuilder lpBuffer, UInt32 uiSize);
private static string foo()
{
StringBuilder sbBuffer = new StringBuilder(1);
uint uiRequiredSize = foo(sbBuffer, (uint)sbBuffer.Capacity);
if (uiRequiredSize > sbBuffer.Capacity)
{
sbBuffer.Capacity = (int)uiRequiredSize;
foo(sbBuffer, (uint)sbBuffer.Capacity);
}
return sbBuffer.ToString();
}
这里有一个很好的主题,解释了从C/C++代码返回字符串的不同方法。(链接)