【问题标题】:Howto implement callback interface from unmanaged DLL to .net app?如何实现从非托管 DLL 到 .net 应用程序的回调接口?
【发布时间】:2019-02-21 00:07:09
【问题描述】:

在我的下一个项目中,我想为现有的 C++ 代码实现一个 GUI。 我的计划是将 C++ 部分包装在 DLL 中,并在 C# 中实现 GUI。我的问题是我不知道如何实现从非托管 DLL 到托管 C# 代码的回调。我已经在 C# 中进行了一些开发,但托管代码和非托管代码之间的接口对我来说是新的。任何人都可以给我一些提示或阅读技巧或一个简单的例子吗?不幸的是,我找不到任何有用的东西。

【问题讨论】:

    标签: c# c++ callback unmanaged managed


    【解决方案1】:

    您不需要使用 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 无法做到的。一个不错的教程是available here.

    【讨论】:

    • 谢谢 nobugz,我会尽快尝试的。我希望我不会遇到太多麻烦 ;-)
    【解决方案2】:

    P/Invoke 可以处理将托管委托编组为函数指针。因此,如果您公开从 DLL 注册回调函数的 API,并在 C# 中将委托传递给该函数。

    MSDN 上有一个使用 EnumWindows 函数执行此操作的示例。在那篇文章中,请注意第 4 点中的一行:

    但是,如果回调函数可以 在调用返回后调用, 托管调用者必须采取措施 确保代表留在 在回调之前未收集 功能结束。如需详细 有关防止垃圾的信息 集合,请参阅互操作封送处理 使用平台调用。

    这就是说,您需要确保在托管代码完成调用它之后,您的委托不会被垃圾回收,方法是在代码中保留对它的引用或固定它。

    【讨论】:

    • +1 表示关键 - 单独使用 P/Invoke 只能保证调用期间指针的有效性 - 如果要缓存它,则需要固定它(不能说存储引用,我很怀疑,但这并不意味着太多:-)
    【解决方案3】:

    请参阅Marshal.GetFunctionPointerForDelegate,它将为您提供一个函数指针,用于从非托管代码调用托管(即 C# 代码)。

    【讨论】:

      【解决方案4】:

      【讨论】:

        猜你喜欢
        • 2011-02-20
        • 2013-06-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-19
        • 2010-11-28
        • 2011-05-26
        • 1970-01-01
        相关资源
        最近更新 更多