To call a managed function from unmanaged code, you can use a delegate to define the signature of the callback function. The callback function itself must be static and have an AOT.MonoPInvokeCallbackAttribute indicating the matching delegate type:
using System;
using System.Runtime.InteropServices;
using UnityEngine;
public class PluginImport : MonoBehaviour
{
delegate void MyFuncType();
[AOT.MonoPInvokeCallback(typeof(MyFuncType))]
static void MyFunction() {
Debug.Log("Got a call back from native code.");
}
[DllImport("__Internal")]
static extern void RegisterMyCallback(MyFuncType func);
// Keep a reference to the delegate so it (and the native function pointer
// that points back to it) isn't garbage collected while native code still
// holds the function pointer.
static MyFuncType s_callback;
void Start()
{
s_callback = MyFunction;
RegisterMyCallback(s_callback);
}
}
Notes:
RegisterMyCallback creates a delegate instance. The marshaller keeps that delegate alive only for the duration of the P/Invoke call, so a callback invoked immediately is safe. If native code stores the function pointer to call later (as in this example), you must keep a managed reference to the delegate yourself – for example in a static field – for as long as native code might call it. Otherwise the garbage collector can collect the delegate and the later callback dereferences a dangling function pointer, which crashes or corrupts your program.DllImport to use the library name if you use precompiled, dynamically linked libraries. Use the special string, __Internal for source code plug-ins and statically linked libraries. Refer to DllImport attribute for more information.You must pass a function pointer to the unmanaged code so that it can call your managed function. In this example, the C# RegisterMyCallback function passes the function pointer.
The unmanaged code must define a function pointer type that corresponds to the C# delegate. It must also export a function that lets you pass the pointer to your delegate:
// A C# delegate passed to native code defaults to the __stdcall calling convention
// on Windows, but C/C++ function pointers default to __cdecl. Define a macro so the
// function pointer type and the exported function use a matching convention on every
// platform. A mismatch crashes the 32-bit Windows player. Guard on the target
// platform (_WIN32), not the compiler: __stdcall applies to the 32-bit Windows ABI
// and is understood by every Windows compiler (MSVC, MinGW, and Clang).
#if defined(_WIN32)
#define STDCALL __stdcall
#else
#define STDCALL
#endif
typedef void (STDCALL *MyFuncType)();
MyFuncType registeredCallback;
extern "C"
{
void STDCALL RegisterMyCallback(MyFuncType func) {
registeredCallback = func;
}
} // end of export C block
Your unmanaged code can invoke the callback immediately or store the function pointer to invoke later:
if(registeredCallback)
{
registeredCallback();
}