如何调用C#中Kotlin / Native生成的本机C库中的函数?

时间:2018-03-22 00:57:11

标签: c# c pinvoke kotlin-native

鉴于Kotlin / Native生成的以下C API:

#ifndef KONAN_bar_H
#define KONAN_bar_H
#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
  /* Service functions. */
  void (*DisposeStablePointer)(bar_KNativePtr ptr);
  void (*DisposeString)(const char* string);
  bar_KBoolean (*IsInstance)(bar_KNativePtr ref, const bar_KType* type);

  /* User functions. */
  struct {
    struct {
      void (*foo)(const char* string);
    } root;
  } kotlin;
} bar_ExportedSymbols;
extern bar_ExportedSymbols* bar_symbols(void);
#ifdef __cplusplus
}  /* extern "C" */
#endif
#endif  /* KONAN_bar_H */

如何使用P / Invoke从C#访问本机函数foo

我一直在浏览Marshal API,并尝试了几种不同的方法来封送原生对象(例如Marshal.PtrToStructureIntPtr调用后返回extern),但是我知道我对如何编组本机对象有一个根本的误解,当给出一个像上面这样的嵌套结构时,事情就更复杂了。

我一直在经历this guide试图学习如何编组复杂对象,但这个特殊的用例似乎没有被涵盖。

经过几个小时试图挤压我们的任何东西,这是我申请的当前状态:

public class TestExtern
{
    [UnmanagedFunctionPointer( CallingConvention.StdCall )]
    public delegate void foo( string @string );

    [DllImport( "bar" )]
    private static extern BarSymbols bar_symbols();

    private void Start()
    {
        var barSymbols = bar_symbols();
        var kotlin = barSymbols.kotlin;
        var root = kotlin.root;
        var fooDelegate = Marshal.GetDelegateForFunctionPointer<foo>( root.instance );
        fooDelegate( "Testing" ); // Access Violation
    }

    [StructLayout( LayoutKind.Sequential )]
    public struct BarSymbols
    {
        public Kotlin kotlin;
    }

    [StructLayout( LayoutKind.Sequential )]
    public struct Kotlin
    {
        public Root root;
    }

    [StructLayout( LayoutKind.Sequential )]
    public struct Root
    {
        public IntPtr instance;
    }
}

提前致谢。

2 个答案:

答案 0 :(得分:2)

首先,我不熟悉Kotlin / Native,但如果有任何方法可以指定它应该为C代码生成一个平面API,那么你一定要使用它。就目前而言,生成的API太复杂了; bar_ExportedSymbols结构只不过是一堆函数 - 它根本不需要被定义为结构。

但是,继续使用我们的代码,您需要定义与本机函数指针对应的委托。

public class NativeMethods
{
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  public delegate void DisposeStablePointer(IntPtr ptr);

  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  public delegate void DisposeString([MarshalAs(UnmanagedType.LPStr)] string str);

  // this assumes that bar_KBoolean is defined as an int
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  [return: MarshalAs(UnmanagedType.Bool)]
  public delegate bool IsInstance(IntPtr pRef, IntPtr type);

  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  public delegate void Foo([MarshalAs(UnmanagedType.LPStr)] string str);

  [DllImport("KONAN_bar.dll", EntryPoint = "bar_symbols")]
  public static extern IntPtr BarSymbols();
}

接下来要做的是定义一个托管结构来保存函数委托。本机结构不必要地包含嵌套的rootkotlin结构,它们没有做任何有用的事情。除非存在特定于您的编译器/平台的结构填充问题,否则此定义应该有效,您必须自己解决。

[StructLayout(LayoutKind.Sequential)]
public struct ExportedSymbols
{
  public NativeMethods.DisposeStablePointer FuncPointerDispose;
  public NativeMethods.DisposeString FuncStringDispose;
  public NativeMethods.IsInstance FuncIsInstance;
  public NativeMethods.Foo FuncFoo;
}

一个测试foo函数访问权限的简单程序:

class Program
{
  static void Main(string[] args)
  {
    IntPtr p = NativeMethods.BarSymbols();
    ExportedSymbols es = (ExportedSymbols)Marshal.PtrToStructure(p, typeof(ExportedSymbols));
    es.FuncFoo("Testing");
  }
}

由于这个结构只不过是一组函数指针,因此您可能会将它存储在某个静态变量中,该变量将持续应用程序的生命周期。如果这涉及一个分配了数据成员并且需要在某个时候释放的结构,那么您将存储指针p,以便将其传递给将释放已分配内存的库函数。

答案 1 :(得分:1)

我建议使用@ konan.internal.CName(&#34; topLevelFunctionName&#34;)注释将您的函数导出为顶级C函数,并像往常一样使用它,因此对于以下代码: @konan.internal.CName("topLevelFunctionVoidFromC") fun topLevelFunctionVoid(x1: Int): Int 将生成以下C代码: int topLevelFunctionVoid(int x1); 请参阅https://github.com/JetBrains/kotlin-native/blob/master/backend.native/tests/produce_dynamic/simple