2013-05-14 101 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(),程序將會泄漏。 在調試器中運行時,內存環境有點不同。 有意義嗎?