Documentation

Unity Engine


User Manual

Script Reference

Unity Engine


Call managed functions from unmanaged code

Read time 2 minutesLast updated 13 days ago

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:
  • Passing a method to
    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.
  • Change
    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#endiftypedef 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();}