注册 C# 委托给 C++ 回调,Marshal.GetFunctionPointerForDelegate 有啥作用?

Posted

技术标签:

【中文标题】注册 C# 委托给 C++ 回调,Marshal.GetFunctionPointerForDelegate 有啥作用?【英文标题】:Register C# delegate to C++ callback, what does Marshal.GetFunctionPointerForDelegate do?注册 C# 委托给 C++ 回调,Marshal.GetFunctionPointerForDelegate 有什么作用? 【发布时间】:2017-07-03 03:48:07 【问题描述】:

我曾经将一个委托从 C# 注册到这样的回调函数指针(不使用 Marshal.GetFunctionPointerForDelegate):

C++ (Test.dll)

typedef void (__stdcall* Callback)(int num);

Callback pCallback = 0;

void __stdcall SetCallback(Callback callback)

    pCallback = callback;


void __stdcall ActionCpp()

    if(pCallback)
    
        pCallback();
    

C#

class Program

    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void SetCallBack(Callback callback);

    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void ActionCpp();

    public delegate void Callback();

    static void Action()
    
        Console.WriteLine("Callback succeeded.");
    

    static void Main(string[] args)
    
        Callback myAction = new Callback(Action);
        SetCallback(myAction);
        ActionCpp(); //Suppose to do the same thing as Action();
    

看来这种方式很好用。但是,我发现我们可以通过使用 Marshal.GetFunctionPointerForDelegate 来做同样的事情,并将委托的 IntPtr 注册到 C++ 中的函数指针。 我想知道有什么区别,哪种做法更好? (还有为什么?提前致谢。)

这是使用 Marshal.GetFunctionPointerForDelegate 时的 C# 代码。 (C++ 代码没有任何变化。)

C#(使用 Marshal.GetFunctionPointerForDelegate)

class Program

    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void SetCallBack(IntPtr pCallBack); //change to type "IntPtr" this time.

    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void ActionCpp();

    public delegate void Callback();

    static void Action()
    
        Console.WriteLine("Callback succeeded.");
    

    static void Main(string[] args)
    
        Callback myAction = new Callback(Action);
        //GCHandle gcHandle = GCHandle.Alloc(myAction); <-- Is this necessary?
        IntPtr pMyAction = Marshal.GetFunctionPointerForDelegate(myAction);
        SetCallback(pMyAction);
        ActionCpp();
        //gcHandle.Free();
    

我还有另一个相关的问题,即是否有必要使用“GCHandle.Alloc”(如上面代码中的我的 cmets)来避免任何 GC 操作,只要我的 Callback myAction 仍然存在? 我是 C# 和 C++ 回调的新手,如果我犯了幼稚的错误,请告诉我。

【问题讨论】:

【参考方案1】:

回调需要处理事件的方法的起始地址。因此,您的两种方法都获得了相同的起始地址。第一种方法很清楚,因为您使用的是方法的名称。第二种方法你有更多的语句可以完成与第一种方法相同的操作。

c# 是托管语言,而 c++ 是非托管语言。这基本上意味着在 c# 中添加了额外的保护以防止 PC 蓝屏。因此,当从 c# 调用非托管 c++ 时,必须遵循规则以防止蓝屏。因此,任何传递给 c++ 方法的指针变量都必须在 c# 中使用 GCHandle.Alloc 或使用其他 Marshal 方法分配为非托管变量。 Marshal.StrToPtr 也会分配非托管内存。

【讨论】:

以上是关于注册 C# 委托给 C++ 回调,Marshal.GetFunctionPointerForDelegate 有啥作用?的主要内容,如果未能解决你的问题,请参考以下文章

从 C++ 到 C# 的结构中的 Marshal 回调

c++或者C 语言中有没有类似C#中事件委托的机制

将带有参数的 c# 回调方法传递给 c++ dll 会导致 System.ExecutionEngineException

C# P/Invoke:可变参数委托回调

如何通过 IntPtr 将 c++ 数组编组为 c#

通过 Marshal.GetFunctionPointerForDelegate 从本机 (C++) 线程调用托管函数 (C#)