目前的答案并不完整,所以我只想在一个地方发布完整的解决方案。返回 IntPtr 而不是 string 根本不能解决任何问题,因为您仍然必须释放 C 脚本中分配的本机内存。最好的解决方案是在托管端分配字节缓冲区并将内存传递给 C 脚本,该脚本将字符串写入该缓冲区而不分配内存。
从 C 中返回一个字符串就像这样:
//extern "C" __declspec(dllexport) uint32_t foo(/*[out]*/ char* lpBuffer, /*[in]*/ uint32_t uSize)
uint32_t __stdcall foo(/*[out]*/ char* lpBuffer, /*[in]*/ uint32_t uSize)
{
const char szReturnString[] = "Hello World";
const uint32_t uiStringLength = strlen(szReturnString);
if (uSize >= (uiStringLength + 1))
{
strcpy(lpBuffer, szReturnString);
// Return the number of characters copied.
return uiStringLength;
}
else
{
// Return the required size
// (including the terminating NULL character).
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()
{
// First allocate a buffer of 1 byte.
IntPtr lpBuffer = Marshal.AllocHGlobal(1);
// Call the API. If the size of the buffer
// is insufficient, the return value in
// uiRequiredSize will indicate the required
// size.
uint uiRequiredSize = foo(lpBuffer, 1);
if (uiRequiredSize > 1)
{
// The buffer pointed to by lpBuffer needs to be of a
// greater size than the current capacity.
// This required size is the returned value in "uiRequiredSize"
// (including the terminating NULL character).
lpBuffer = Marshal.ReAllocHGlobal(lpBuffer, (IntPtr)uiRequiredSize);
// Call the API again.
foo(lpBuffer, uiRequiredSize);
}
// Convert the characters inside the buffer
// into a managed string.
string str = Marshal.PtrToStringAnsi(lpBuffer);
// Free the buffer.
Marshal.FreeHGlobal(lpBuffer);
lpBuffer = IntPtr.Zero;
// Display the string.
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 needs to be of a greater size than current capacity.
// This required size is the returned value in "uiRequiredSize"
// (including the terminating NULL character).
sbBuffer.Capacity = (int)uiRequiredSize;
// Call the API again.
foo(sbBuffer, (uint)sbBuffer.Capacity);
}
return sbBuffer.ToString();
}
有一个很好的主题解释了从 C/C++ 代码返回字符串的不同方式。