如何在未托管的DLL和.NET应用程序之间实现回调接口?

24

在我的下一个项目中,我想为已经存在的C++代码实现GUI界面。我的计划是将C++部分封装在DLL中,并使用C#实现GUI。我的问题是我不知道如何从非托管的DLL调用回托管的C#代码。我已经在C#方面做了一些开发,但是托管和非托管代码之间的接口对我来说是新的。有人可以给我一些提示或阅读建议或一个简单的例子作为起点吗?不幸的是,我没有找到任何有用的信息。

4个回答

52
您无需使用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无法做到的。一个不错的教程在此处提供。

谢谢nobugz,我很快就会尝试。希望我不会遇到太多麻烦;-) - chrmue

7
P/Invoke可以处理将托管委托转换为函数指针的操作。因此,如果您公开API来注册从DLL调用回调函数,并在C#中传递一个委托到该函数,则可以使用它。
有一个MSDN上的示例,演示如何使用EnumWindows函数完成这个过程。请注意第4点中提到的一行:
如果回调函数可以在调用返回后被调用,则托管调用方必须采取措施确保委托在回调函数完成之前不会被收集垃圾。有关防止垃圾收集的详细信息,请参见Interop Marshaling with Platform Invoke。
这意味着您需要确保在托管代码完成调用之后,委托不会被垃圾回收,可以通过在代码中保留对其的引用或固定它来实现。

1
+1 表示很重要 - 仅使用 P/Invoke 无法保证指针在调用期间的有效性 - 如果你要缓存它,就需要固定它(不能确定引用是否被隐藏起来,有些可疑,但这意义不大 :-) - Mark Mullin

1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接