Given the following C API generated by Kotlin/Native:
#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 */
how can I access the native function foo
from C# using P/Invoke?
I have been going through Marshal API, and have tried several different ways to marshal the native object (like Marshal.PtrToStructure
after returning an IntPtr
from the extern
call), but I know I have a fundamental misunderstanding of how to marshal native objects, and things are even more complex when given a nested struct like above.
I have been going through this guide trying to learn how to marshal complex objects, but this particular use-case doesn't seem to be covered.
After some hours trying to squeeze anything our of this, here is the current state of my application:
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;
}
}
Thanks in advance.
First of all, I'm not familiar with Kotlin/Native, but if there is any way to specify that it should produce a flat API for C code, you should definitely use that. As it stands, the generated API is much too complicated; the bar_ExportedSymbols
structure is nothing more than a bag of functions -- it doesn't need to be defined as a structure at all.
However, going forward with the code we have, you need to define delegates that correspond to the native function pointers.
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();
}
The next thing to do is to define a managed struct to hold the function delegates. The native struct needlessly contains the nested root
and kotlin
structures, which don't do anything useful. This definition should work, unless there are struct padding issues that are specific to your compiler/platform, which you will have to resolve yourself.
[StructLayout(LayoutKind.Sequential)]
public struct ExportedSymbols
{
public NativeMethods.DisposeStablePointer FuncPointerDispose;
public NativeMethods.DisposeString FuncStringDispose;
public NativeMethods.IsInstance FuncIsInstance;
public NativeMethods.Foo FuncFoo;
}
A simple program to test access to the foo
function:
class Program
{
static void Main(string[] args)
{
IntPtr p = NativeMethods.BarSymbols();
ExportedSymbols es = (ExportedSymbols)Marshal.PtrToStructure(p, typeof(ExportedSymbols));
es.FuncFoo("Testing");
}
}
Since this structure is nothing more than a set of function pointers, you will presumably store it in a static variable somewhere that will last the life of the application. If this involved a structure with data members that was allocated and needed to be freed at some point, you would store the pointer p
so that you could pass it to a library function that would release the allocated memory.