[英]Callback from unmanaged C++ to C# works, but only in debugger
從非托管C ++回調C#很棘手。 我從這篇MSDN文章和這個stackoverflow技巧中了解了大部分所需的內容 ,結果在調試器中運行良好。 但是在調試器之外它失敗了“對象引用沒有設置為對象的實例”。
這是(簡化的)C#代碼:
class CSharpCode
{
delegate void CallbackDelegate();
void DoCSharp()
{
CallbackDelegate callbackDelegate = TheCallback;
IntPtr callbackDelegatePointer = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
GCHandle gchCallbackDelegate = GCHandle.Alloc(callbackDelegatePointer);
GC.Collect(); // create max space for unmanaged allocations
CppCliCode.DoCppCli(callbackDelegatePointer);
}
public static void TheCallback()
{
MessageBox.Show("It worked");
}
}
這是C ++代碼:
#pragma managed
public ref class CppCliCode
{
static void DoCppCli(IntPtr^ callbackDelegatePointer)
{
callback theCallback = static_cast<callback>(callbackDelegatePointer->ToPointer());
DoCpp(theCallback);
}
}
#pragma unmanaged
typedef void (__stdcall *callback)();
void DoCpp(callback theCallback)
{
theCallback();
}
錯誤發生在調用theCallback()
和到達TheCallback()
。 該錯誤表明某些不可見的托管對象已變為null
。
如果我刪除GC.Collect(),問題就會消失。 但這只是意味着它有一天會再次出現,因為GC恰好在錯誤的時刻發生。
GCHandle保護代表不被收集,但允許重新定位。 MSDN文章說“如果一個委托由垃圾收集重新定位,它不會影響底層托管回調,因此Alloc用於添加對委托的引用,允許重定位委托,但防止丟棄。使用GCHandle而不是pin_ptr減少了托管堆的碎片潛力。“
怎么了?
您必須分配委托本身,而不是其IntPtr
。 另外你必須釋放GCHandle
當你與完成CSharpCode
實例。
class CSharpCode : IDisposible
{
delegate void CallbackDelegate();
GCHandle gchCallbackDelegate;
void DoCSharp()
{
CallbackDelegate callbackDelegate = TheCallback;
IntPtr callbackDelegatePointer = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
gchCallbackDelegate = GCHandle.Alloc(callbackDelegate); // !!!!
GC.Collect(); // create max space for unmanaged allocations
CppCliCode.DoCppCli(callbackDelegatePointer);
}
public void Dispose()
{
CleanUp();
}
~CSharpCode()
{
CleanUp();
}
CleanUp()
{
if(gchCallbackDelegate.IsAllocated)
gchCallbackDelegate.Free();
}
}
順便說一句,我希望你有更強大的命名系統。 像DoCSharp
, TheCallBack
, theCallBack
等名字讓我很難理解這個問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.