Unity MonoPInvokeCallback 使用教程

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. 常见错误排查

  • 错误1Attempting 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;`
相关推荐
吗~喽1 小时前
【LeetCode】滑动窗口_水果成篮_C++
c++·算法·leetcode
心疼你的一切2 小时前
Unity开发Rokid应用之离线语音指令交互模型
android·开发语言·unity·游戏引擎·交互·lucene
BestOrNothing_20152 小时前
【C++基础】Day 4:关键字之 new、malloc、constexpr、const、extern及static
c++·八股文·static·extern·new与malloc·constexpr与const
无敌最俊朗@2 小时前
如何把一个压缩的视频文件,解压成一张张原始图片-decode_video.c
c++
fpcc2 小时前
C++编程实践——手动实现std::visit
c++
重启的码农2 小时前
enet源码解析(4)多通道机制 (Channels)
c++·网络协议
重启的码农2 小时前
enet源码解析(3)数据包 (ENetPacket)
c++·网络协议
wefg13 小时前
【C++】智能指针
开发语言·c++·算法
MSTcheng.3 小时前
【C++模板进阶】C++ 模板进阶的拦路虎:模板特化和分离编译,该如何逐个突破?
开发语言·c++·模板