Reputation: 14399
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
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