【问题标题】:What type to PInvoke for a char**PInvoke 用于 char 的类型**
【发布时间】:2014-04-22 21:06:32
【问题描述】:

考虑以下 C 函数:

void get_lib_version(const char **ver_string);

如何使用 PInvoke 正确编组?文档说它返回一个指向静态字符串的指针。我认为这样做可以:

[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int get_lib_version(StringBuilder version);

但我得到的只是胡言乱语。

【问题讨论】:

  • char** 是一个字符串数组,所以可能是 StringBuilder [] 或 string []。
  • 你试过ref StringBuilder version吗?这可能工作......

标签: c#


【解决方案1】:

该函数返回一个全新的 C 字符串。 pinvoke marshaller 始终确保再次释放存储本机代码返回的字符串所需的内存。这不会有一个好的结局,这个函数的调用者肯定不应该释放它。 const 关键字强烈暗示本机代码将返回指向未在堆上分配的字符串文字的指针。尝试释放这样的指针会使您的程序在更高版本的 Windows 上崩溃,这种版本具有严格的堆实现(在 XP 之后)。

你必须帮助阻止编组器这样做。这要求您将参数声明为原始指针,而不是字符串:

  [DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
  public static extern int get_lib_version(out IntPtr version);

你必须做额外的步骤来将指针转换为字符串:

  public string GetLibraryVersion() {
      IntPtr strptr;
      get_lib_version(out strptr);
      return Marshal.PtrToStringAnsi(strptr);
  }

编写一个小测试程序来验证这个假设。调用 GetLibraryVersion() 十亿次。如果内存使用没有爆炸,那你就很好。

【讨论】:

    【解决方案2】:

    根据this answer,当您编组string 时,PInvoke 会对它应该如何被释放做出各种假设。请注意,这是一个const char *;它是某处的常量字符串。它从不需要被释放!

    显然处理这个问题的方法是

    1. Marshall 为 IntPtr

    2. 使用Marshall.PtrToStringAnsi() 将结果复制到C# 字符串中。

    我设法让它正常工作:

    [DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
    private static extern int get_lib_version(ref IntPtr version);
    
    public static string GetLibVersion()
    {
      var ptrVersion = IntPtr.Zero;
      get_lib_version(ref ptrVersion);
      var version = Marshal.PtrToStringAnsi(ptrVersion);
      return version;
    }
    

    【讨论】:

      猜你喜欢
      • 2010-09-27
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      • 1970-01-01
      • 2018-07-23
      • 1970-01-01
      • 2019-08-25
      相关资源
      最近更新 更多