5

假设这个 C 函数:

void do_something(const char* str)

它将字符串存储在某处以供以后参考

此外,我在 C# 中有这个签名来调用这个函数:

[DllImport("NativeLib")]
static extern void do_something(string str);

现在,将字符串传递给此方法时我需要做什么:

  • 我是否需要固定字符串(用GCHandle.Alloc())(或者编组器创建副本)?
  • 如果我确实需要固定它,我是否传递“原始”字符串(即我传递给的字符串GCHandle.Alloc())?还是我需要传递的返回值GCHandle.AddrOfPinnedObject()
  • 在这种情况下是string正确的数据类型(for )吗?do_something还是我应该IntPtr改用?
4

2 回答 2

7

通过互操作边界存储指针是一个非常糟糕的主意,尽你所能修改 C 代码以制作字符串的副本。

按照赞成的答案中的建议固定字符串不起作用,pinvoke marshaller 必须将字符串转换为 char* 并在进行本机调用后释放转换后的字符串,从而使指针无效。您必须自己编组字符串。将参数声明为 IntPtr 并使用 Marshal.StringToHGlobalAnsi() 创建指针值。

或者使用 Marshal.StringToCoTaskMemAnsi(),它从 COM 堆中分配。这是你真正的问题,没有很好的场景来释放字符串。C 代码无法释放字符串,因为它不知道它是如何分配的。您的 C# 代码无法释放字符串,因为它不知道 C 代码何时使用指针完成。这就是复制字符串如此重要的原因。如果您只进行几次此调用,您可以忍受内存泄漏。

于 2012-06-11T12:31:36.047 回答
0

鉴于非托管代码将存储指针而不是复制字符串,我建议您使用 GCHandle.Alloc() 手动创建字符串的副本并将其作为 IntPtr 传递。

然后,您将能够管理传递的内存块的生命周期,只有在完成后才释放它(通过 GCHandle)。

于 2012-06-11T11:41:01.017 回答