C#注册回调给C++使用

1. C++ 端核心设计:存储回调函数指针

存储回调指针

// MyNativeLib.cpp
#include <cstdio>
#include <mutex>      // 用于线程安全

// 1. 定义函数指针类型(必须与C#委托签名完全一致)
typedef int (__cdecl *Callback)(int a, int b);

// 2. 全局变量:存储回调指针(初始为空)
static Callback g_callback = nullptr;

// 3. 互斥锁:防止C#在注册/注销时,C++刚好在调用,导致崩溃
static std::mutex g_mutex;

2. 导出给 C# 调用的注册、注销、触发函数

C++端需要导出几个关键函数,供C#调用。

extern "C" {

    // 【C#调用】注册回调
    __declspec(dllexport) void RegisterCallback(Callback cb) {
        std::lock_guard<std::mutex> lock(g_mutex);
        g_callback = cb;
        printf("C++: Callback registered successfully.\n");
    }

    // 【C#调用】注销回调(非常重要!防止C++持有悬空指针)
    __declspec(dllexport) void UnregisterCallback() {
        std::lock_guard<std::mutex> lock(g_mutex);
        g_callback = nullptr;
        printf("C++: Callback unregistered.\n");
    }

    // 【C#调用】模拟“C++主动触发事件”,让C++调用C#的回调
    // 实际项目中,这个函数可能是被C++内部线程、网络事件、定时器自动调用的
    __declspec(dllexport) void TriggerEvent(int a, int b) {
        // 先加锁拷贝一份,避免在调用期间C#那边注销导致指针失效
        Callback temp_cb = nullptr;
        {
            std::lock_guard<std::mutex> lock(g_mutex);
            temp_cb = g_callback;
        }

        if (temp_cb != nullptr) {
            int result = temp_cb(a, b);  // 这里正式回调 C# 方法
            printf("C++: Callback invoked, result from C# = %d\n", result);
        } else {
            printf("C++: Triggered but no callback registered.\n");
        }
    }
}

 

C#使用

 // ========== 1. 定义与C++函数指针匹配的委托 ==========
    // 必须使用 [UnmanagedFunctionPointer] 指定调用约定,与C++端 __cdecl 一致
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int CallbackDelegate(int a, int b);

    // ========== 2. 将委托实例保存为静态字段,防止GC回收 ==========
    private static CallbackDelegate _callbackInstance;

    // ========== 3. 导入C++ DLL中的函数 ==========
    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void RegisterCallback(CallbackDelegate cb);

    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void UnregisterCallback();

    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void TriggerEvent(int a, int b);

    

    // ========== 4. C# 回调方法的实现 ==========
    private static int MyCallbackMethod(int a, int b)
    {
        // 注意:此方法可能由C++的任意线程调用
        Console.WriteLine($"[C#] Callback invoked with a={a}, b={b} on thread {Thread.CurrentThread.ManagedThreadId}");
        
        // 模拟业务处理
        int result = a + b;
        
        // 如果需要在UI线程更新界面,需要通过 SynchronizationContext 或 Invoke 封送
        // 这里只是演示,不做跨线程操作
        
        return result;
    }

    // ========== 5. 主程序 ==========
    static void Main()
    {
        // 注册回调:将方法包装为委托,并保存到静态字段
        _callbackInstance = new CallbackDelegate(MyCallbackMethod);
        RegisterCallback(_callbackInstance);
        Console.WriteLine("[C#] Callback registered.");

        // 手动触发一次回调(演示)
        Console.WriteLine("[C#] Triggering event from C#...");
        TriggerEvent(10, 20);

        

        // ========== 6. 程序退出前注销回调,防止C++持有悬空指针 ==========
        UnregisterCallback();
        Console.WriteLine("[C#] Callback unregistered.");

        // (可选)等待C++线程完全结束,如果有的话,可以通过事件或标志位实现
        // 这里简单等待一下
        Thread.Sleep(500);
    }

 

posted @ 2026-08-04 18:40  家煜宝宝  阅读(3)  评论(0)    收藏  举报