Unity MonoPInvokeCallback 使用教程
MonoPInvokeCallback 是 Unity 中实现 C++ 到 C# 回调机制的关键特性,它解决了跨语言调用中的内存管理和线程安全问题。本教程将通过完整示例详细讲解其使用方法。
一、核心概念解析
1. 为什么需要 MonoPInvokeCallback
- 跨语言调用限制:C++ 无法直接调用 C# 实例方法
- 内存模型差异:托管(C#)与非托管(C++)内存管理方式不同
- 线程安全:防止回调导致堆栈不平衡或线程冲突
2. 工作原理
- 通过特性标记静态方法
- Unity 运行时自动生成适配代码
- 确保回调在正确的内存上下文中执行
二、完整实现步骤
1. C++ DLL 端实现
头文件 (NativeLib.h)
`#ifdef _WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__((visibility("default")))
#endif
extern "C" {
// 定义回调类型
typedef void (*MessageCallback)(const char* message, int type);
// 导出函数
EXPORT void SetupCallback(MessageCallback callback);
EXPORT void TriggerCallback();
}`
源文件 (NativeLib.cpp)
`#include "NativeLib.h"
#include <iostream>
MessageCallback g_callback = nullptr;
EXPORT void SetupCallback(MessageCallback callback) {
g_callback = callback;
std::cout << "Callback setup complete" << std::endl;
}
EXPORT void TriggerCallback() {
if(g_callback) {
// 模拟不同类型消息
g_callback("Hello from C++!", 0);
g_callback("This is an error", 1);
}
}`
2. C# 端实现
委托定义与特性应用
`using System;
using System.Runtime.InteropServices;
using UnityEngine;
using AOT; // 必须引用此命名空间
public class NativeCallbackDemo : MonoBehaviour
{
// 1. 定义与C++匹配的委托类型
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MessageCallback(string message, int type);
// 2. 导入DLL函数
[DllImport("NativeLib")]
private static extern void SetupCallback(MessageCallback callback);
[DllImport("NativeLib")]
private static extern void TriggerCallback();
// 3. 实际回调方法(必须静态)
[MonoPInvokeCallback(typeof(MessageCallback))]
private static void OnMessageReceived(string message, int type)
{
// 注意:此方法不能访问实例成员
Debug.Log($"Message: {message}, Type: {type}");
// 根据类型处理消息
switch(type)
{
case 0: Debug.Log("Normal message"); break;
case 1: Debug.LogError("Error message"); break;
}
}
void Start()
{
// 4. 设置回调
SetupCallback(OnMessageReceived);
// 5. 触发回调(测试用)
Invoke("CallNative", 1f);
}
void CallNative()
{
TriggerCallback();
}
}`
三、关键技术要点
1. 委托声明规范
- 必须 使用
[UnmanagedFunctionPointer]指定调用约定 - 参数类型 必须严格匹配:
- 字符串:
string对应const char* - 整数:
int对应int32_t - 浮点数:
float对应float
- 字符串:
- 返回类型 :与C++声明一致(通常为
void)
2. 回调方法要求
- 必须 标记
[MonoPInvokeCallback] - 必须是静态方法
- 不能访问实例成员(除非通过闭包捕获)
- 避免在回调中执行耗时操作
3. 内存管理策略
-
字符串处理:
`// 安全处理字符串参数 [MonoPInvokeCallback(typeof(MessageCallback))] private static void SafeCallback(string message, int type) { if(!string.IsNullOrEmpty(message)) { // 处理逻辑 } }` -
大内存数据 :建议使用
IntPtr传递`[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void DataCallback(IntPtr data, int size);`
四、平台适配方案
1. Windows 平台
`[DllImport("NativeLib.dll", EntryPoint = "SetupCallback")]
private static extern void SetupCallbackWin(MessageCallback callback);`
2. macOS/Linux 平台
`[DllImport("libNativeLib.so", EntryPoint = "SetupCallback")]
private static extern void SetupCallbackMac(MessageCallback callback);`
3. Android 平台
`#if UNITY_ANDROID && !UNITY_EDITOR
[DllImport("__Internal")]
#else
[DllImport("NativeLib")]
#endif
private static extern void SetupCallbackAndroid(MessageCallback callback);`
五、高级应用技巧
1. 实例方法访问(通过闭包)
`public class InstanceCallbackDemo : MonoBehaviour
{
private Action<string> instanceCallback;
[MonoPInvokeCallback(typeof(MessageCallback))]
private static void StaticWrapper(string message, int type)
{
// 通过闭包访问实例
instanceCallback?.Invoke(message);
}
void Start()
{
instanceCallback = OnInstanceMessage;
SetupCallback(StaticWrapper);
}
void OnInstanceMessage(string message)
{
Debug.Log($"Instance received: {message}");
}
}`
2. 多回调管理
`public class MultiCallbackDemo : MonoBehaviour
{
// 定义多个委托类型
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LogCallback(string message);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ProgressCallback(float progress);
[DllImport("NativeLib")]
private static extern void SetLogCallback(LogCallback callback);
[DllImport("NativeLib")]
private static extern void SetProgressCallback(ProgressCallback callback);
[MonoPInvokeCallback(typeof(LogCallback))]
private static void OnLog(string message) => Debug.Log(message);
[MonoPInvokeCallback(typeof(ProgressCallback))]
private static void OnProgress(float progress)
{
Debug.Log($"Progress: {progress*100}%");
}
void Start()
{
SetLogCallback(OnLog);
SetProgressCallback(OnProgress);
}
}`
六、调试与优化
1. 常见错误排查
- 错误1 :
Attempting to JIT compile method...- 解决方案:确保回调方法标记了
[MonoPInvokeCallback]
- 解决方案:确保回调方法标记了
- 错误2 :回调未触发
- 检查:委托类型是否完全匹配
- 检查:DLL是否正确加载
- 错误3 :内存访问冲突
- 检查:字符串编码是否一致
- 检查:指针参数是否有效
2. 性能优化建议
-
批量处理:减少回调频率
`private static float lastProgress = -1; [MonoPInvokeCallback(typeof(ProgressCallback))] private static void OptimizedProgress(float progress) { if(Mathf.Abs(progress - lastProgress) > 0.05f) { lastProgress = progress; Debug.Log($"Progress: {progress*100}%"); } }` -
对象池:复用回调对象
`private static readonly MessageCallback pooledCallback = OnMessageReceived;`