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 天前
c++练习题-双分支
c++
司徒轩宇1 天前
C++ 内存分配详解
开发语言·c++
alibli1 天前
一文学会设计模式之创建型模式及最佳实现
c++·设计模式
️停云️1 天前
C++类型转换、IO流与特殊类的设计
c语言·开发语言·c++
进击的荆棘1 天前
C++起始之路——类和对象(下)
开发语言·c++
liu****1 天前
10.排序
c语言·开发语言·数据结构·c++·算法·排序算法
快乐的划水a1 天前
std::thread与pthread关系
c++
_OP_CHEN1 天前
【算法基础篇】(三十二)动态规划之背包问题扩展:从多重到多维,解锁背包问题全场景
c++·算法·蓝桥杯·动态规划·背包问题·算法竞赛·acm/icpc
Studying 开龙wu1 天前
Windos 10系统安装OpenPose的CPU版本过程说明和Release版本直接使用
c++·windows
温柔の敲代码1 天前
从微观到宏观了解C++项目的编译
开发语言·c++