将 C++/CLI 类方法作为 C 函数指针传递

Posted

技术标签:

【中文标题】将 C++/CLI 类方法作为 C 函数指针传递【英文标题】:Passing C++/CLI Class Method as C function pointer 【发布时间】:2012-10-22 11:35:05 【问题描述】:

我有一个提供此标头的第三方 C 库:

//CLibrary.h
#include <Windows.h>
#include <process.h>
typedef void (WINAPI *CLibEventCallback)(int event, void *data);
__declspec(dllexport) bool CLibStart (CLibEventCallback callback, void *data);

// CLibrary.c -- sample implementation
static CLibEventCallback cb;

void _cdecl DoWork (void *ptr)

    for (int i = 0; i < 10; ++i)
    
        cb (i*i, ptr);
        Sleep (500);
    


__declspec(dllexport) bool CLibStart (CLibEventCallback callback, void *data)

    cb = callback; // save address for DoWork thread...
    _beginthread (DoWork, 0, data);
    return true;

我需要创建一个可以调用CLibStart的C++/CLI类,并提供一个类方法作为函数指针。如下所示,这需要使用 GetFunctionPointerForDelegate 来完成。因为删除构造函数包含'this'并且不需要静态方法,所以我不需要将'this'传递给CLibStart。

using namespace System;
using namespace System::Runtime::InteropServices;

namespace Sample 
    public ref class ManagedClass
       
        delegate void CLibraryDelegate (int event, void *data);

    private:
        CLibraryDelegate^ managedDelegate;
        IntPtr unmanagedDelegatePtr;
        int someInstanceData;

    public:
        ManagedClass() 
         
            this->managedDelegate = gcnew CLibraryDelegate(this, &ManagedClass::ManagedCallback);
            this->unmanagedDelegatePtr = Marshal::GetFunctionPointerForDelegate(this->managedDelegate);
            this->someInstanceData = 42;
        

        void Start ()
        
            // since the delegate includes an implicit 'this' (as static function is not needed)
            // I no longer need to pass 'this' in the second parameter!
            CLibStart ((CLibEventCallback) (void *) unmanagedDelegatePtr, nullptr); 
        

    private:
        void Log (String^ msg)
        
            Console::WriteLine (String::Format ("someInstanceData: 0, message: 1", this->someInstanceData, msg));  
        

        void ManagedCallback (int eventType, void *data)
        
            // no longer need "data" to contain 'this'
            this->Log (String::Format ("Received Event 0", eventType));
        
    ;

使用这个 C# 测试器,所有这些都可以正常编译和运行:

using System;
using Sample;

namespace Tester

    class Program
    
        static void Main(string[] args)
        
            var mc = new ManagedClass();
            mc.Start();
            Console.ReadKey();
        
    

样本输出:

Received Event 0
Received Event 1
Received Event 4
Received Event 9
Received Event 16
Received Event 25
Received Event 36
Received Event 49
Received Event 64
Received Event 81

悬而未决的问题:

    我觉得我需要使用 gcroot 和/或 pin_ptr?如果 又怎样?在哪里?

谢谢。

【问题讨论】:

使用 Marshal::GetFunctionPointerForDelegate()。它不必是静态方法。您必须通过存储委托对象来保持其活动状态。 如果您使用@HansPassant 的建议,您可以使用gcroot 模板来保持对象处于活动状态。 谢谢。我已经用似乎有效的解决方案更新了“问题”(在 VS2010 中编译和运行)。但我不确定是否/在哪里/如何使用 gcroot 和/或 pin_ptr。有什么想法吗? C++/CLI类的'log'方法在被回调调用时如何访问隐含的'this'? 这段代码中没有任何东西可以阻止“mc”对象被垃圾回收。这也将收集代表。它现在可以工作,因为调试器将变量的生命周期延长到方法的末尾。但在现实生活中,当这种情况发生时,它会发出响亮的轰鸣声。将对象添加到静态 List 并在保证本机代码停止回调时再次删除它们。 【参考方案1】:

gcroot 应该在 ref 类存储委托的地方,例如:

gcroot<CLibraryDelegate^> managedDelegate;

【讨论】:

不仅不应该,而且不能。 gcroot 是非托管类型,不能是 ref class 的成员。

以上是关于将 C++/CLI 类方法作为 C 函数指针传递的主要内容,如果未能解决你的问题,请参考以下文章

将 C# 函数指针传递到 C++/CLI 互操作 dll

如何将 void* 的引用从 C++/CLI 传递给本机 C 函数

C ++通过引用将`this`传递给方法

从 C++ 传递函数指针以由 C# 调用 - 函数参数包括宽字符字符串 (LPCWSTR)

c ++:将指针传递给基类时访问派生类方法?

C ++:将成员函数作为普通函数指针传递的闭包