Unity 6.3
0 онлайн 46 гостей 3 в системе
Вход

Вызов управляемых функций из неуправляемого кода

Чтобы вызвать управляемую функцию из неуправляемого кода, можно описать сигнатуру функции обратного вызова делегатом. Сама функция обратного вызова должна быть статической и иметь AOT.MonoPInvokeCallbackAttribute указание соответствующего типа делегата:

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);
    }
}

Примечания:

  • Передача метода RegisterMyCallback создаёт экземпляр делегата. Маршалер удерживает этот делегат живым только на время вызова P/Invoke, поэтому немедленно вызванный обратный вызов безопасен. Если же нативный код сохраняет указатель на функцию, чтобы вызвать её позже (как в этом примере), вы должны сами удерживать управляемую ссылку на делегат — например, в статическом поле — всё время, пока нативный код может его вызвать. Иначе сборщик мусора может собрать делегат, и поздний обратный вызов обратится по «повисшему» указателю на функцию, что приведёт к аварийному завершению или повреждению данных.
  • Измените DllImport, чтобы использовать имя библиотеки, если вы используете предкомпилированные, динамически связанные библиотеки. Используйте специальную строку, __Internal для плагинов исходного кода и статически связанных библиотек. См. DllImport атрибут для дополнительной информации.

Необходимо передать указатель на функцию неуправляемому коду, чтобы он мог вызвать управляемую функцию. В этом примере функция C# RegisterMyCallback передает указатель на функцию.

Неуправляемый код должен определять тип указателя функции, соответствующий делегату C#. Он также должен экспортировать функцию, которая позволяет передавать указатель вашему делегату:

// 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

Неуправляемый код может вызвать обратный вызов немедленно или сохранить указатель функции для вызова позже:

if(registeredCallback)
{
    registeredCallback();
}