我目前正在尝试将C#委托封送到C ++函数指针,我查看了example from Microsoft:
// MarshalDelegate1.cpp
// compile with: /clr
#include <iostream>
using namespace System;
using namespace System::Runtime::InteropServices;
#pragma unmanaged
// Declare an unmanaged function type that takes two int arguments
// Note the use of __stdcall for compatibility with managed code
typedef int (__stdcall *ANSWERCB)(int, int);
int TakesCallback(ANSWERCB fp, int n, int m) {
printf_s("[unmanaged] got callback address, calling it...\n");
return fp(n, m);
}
#pragma managed
public delegate int GetTheAnswerDelegate(int, int);
int GetNumber(int n, int m) {
Console::WriteLine("[managed] callback!");
return n + m;
}
int main() {
GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
GCHandle gch = GCHandle::Alloc(fp);
IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
ANSWERCB cb = static_cast<ANSWERCB>(ip.ToPointer());
Console::WriteLine("[managed] sending delegate as callback...");
// force garbage collection cycle to prove
// that the delegate doesn't get disposed
GC::Collect();
int answer = TakesCallback(cb, 243, 257);
// release reference to delegate
gch.Free();
}
对 GCHandle :: Alloc()的调用应该阻止垃圾收集器收集委托。但我的理解是变量 GetTheAnswerDelegate ^ fp 已经使委托保持活动状态,因为它是一个根对象,即使我删除对GCHandle的调用,该示例仍然有效。只有在我内联代理实例化时才会这样:
IntPtr ip = Marshal::GetFunctionPointerForDelegate(gcnew GetTheAnswerDelegate(GetNumber));
然后我看到了崩溃。
微软的例子是错误的,还是我错过了什么?
答案 0 :(得分:7)
您缺少使用调试器对局部变量生命周期的影响。附加调试器后,抖动会将使用的变量标记为方法结束。重要的是使调试可靠。但是这也会阻止GC.Collect()调用收集委托对象。
当您在没有调试器的情况下运行程序的Release版本时,此代码将崩溃。
提供了关于调试构建行为对垃圾收集器的影响的深入解答答案 1 :(得分:2)
'Alloc'调用添加了对委托的引用,这阻止了GC收集它。你必须保持从Alloc返回的句柄,并在完成使用函数指针后调用Free()。代表将在没有给Alloc打电话的情况下进行GC。如果你没有在GCHandle上调用Free(),程序就会泄漏。 在调试器中运行时,内存环境有点不同。 有意义吗?