You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从非托管C++ DLL调用C# Windows Forms中的委托?

反向PInvoke实现方案(无需新增CLR DLL)

可以实现从非托管C++ DLL到.NET WinForms应用的反向调用,无需新增启用CLR编译的中间DLL,核心思路是通过函数指针传递委托,具体步骤如下:

  • 定义匹配的签名

    1. .NET端定义与非托管回调函数签名一致的委托,指定正确的调用约定:
      [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
      public delegate void OutputCallback(string data);
      
    2. 非托管C++的NetPcapWrap中,声明回调函数指针类型并暴露设置回调的导出函数:
      // 定义回调函数指针类型
      typedef void(*OutputCallbackFunc)(const char*);
      // 全局变量保存回调指针
      static OutputCallbackFunc g_outputCallback = nullptr;
      
      // 导出设置回调的函数
      extern "C" __declspec(dllexport) void SetOutputCallback(OutputCallbackFunc callback) {
          g_outputCallback = callback;
      }
      
  • 传递委托到非托管代码
    .NET WinForms中创建委托实例,通过Marshal.GetFunctionPointerForDelegate获取非托管函数指针,再通过PInvoke传递给NetPcapWrap。需将委托实例保存在类成员变量中,避免被GC回收:

    private OutputCallback _callbackInstance;
    
    public void InitializeCallback() {
        _callbackInstance = new OutputCallback(OnOutputReceived);
        IntPtr callbackPtr = Marshal.GetFunctionPointerForDelegate(_callbackInstance);
        SetOutputCallback(callbackPtr);
    }
    
    private void OnOutputReceived(string data) {
        // WinForms跨线程更新UI需切换到UI线程
        if (this.InvokeRequired) {
            this.Invoke(new Action<string>(OnOutputReceived), data);
            return;
        }
        // 此处编写UI更新逻辑,如追加文本到控件
        txtOutput.AppendText(data + Environment.NewLine);
    }
    
    // PInvoke声明SetOutputCallback
    [DllImport("NetPcapWrap.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void SetOutputCallback(IntPtr callback);
    
  • 非托管代码触发回调
    修改NetPcapWrap中调用npcap循环函数的逻辑,将原本输出到std::out的内容通过保存的回调指针传递给.NET端:

    // 替换原std::cout输出逻辑
    if (g_outputCallback != nullptr) {
        const char* cStr = data.c_str(); // 将std::string转为C风格字符串
        g_outputCallback(cStr);
    }
    
  • 关键注意事项

    • 调用约定必须完全匹配:.NET委托的CallingConvention要与非托管函数指针的调用约定一致,常用Cdecl(C/C++默认)或StdCall。
    • 保留委托强引用:必须在.NET端维持委托实例的存活(如存储为成员变量),否则GC回收后非托管调用会触发崩溃。
    • 跨线程UI处理:非托管回调通常在后台线程执行,WinForms更新UI必须使用Invoke/BeginInvoke切换到UI线程。

内容的提问来源于stack exchange,提问作者MtnManChris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 17:22:22