Callback из неуправляемого C++ в C# работает, но только в отладчике

Обратные вызовы в C# из неуправляемого C++ - сложная задача. Большую часть необходимых знаний я почерпнул из этой статьи статья MSDN и этой stackoverflow tip, и результат прекрасно работает в отладчике. Но вне отладчика происходит сбой с сообщением "Object reference not set to an instance of an object".

Вот (упрощенный) код на 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(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 снижает потенциал фрагментации управляемой кучи."

Что не так?

7
задан Community 23 May 2017 в 10:27
поделиться