如何从C#调用Kotlin/Native生成的原生C库中的函数?
如何从C#调用Kotlin/Native生成的C API
我来帮你一步步实现从C#调用这个Kotlin/Native导出的C库,核心是利用.NET的**P/Invoke(平台调用)**特性来对接原生函数,下面是具体的实现细节:
1. 定义C#端的类型映射
首先要把C API里的结构体、类型和函数指针,对应转换成C#的托管类型:
using System; using System.Runtime.InteropServices; // 对应C里的bar_KNativePtr using bar_KNativePtr = IntPtr; // 对应C里的bar_KBoolean(Kotlin/Native通常用uint8_t,这里根据需求选bool或byte) using bar_KBoolean = bool; // 对应C里的bar_KType(无需直接操作,用IntPtr占位即可) using bar_KType = IntPtr; // 定义服务函数的委托 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void DisposeStablePointerDelegate(bar_KNativePtr ptr); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void DisposeStringDelegate(IntPtr str); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bar_KBoolean IsInstanceDelegate(bar_KNativePtr reference, bar_KType type); // 定义用户函数里的root嵌套结构体 [StructLayout(LayoutKind.Sequential)] public struct RootFunctions { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void FooDelegate(IntPtr str); public FooDelegate foo; } // 定义kotlin层级的嵌套结构体 [StructLayout(LayoutKind.Sequential)] public struct KotlinFunctions { public RootFunctions root; } // 定义最外层的导出符号结构体 [StructLayout(LayoutKind.Sequential)] public struct bar_ExportedSymbols { // 服务函数集合 public DisposeStablePointerDelegate DisposeStablePointer; public DisposeStringDelegate DisposeString; public IsInstanceDelegate IsInstance; // 用户自定义函数集合 public KotlinFunctions kotlin; }
2. 声明原生库的导出函数
用[DllImport]特性声明获取导出符号的bar_symbols函数:
public static class BarNativeLibrary { // 替换为你的原生库文件名(Windows是bar.dll,Linux是libbar.so,macOS是libbar.dylib) private const string LibraryName = "bar"; [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr bar_symbols(); }
3. 在C#中调用原生函数
现在就可以获取导出的符号结构体,然后调用目标函数了,内存管理是这里的关键:
class Program { static void Main(string[] args) { // 获取导出符号的指针,转换为C#托管结构体 IntPtr symbolsPtr = BarNativeLibrary.bar_symbols(); bar_ExportedSymbols symbols = Marshal.PtrToStructure<bar_ExportedSymbols>(symbolsPtr); // 准备要传递的字符串:转换为原生ANSI编码的内存块 string message = "Hello from C#!"; IntPtr nativeStr = Marshal.StringToHGlobalAnsi(message); try { // 调用Kotlin导出的foo函数 symbols.kotlin.root.foo(nativeStr); } finally { // 释放C#分配的原生字符串(如果是Kotlin返回的字符串,必须用symbols.DisposeString释放) Marshal.FreeHGlobal(nativeStr); } // 注:导出符号结构体是原生库的全局对象,无需手动释放 } }
关键注意事项
- 调用约定:必须指定
CallingConvention.Cdecl,因为Kotlin/Native生成的C API默认使用Cdecl调用约定,不匹配会导致程序崩溃。 - 内存管理:
- C#主动分配的原生内存(比如
Marshal.StringToHGlobalAnsi创建的),要用Marshal.FreeHGlobal释放。 - 由Kotlin/Native返回的字符串或稳定指针,必须用结构体里的
DisposeString或DisposeStablePointer方法释放,避免内存泄漏。
- C#主动分配的原生内存(比如
- 类型匹配:如果
bar_KBoolean在C中是uint8_t,可以用C#的byte类型对应,避免跨平台下bool类型的兼容性问题。
内容的提问来源于stack exchange,提问作者Xaero Degreaz
相关产品推荐
相关产品推荐

