簡體   English   中英

從非托管C ++到C#的回調可以正常工作,但僅限於調試器

[英]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();
    }


}

順便說一句,我希望你有更強大的命名系統。 DoCSharpTheCallBacktheCallBack等名字讓我很難理解這個問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM