在我的下一个项目中,我想为 C++ 中现有的代码实现一个 GUI。我的计划是将 C++ 部分包装在 DLL 中,并在 C# 中实现 GUI。我的问题是我不知道如何实现从非托管 DLL 到托管 C# 代码的回调。我已经在 C# 中进行了一些开发,但托管代码和非托管代码之间的接口对我来说是新的。任何人都可以给我一些提示或阅读技巧或一个简单的例子吗?不幸的是,我找不到任何有用的东西。
4 回答
您不需要使用 Marshal.GetFunctionPointerForDelegate(),P/Invoke marshaller 会自动执行此操作。您需要在 C# 端声明一个委托,其签名与 C++ 端的函数指针声明兼容。例如:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
class UnManagedInterop {
private delegate int Callback(string text);
private Callback mInstance; // Ensure it doesn't get garbage collected
public UnManagedInterop() {
mInstance = new Callback(Handler);
SetCallback(mInstance);
}
public void Test() {
TestCallback();
}
private int Handler(string text) {
// Do something...
Console.WriteLine(text);
return 42;
}
[DllImport("cpptemp1.dll")]
private static extern void SetCallback(Callback fn);
[DllImport("cpptemp1.dll")]
private static extern void TestCallback();
}
以及用于创建非托管 DLL 的相应 C++ 代码:
#include "stdafx.h"
typedef int (__stdcall * Callback)(const char* text);
Callback Handler = 0;
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int retval = Handler("hello world");
}
这足以让你开始使用它。有一百万个细节会让你陷入困境,你一定会遇到其中的一些。让这种代码运行的更有效的方法是用 C++/CLI 语言编写一个包装器。这也可以让你包装一个 C++ 类,这是 P/Invoke 无法做到的。这里有一个不错的教程。
P/Invoke 可以处理将托管委托编组到函数指针。因此,如果您公开从 DLL 注册回调函数的 API,并在 C# 中将委托传递给该函数。
MSDN上有一个使用 EnumWindows 函数执行此操作的示例。在那篇文章中,请注意第 4 点中的一行:
但是,如果可以在调用返回后调用回调函数,则托管调用者必须采取措施确保委托在回调函数完成之前保持未被收集。有关防止垃圾收集的详细信息,请参阅使用平台调用的互操作封送处理。
这就是说,您需要确保在托管代码完成调用它之前不会对您的委托进行垃圾收集,方法是在代码中保留对它的引用或固定它。
请参阅Marshal.GetFunctionPointerForDelegate,它将为您提供一个函数指针,用于从非托管代码调用托管(即 C# 代码)。