我尝试使用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为它们生成了不错的包装器。
谢谢!
答案 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);
}
}
按预期工作。