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

  • 错误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;`
相关推荐
白杨尚青2 小时前
C++进阶篇(二):多态——虚函数、重写、虚函数表与三个经典坑(附完整代码仓库)
开发语言·c++·多态·虚函数·重写·动态绑定
syagain_zsx2 小时前
spdlog实战:日志分级、滚动与性能调优
linux·c++·日志库·二次开发日志库
weixin_3077791320 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab
AI wins20 小时前
c++新增頭文件
c++
Shadow(⊙o⊙)20 小时前
仿muduo实现高并发服务器——事件就绪管理Channel.hpp
服务器·c++
by2099921 小时前
哈希表——快速找到数据:冲突、删除与扩容的实现
数据结构·c++·哈希算法·散列表
音草21 小时前
C++ 继承详解:从概念到菱形继承
c++
weixin_307779131 天前
C++代码实现MATLAB中的spectralcluster函数功能
开发语言·c++·算法·matlab
xiaoye-duck1 天前
《C++ 项目:手写大模型接入 SDK》Trae IDE 开发环境搭建与依赖管理
c++·ide·语言模型
沫璃染墨1 天前
《Qt从零入门系列(十四):QThread多线程详解——从线程创建到客户端阻塞处理》
开发语言·c++·笔记·qt·ui·交互·线程