使用参数 "char**" 为 C 函数生成 SWIG 代理

Generate SWIG-Proxy for C-function with param "char**"

我尝试使用 SWIG 为此 C 代码生成包装器:

extern APIKeyRef_t parse_api_key(char* p0, char** p1)

第二个参数 p1 由 SWIG 生成为 "SWIGTYPE_p_p_char",这在 C# 中是无用的。我如何告诉 SWIG 在此处生成 "out string" 或 "ref string" 参数?我已经阅读了 SWIG 的文档,但只了解其中的一半。对于 SWIG 专业人士来说,这可能是一件容易的事。

该方法是从 Go 函数自动生成的。 "APIKeyRef_t" 和 "char*" 工作完美 - SWIG 为它们生成了很好的包装器。

谢谢!

您可以在 SWIG 中执行此操作。我通常不会写太多(阅读:任何)C#,我在 Linux 上用 Mono 测试了这个,所以我的回答有一个相当重要的警告 - 你应该仔细验证它的正确性。

无论如何我们可以生成一个包装器,我很高兴它是正确的。 SWIG(在大多数语言的大多数模式中)分两部分生成包装器。一些代码,用您正在为其构建包装器的语言(即此处的 C#)以及一些 C 或 C++ 编写。

通过自定义包装器中的 C# 入口点,我们可以为我们的论点做好准备。本质上,我们使用它通过引用将 IntPtr(初始化为 NULL)传递给我们的 C 函数。函数调用发生后,我们然后使用 Marshal.PtrToStringAnsi 读取输出字符串,我们现在知道其地址,返回到 C# 世界作为我们的 C# 函数的输出。

最后剩下要做的就是清理了。这一步取决于我们正在调用的函数的语义——如果我们最终拥有字符串,我们需要在获取副本后释放它。所以如果我们不拥有这个字符串,我们就不能释放它。如果 FreeHGlobal 不是释放它的正确方法,您将需要替代方法。

%module test

%typemap(csin,pre="global::System.IntPtr tmp$csinput=global::System.IntPtr.Zero;",
              post="$csinput=global::System.Runtime.InteropServices.Marshal.PtrToStringAnsi(tmp$csinput);
                    global::System.Runtime.InteropServices.Marshal.FreeHGlobal(tmp$csinput);") char **OUTPUT "ref tmp$csinput";
%typemap(cstype) char **OUTPUT "out string";

%typemap(imtype) char **OUTPUT "ref global::System.IntPtr"

%apply char **OUTPUT { char **outarg };

%{
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
%}

%inline %{
void foobar(char **outarg) {
  fprintf(stderr, "In foobar: outarg is: %p\n", outarg);
  fprintf(stderr, "*outarg starts as: %p\n", *outarg); // This will be NULL, we initalised to Zero
  *outarg = StrDupA("Hello world"); // This is good enough for testing
  fprintf(stderr, "*outarg is now: %p\n", *outarg); // We can see this value by looking at our IntPtr instead of copying it
}
%}

有了这个,我们就可以 运行 成功地完成这样的事情:

public class runme {
  static void Main(string[] args) {
        string blah;
        test.foobar(out blah);
        System.Console.WriteLine(blah);
  }
}

按预期工作。