Unity 6.3
0 онлайн 78 гостей 3 в системе
Вход
Программирование в Unity Шаг 105 из 210

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

Чтобы вызвать управляемую функцию из неуправляемого кода, можно описать сигнатуру функции обратного вызова делегатом. Сама функция обратного вызова должна быть статической и иметь 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();
}