C++/CLI - Pin a function pointer
-
I need to pass a managed callback to an unmanaged TCP receiver. Since its a thread that needs to exist for the lifetime of the application, I need to prevent it from getting garbage collected. I have read everywhere that pinning function pointers is not required and the GCHandle.Alloc will do the job of preventing garbage collection. But is this a given? I have seen that the AppPool hosting this code crashes with an access violation. Why should I not suspect the fact that this error occurs because the function pointer was garbage collected? Why does pinning the pointer as below reduce the number of crashes?
typedef void (__cdecl *ProcMessageFunc)(void* param, void* paramBuf, ULONG bufSize);
FuncDelegate^ fp = gcnew MessageFuncDelegate(this, &Handler);
pin_ptr<MessageFuncDelegate^> pinnedFunctionPointer = &fp;
ret = Receiver ((ProcMessageFunc)pinnedFunctionPointer); -
I need to pass a managed callback to an unmanaged TCP receiver. Since its a thread that needs to exist for the lifetime of the application, I need to prevent it from getting garbage collected. I have read everywhere that pinning function pointers is not required and the GCHandle.Alloc will do the job of preventing garbage collection. But is this a given? I have seen that the AppPool hosting this code crashes with an access violation. Why should I not suspect the fact that this error occurs because the function pointer was garbage collected? Why does pinning the pointer as below reduce the number of crashes?
typedef void (__cdecl *ProcMessageFunc)(void* param, void* paramBuf, ULONG bufSize);
FuncDelegate^ fp = gcnew MessageFuncDelegate(this, &Handler);
pin_ptr<MessageFuncDelegate^> pinnedFunctionPointer = &fp;
ret = Receiver ((ProcMessageFunc)pinnedFunctionPointer);Difficult to say from what you have shown. You can pin a handle to a managed object only temporarily, since a pin_ptr can only be created on the stack. Therefore, if you have something like this:
IntPtr YourClass::GetFuncPointer()
{
FuncDelegate^ fp = gcnew MessageFuncDelegate(this, &Handler);
pin_ptr pinnedFunctionPointer = &fp;
return ((ProcMessageFunc)pinnedFunctionPointer);
}then you effectively have only pinned fp for the duration of this method call. A correct way to do this depends on your scenario. I'd recommend something along the lines of the following (I didn't compile it, but you get the idea):
public ref class ClientRegistrar abstract sealed {
static FuncDelegate^ s_ManagedReceiverList;// ... public static void RegisterClient(YourClient^ p\_Client) { // thread safety for free s\_ManagedReceiverList += gcnew MessageFuncDelegate(p\_Client, &YourClient::Handler); // here I assume that ::Receiver is an unmanaged global function which // takes a funtion pointer of type ProcMessageFunc. Don't know // if you need to deal with multiple registrations.... ::Receiever((ProcMessageFunc)Marshal::GetFunctionPointerForDelegate(s\_ManagedReceiver)); } public static void UnregisterClient(YourClient^ p\_Client) { s\_ManagedReceiverList -= gcnew MessageFuncDelegate(p\_Client, &YourClient::Handler); // use the same schema to unregister from the native receiver }
}
Usage (C#):
YourClient tClient = new YourClient();
// ...
ClientRegistrar.RegisterClient(tClient);
// ...s_ManagedReceiverList
and anyYourClient
instance you register viaRegisterClient
will be kept alive as long as the current application domain will exist. ImplementUnregisterClient
if you need more fine grained control. A delegate does not need to be pinned, the CLR takes care of proper handling of that. It just needs to be kept alive. Cheers, Paul