1

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);
}
theateist
  • 13,879
  • 17
  • 69
  • 109
  • The problem isn't on the C++ side, but on the C# side, your reference to the method is being GCed. You need to make sure that the reference to the object containing the callback function doesn't go out of scope until it is guaranteed that the callback won't be called – MindSwipe Aug 23 '19 at 05:38
  • 1
    You're right. I was focused on C++ side and didn't notice it. Don't I need to store the output of `GetFunctionPointerForDelegate` so it won't be desroyed at the end of the scope or it doesn't create anything new, but just gets the address of the C# function? – theateist Aug 23 '19 at 14:23
  • Does this answer your question? [CallbackOnCollectedDelegate in globalKeyboardHook was detected](https://stackoverflow.com/questions/9957544/callbackoncollecteddelegate-in-globalkeyboardhook-was-detected) – StayOnTarget Jan 14 '22 at 15:39

1 Answers1

1

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;
    ...
}
David Yaw
  • 27,383
  • 4
  • 60
  • 93