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