要PInvoke的字符**的类型

本文关键字:类型 PInvoke 字符 | 更新日期: 2023-09-27 17:59:24

考虑以下C函数:

void get_lib_version(const char **ver_string);

如何使用PInvoke正确整理?文档中说它返回一个指向静态字符串的指针。我以为这样就可以了:

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

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

要PInvoke的字符**的类型

函数返回一个全新的C字符串。pinvokemarshaller总是确保再次释放存储本机代码返回的字符串所需的内存。这不会有好的结果,当然这个函数的调用方不应该释放它。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()十亿次。如果内存使用率没有爆炸式增长,那么你就很好。

根据这个答案,当您将某个东西封送为string时,PInvoke会对如何释放它做出各种假设。请注意,这是一个const char *;它在某个地方是一个常量字符串。它永远不需要释放

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

  1. 马歇尔作为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;
}