2013-05-14 74 views
4

我目前正在试图获取一个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)); 

然后我看到崩溃。

所以是微软的例子错误还是我错过了什么?

回答

7

您错过了使用调试器对本地变量生存期的影响。在附加调试器的情况下,抖动将标记正在使用的变量,直到方法结束。重要的是要使调试可靠。但是,这也会阻止GC.Collect()调用收集委托对象。

当您在没有调试器的情况下运行程序的Release版本时,此代码会崩溃。

上调试的影响

在深入答案建立在垃圾收集器的行为是在this post

+0

你是完全正确的!即使在调试器中运行发布版本(无GCHandle :: Alloc())也会崩溃。谢谢! – BugSlayer 2013-05-14 13:57:16

1

可用“的Alloc”呼叫添加到委托,这防止了从GC收集对它的引用。您必须保持Alloc返回的句柄,并在完成使用函数指针时调用Free()。代表将在没有致电Alloc的情况下进行GC调查。如果您没有在GCHandle上调用Free(),程序将会泄漏。 在调试器中运行时,内存环境有点不同。 有意义吗?