Default Marshaling for Delegates

A managed delegate is marshaled as a COM interface or as a function pointer, based on the calling mechanism:

For platform invoke, a delegate is marshaled as an unmanaged function pointer by default.

For COM interop, a delegate is marshaled as a COM interface of type _Delegate by default. The _Delegate interface is defined in the Mscorlib.tlb type library and contains the Delegate.DynamicInvoke method, which enables you to call the method that the delegate references.

The following table shows the marshaling options for the managed delegate data type. The MarshalAsAttribute attribute provides several UnmanagedType enumeration values to marshal delegates.

Enumeration type

Description of unmanaged format

UnmanagedType.FunctionPtr

An unmanaged function pointer.

UnmanagedType.Interface

An interface of type _Delegate, as defined in Mscorlib.tlb.

Consider the following example code in which the methods of DelegateTestInterface are exported to a COM type library. Notice that only delegates marked with the ref (or ByRef) keyword are passed as In/Out parameters.

A function pointer can be dereferenced, just as any other unmanaged function pointer can be dereferenced.

Note

A reference to the function pointer to a managed delegate held by unmanaged code does not prevent the common language runtime from performing garbage collection on the managed object.

For example, the following code is incorrect because the reference to the cb object, passed to the SetChangeHandler method, does not keep cb alive beyond the life of the Test method. Once the cb object is garbage collected, the function pointer passed to SetChangeHandler is no longer valid.

To compensate for unexpected garbage collection, the caller must ensure that the cb object is kept alive as long as the unmanaged function pointer is in use. Optionally, you can have the unmanaged code notify the managed code when the function pointer is no longer needed, as the following example shows.

internalclass DelegateTest {
CallBackClass cb;
// Called before ever using the callback function.publicstaticvoid SetChangeHandler() {
cb = new CallBackClass();
ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));
}
// Called after using the callback function for the last time.publicstaticvoid RemoveChangeHandler() {
// The cb object can be collected now. The unmanaged code is // finished with the callback function.
cb = null;
}
}