I’ve been experimenting with C# 9 function pointers in Unity and, for the most part, have found they work well* for managed-to-native calls. However, I’ve been unable to get the reverse, native-to-managed calls, working under IL2CPP in particular.
Imagine a simple native library with a function set_callback that takes a function pointer in order to set a callback, and a function trigger_callback that causes invocation of the stored callback. And imagine I have simple C# DllImport bindings to these functions in the static class NativeLib (using void* as the callback type in the managed signatures*)*.
I can declare my static managed callback method, declare a matching delegate type, annotate the method with MonoPInvokeCallbackAttribute and then use GetFunctionPointerForDelegate and everything works as you’d expect using either Mono or IL2CPP:
delegate void CallbackDelegate();
[MonoPInvokeCallback(typeof(CallbackDelegate))]
static void Callback()
{
Debug.Log("Callback!");
}
void Start()
{
var callback = (void*)Marshal.GetFunctionPointerForDelegate<CallbackDelegate>(Callback);
Debug.Log($"Setting callback: {(nint)callback}");
NativeLib.set_callback(callback);
Debug.Log("Triggering callback");
NativeLib.trigger_callback();
}
However, if try to do the same with function pointers instead, IL2CPP players (only) will crash:
void Start()
{
delegate* managed <void> callback = &Callback;
Debug.Log($"Setting callback: {(nint)callback}");
NativeLib.set_callback(callback);
Debug.Log("Triggering callback");
NativeLib.trigger_callback(); // crashes w/ IL2CPP!
}
Is there something special that needs to happen to make this work (ala using MonoPInvokeCallbackAttribute for IL2CPP compatibility in the delegate case*)*? Note that the function pointer approach works fine using Mono, and also under .NET 7 with Native AOT. I can’t declare my callback pointer as an unmanaged function pointer without the UnmanagedCallersOnlyAttribute from .NET 5+, but in any case, I understood that to be optional (and things seems to be fine without it in .NET 7 Native AOT).
*I have to explicitly declare a calling convention on managed-to-native function pointers since “The target runtime doesn’t support extensible or runtime-environment default calling conventions”, which is weird, since any of the built in conventions seem to work even though they aren’t relevant on most platforms, so seemingly some sort of default is being used. Any way around this?