在P / Invoke中为固定字符串传递什么?
假设这个C函数:
void do_something(const char* str)
它将字符串存储在某处以供以后参考 。
此外,我在C#中有这个签名来调用这个函数:
[DllImport("NativeLib")] static extern void do_something(string str);
现在,将字符串传递给此方法时需要做什么:
- 我是否需要固定字符串(使用
GCHandle.Alloc()
)(或者是编组创建副本)? - 如果我确实需要固定它,我是否传递“原始”字符串(即我传递给
GCHandle.Alloc()
的字符串)? 或者我是否需要传递GCHandle.AddrOfPinnedObject()
的返回值? - 在这种情况下,
string
是正确的数据类型(对于do_something
)吗? 或者我应该使用IntPtr
吗?
通过互操作边界存储指针是一个非常糟糕的想法,尽你所能修改C代码来制作字符串的副本。
按照upvoted答案中的建议固定字符串是行不通的,pinvoke marshaller必须将字符串转换为char *并在进行本机调用后释放转换后的字符串,使指针无效。 你必须自己编组弦乐。 将参数声明为IntPtr并使用Marshal.StringToHGlobalAnsi()创建指针值。
或者使用Marshal.StringToCoTaskMemAnsi(),它从COM堆中分配。 这是你真正的问题,没有很好的方案来释放字符串。 C代码无法释放字符串,因为它不知道它是如何分配的。 您的C#代码无法释放字符串,因为它不知道何时使用指针完成C代码。 这就是复制字符串非常重要的原因。 如果您只拨打几次电话,就可以忍受内存泄漏。
鉴于非托管代码将存储指针而不是复制字符串,我建议您使用GCHandle.Alloc()手动创建字符串的副本并将其作为IntPtr传递。
然后,您将能够管理传递的内存块的生命周期,只有在完成后才能释放它(通过GCHandle)。