【问题标题】:What to pass for pinned string in P/Invoke?在 P/Invoke 中为固定字符串传递什么?
【发布时间】:2012-06-14 04:31:26
【问题描述】:

假设这个 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

【问题讨论】:

  • 当你说它“将字符串存储在某处”时,你的意思是它复制了字符串,还是你的意思是它只存储了指针?这对您的问题的答案有很大的影响。如果您只是存储指针,则需要使用 GCHandle.Alloc()。否则,编组器会制作一个临时副本。
  • 它直接存储点(不是副本)。
  • 您应该将互操作签名中的字符串参数替换为IntPtrbyte*

标签: c# pinvoke pinning


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 我不清楚你的建议是什么。你想用GCHandle.Alloc 固定字符串吗?这是个坏主意。
    • 抱歉,回答不完整。我正在考虑使用 GCHandle.Alloc() 来获取字符串的内存,然后使用 Marshal.AllocHGlobal() 分配一些全局内存并使用 Marshal.Copy() 将字符串复制到其中。但是,Hans 建议使用 StringToHGlobalAnsi,这显然更好。
    猜你喜欢
    • 2020-02-27
    • 1970-01-01
    • 2010-09-14
    • 1970-01-01
    • 2013-10-19
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    • 2017-11-22
    相关资源
    最近更新 更多