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;`
相关推荐
Pointer Pursuit1 小时前
C++11特性(二)
前端·c++·算法
tudousisi2222 小时前
CSP 第3题训练 Day 5|CSP202509C HTTP 头信息|Blog A
c++
闻缺陷则喜何志丹3 小时前
【动态规划】P10726 [GESP202406 八级] 空间跳跃|普及+
c++·算法·动态规划·洛谷
Chester_19993 小时前
CSP202312C.树上搜索
开发语言·数据结构·c++·蓝桥杯·stl
谢斯3 小时前
[unity]如何在unity中添加newtonsoft-json
unity·json·游戏引擎
见叶之秋4 小时前
【C++】String类的使用(含模拟实现)
java·开发语言·c++
天人合一peng5 小时前
Unity标记固定颜色及投影标记
unity
wuminyu5 小时前
Linux内核线程调度与虚拟线程调度机制系统级深度剖析
java·linux·c语言·jvm·c++
xxwxx__6 小时前
深入理解 C++ 继承:从基础语法到底层原理全解析
c++
键盘会跳舞6 小时前
【C++多线程】多线程性能调优:伪共享、缓存行对齐与吞吐优化
c++·多线程·多线程调优