theateist
theateist

Reputation: 14399

Garbage Collector error when passing C# function to CLI/C++ code

I try to pass a C# function pointer into CLI/C++ code, just like here? When I use the code below I get the following error

 A callback was made on a garbage collected delegate of type 'MyProject!MyClass.CallbackFunc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.

C# Project A

private void Init()
{
   MyWrapper.Instance.SetCallbackFunc(MyFunc);
}

private void MyFunc(int id, bool success)
{

}

C++/CLI project B

//MyWrapper.cpp
public delegate void CallbackFunc(int, bool);

System::Void SetCallbackFunc(CallbackFunc^ callback)
{
    System::IntPtr ip = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callback);
    auto cb = static_cast<void(*)(int, bool)>(ip.ToPointer());      
    unmanagedCppClass->SetCallbackFunc(cb);
 }

unmanagedCppClass.cpp

typedef void(*funcPtr)(int, bool);
void SetCallbackFunc(funcPtr callback)
{
}

I tried to make ip and cb global, so GC won't collect but it didn't help

System::IntPtr ip;
funcPtr cb;
System::Void SetCallbackFunc(CallbackFunc^ callback)
{
    ip = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callback);
    cb = static_cast<funcPtr>(ip.ToPointer());      
    unmanagedCppClass->SetCallbackFunc(cb);
}

Upvotes: 2

Views: 1568

Answers (1)

David Yaw
David Yaw

Reputation: 27864

You need to keep the delegate object around so that it doesn't get garbage collected. The important object to keep around is the managed delegate object, not the unmanaged function pointer.

In your current implementation, when you call SetCallbackFunc(MyFunc), a delegate object is created from MyFunc, and that delegate object is passed to the C++/CLI method. When SetCallbackFunc returns, there are no more references to the delegate object, so it gets collected. The result of GetFunctionPointerForDelegate is a dumb raw function pointer, it has no way to keep the managed object alive, so keeping that function pointer around does nothing.

You can keep the delegate alive in either C# or C++/CLI.

In C#:

private CallbackFunc keepalive_myfunc;

private void Init()
{
    keepalive_myfunc = MyFunc;
    MyWrapper.Instance.SetCallbackFunc(keepalive_myfunc);
}

Or in C++/CLI:

CallbackFunc^ keepalive_callback;

System::Void SetCallbackFunc(CallbackFunc^ callback)
{
    keepalive_callback = callback;
    ...
}

Upvotes: 4

Related Questions