生成带有参数“ char **”的C函数的SWIG代理

时间:2019-06-17 11:53:49

标签: c# c swig

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

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

第二个参数p1由SWIG作为“ SWIGTYPE_p_p_char”生成,这在C#中是无用的。如何在这里告诉SWIG生成“输出字符串”或“参考字符串”参数?我已经阅读了SWIG的文档,但只了解其中的一半。对于SWIG专业人员而言,这可能很容易。

该方法是从Go功能自动生成的。 “ APIKeyRef_t”和“ char *”运行良好-SWIG为它们生成了不错的包装器。

谢谢!

1 个答案:

答案 0 :(得分:0)

您可以在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);
  }
}

按预期工作。