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

Passing C++/CLI Class Method as C function pointer

提问人:Tony 提问时间:11/2/2012 最后编辑:RobᵩTony 更新时间:7/1/2021 访问量:5287

问:

我有一个提供此标头的第三方 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

悬而未决的问题:

  1. 我有一种感觉,我需要使用 gcroot 和/或 pin_ptr?如果 那么,如何?哪里?

谢谢。

函数 回调 C++-CLI

评论

3赞 Hans Passant 11/2/2012
使用 Marshal::GetFunctionPointerForDelegate()。它不一定是静态方法。您必须通过存储委托对象来使委托对象保持活动状态。
0赞 Alexandre C. 11/2/2012
如果使用 @HansPassant 的建议,则可以使用模板使对象保持活动状态。gcroot
0赞 Tony 11/2/2012
谢谢。我已经用一个似乎有效的解决方案更新了“问题”(在VS2010中编译和运行)。但我不确定是否/在哪里/如何使用 gcroot 和/或pin_ptr。有什么想法吗?
0赞 Tony 11/2/2012
当回调调用隐式“this”时,C++/CLI 类的“log”方法如何访问它?
1赞 Hans Passant 11/3/2012
此代码中没有任何内容可以阻止“mc”对象进行垃圾回收。这也将收集委托。它现在有效,因为调试器将变量的生存期延长到方法的末尾。但在现实生活中,当这种情况发生时,它会发出响亮的咔嚓声。将对象添加到静态列表<>并在保证本机代码停止进行回调时再次删除它们。

答:

-1赞 whats_wrong_here 8/1/2019 #1

GCReate 应该位于 ref 类存储委托的位置,例如:

gcroot<CLibraryDelegate^> managedDelegate;

评论

0赞 Ben Voigt 7/1/2021
它不仅不应该,而且不能。 是非托管类型,不能是 的成员。gcrootref class