注册 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++ dll 会导致 System.ExecutionEngineException
通过 Marshal.GetFunctionPointerForDelegate 从本机 (C++) 线程调用托管函数 (C#)