P/Invoking C++ callback functions crashes when .NET application is not in focus
A C++ DLL exports one function having its only parameter as a pointer to another function:
#define AFX_EXT_CLASS __declspec(dllexport)
typedef void (CALLBACK* CALLBACKFUNC)(INT param1);
AFX_EXT_CLASS INT TestCallback(CALLBACKFUNC lpCallback);
INT TestCallback(CALLBACKFUNC lpCallback)
{
MessageBox(GetActiveWindow(), L“Press OK to make the callback”, L“Testing”, MB_OK);
lpCallback(1);
}
From .NET code, P/invoke the function TestCallback()
Public Delegate Sub MyCallback(ByVal param1 As Integer)
<DllImport(DllName)> _
Public Function TestCallback(ByVal lpCallback as MyCallback) As Integer
End Function
Public Function MyCallbackFunc(ByVal param1 As Integer) As Integer
MessageBox.Show(“Callback works. Param = ” + param1.ToString)
End sub
TestCallback(AddressOf MyCallbackFunc)
It seems to work fine for a while. However, if the C++ function TestCallback calls lpCallback() when the .NET application does not have focus, or after it’s been running for sometime, the .NET application will crash.
The reason is probably that the callback delegate has been garbage collected and the function pointer that was passed to unmanaged code has been invalidated. You have to ensure that the delegate is alive for as long as the function pointer is used, by holding a reference to it. This means you can’t create the delegate inline in the same way it’s done in the above code sample:
TestCallback(AddressOf MyCallback) ‘VB.NET
TestCallback(new MyCallback(MyCallback) //C#
The correct declaration would be:
Dim MyCallbackEvent As MyCallback = AddressOf CallbackHandler
TestCallback(MyCallbackEvent)
It is advised to keep these delegate as global variables, otherwise GC.Collect() and GC.KeepAlive() may be needed