【问题标题】:How can I call functions in a native C library generated by Kotlin/Native from C#?如何从 C# 调用 Kotlin/Native 生成​​的本机 C 库中的函数?
【发布时间】:2018-03-22 00:57:10
【问题描述】:

鉴于以下由 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,并尝试了几种不同的方法来编组本机对象(例如从extern 调用返回IntPtr 后的Marshal.PtrToStructure),但我知道我有一个根本的误解关于如何编组本机对象,当给定一个像上面这样的嵌套结构时,事情会变得更加复杂。

我一直在通过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;
    }
}

提前致谢。

【问题讨论】:

    标签: c# c pinvoke kotlin-native


    【解决方案1】:

    我建议使用 @konan.internal.CName("topLevelFunctionName") 注释将您的函数导出为顶级 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

    【讨论】:

      【解决方案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,以便您可以将其传递给将释放分配的内存的库函数。

      【讨论】:

      • 不幸的是,我不知道如何制作平面对象。它生成一个表示应用程序包结构的结构(例如com.xaerodegreaz.somepackage),在这个特定的输出中,我只是通过在root 包中编写一个函数来进行测试。
      • 谢谢——您的代码在制作标准控制台应用程序时有效。但奇怪的是,它使 Unity 崩溃(我在其中进行了所有初始测试)。我之前尝试过类似的方法,但它总是崩溃。我回去尝试以前在控制台应用程序中使用的那些方法,它们现在也可以工作了。似乎我可以通过首先在标准控制台应用程序中尝试这一点来节省很多挫败感。至少我会知道故障出在 Unity 的某个地方。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-17
      • 1970-01-01
      • 1970-01-01
      • 2016-06-27
      • 1970-01-01
      相关资源
      最近更新 更多