Обратные вызовы в 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 уменьшает потенциал фрагментации управляемой кучи. "
Что не так?