如何从非托管C++ DLL调用C# Windows Forms中的委托?
反向PInvoke实现方案(无需新增CLR DLL)
可以实现从非托管C++ DLL到.NET WinForms应用的反向调用,无需新增启用CLR编译的中间DLL,核心思路是通过函数指针传递委托,具体步骤如下:
定义匹配的签名
- .NET端定义与非托管回调函数签名一致的委托,指定正确的调用约定:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void OutputCallback(string data); - 非托管C++的NetPcapWrap中,声明回调函数指针类型并暴露设置回调的导出函数:
// 定义回调函数指针类型 typedef void(*OutputCallbackFunc)(const char*); // 全局变量保存回调指针 static OutputCallbackFunc g_outputCallback = nullptr; // 导出设置回调的函数 extern "C" __declspec(dllexport) void SetOutputCallback(OutputCallbackFunc callback) { g_outputCallback = callback; }
- .NET端定义与非托管回调函数签名一致的委托,指定正确的调用约定:
传递委托到非托管代码
.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线程。
- 调用约定必须完全匹配:.NET委托的
内容的提问来源于stack exchange,提问作者MtnManChris
相关产品推荐
相关产品推荐

