![](/img/trans.png)
[英]How do I call native C++ code from a C# project in an environment where I can't reference a C++/CLI library?
[英]How can I call functions in a native C library generated by Kotlin/Native from C#?
給定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
),但我知道我對如何編組本機對象有一個基本的誤解,並且當給出如上所述的嵌套結構時,事情會更加復雜。
我一直在閱讀本指南,試圖學習如何編組復雜的對象,但這個特殊的用例似乎沒有被涵蓋。
幾個小時后試圖擠壓我們的任何東西,這是我的應用程序的當前狀態:
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;
}
}
提前致謝。
首先,我不熟悉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();
}
接下來要做的是定義一個托管結構來保存函數委托。 本機結構不必要地包含嵌套的root
和kotlin
結構,它們沒有做任何有用的事情。 除非存在特定於您的編譯器/平台的結構填充問題,否則此定義應該有效,您必須自己解決。
[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
以便將其傳遞給將釋放已分配內存的庫函數。
我建議使用@ 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 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.