Nadav's Blog
Calling managed callbacks from native code
|
|
Preface
This article is directed to developers familiar with managed and native programming.
It discusses two approaches for native code to asynchronously call managed code callbacks.
It starts by giving a short explanation of the interop mechanisms used followed by a description of the callback model, it then present two separate techniques of implementing the callback model followed by a code sample, finally, it present
a sample code demonstrating each approach.
Platform Invoke
PInvoke enables unmanaged DLL methods invocation by managed code, by marshaling managed types to the unmanaged domain.
The COM Callable Wrapper
The CCW for COM is the same as the RCW is for managed code, that is, it is exposing a managed class to the unmanaged domain enabling Unmanaged COM object to call its managed code.
The callback model
I will discuss a simple and robust callback mechanism, that is, a callback reference passed as an argument to a method, the method will execute an asynchronous operation, and, when appropriate, will call back the managed code to indicate completion.
Figure 1
There are two ways the above model can be implemented, one is using PInvoke only and the other is using COM Interop.
Using PInvoke it is possible for native code to call upon a managed delegate as demonstrated in the snap bellow.
Figure 2
Figure 2 above demonstrate a managed application implementing a delegate ( CallbackProc ) to be called upon by the native code.
The ‘RegisterCallback’ method is ~imported~ from a Windows native DLL, the method get a single IntPtr argument representing the callback method, upon invocation ‘Marshal.GetFunctionPointerForDelegate’ is used to generate a Native proxy for the ‘OnCallback’ delegate, this Native Proxy is then used by the native code to call back the managed code.
Figure 3
Figure 3 Above demonstrate the Native code used to call back on the managed application, ‘MANAGED_CALLBACK’ define the callback prototype.
The callback prototype MUST CORRESPOND to the managed CallbackProc delegate prototype presented at Figure 2, the .NET framework doesn’t validate correspondence, having mis-matched prototypes might lead to crash and unpredictable behavior, moreover, it is not guaranteed, that the .NET GC will maintain a reference to the managed object owning the delegate, and thus, the GC might collect the managed object BEFORE the Native code has executed the callback method, once the Native code will call back the managed method it’s owner object might already be collected resulting a crash and unpredictable behavior.
To avoid the above mentioned pit-falls, a different approach should be taken, this approach involve COM Interop usage, with COM, each object maintain a reference count, when this reference count hit zero the object is removed from memory, what if, when the managed code was passing the callback reference to the Native application it could also add a reference to the object implementing that method, to achieve that we will have to expose the managed callback as a COM method passing a reference to its object, and thus, maintain a reference count and preventing the GC from collecting the object prematurely.
The following present a simple code snap demonstrating how to achieve the callback model using COM Interop ( later a more robust example will be provided ).

Figure 4
In Figure 4 above a new class is presented, namely the CallbackConsumer class, this class is a COM Object implementing the callback method, the
ComVisible attribute indicates the .NET Framework that this managed class should be exposed to native code as a COM object, and thus, the framework will create a CCW for the class enabling native code to call upon its method while preserving a reference count preventing the object of being removed from memory pre-maturely.
The DispId attribute is used to uniquely identify the method, this id is later used bay native code to call upon the managed method, for detailed explanation regarding DISPIDs please see
this article
Figure 5
The code at Figure 5 above present the native code used to call upon the managed CCW callback, it use the
QueueUserWorkItem windows API to execute the callback in OS thread, to guarantee persistence of the managed callback until it is executed it’s object reference count is raised ( ‘pDisp->AddRef’ ).
When the OS thread executes at ‘AsyncCallback’ the callback in invoked and the previously added reference is released enabling the GC to collect the callback object.
Callback execution might fail, I will specifically cover dealing with DISP_E_EXCEPTION,
click here for explanation of other return codes.
DISP_E_EXCEPTION is returned when the code is throwing an exception not handled in the callback context, in this case, we are dealing with a managed exception (
System.Exception ), it is possible for unmanaged code to resolve the managed exception, that is, to extract managed call stack, message, and more, the following illustrate how to resolve a managed exception in Native code.
Figure 6
COM is using an interface called
IErrorInfo to communicate error information, and thus, is used to report compound error information on our managed callback.
GetErrorInfo is a Windows API used to resolve the Error Information object for the current thread context, IErrorInfo by it-self is not enough to resolve the managed exception information, for that, we need to access the internal Framework API, for this we #import
, this include COM interfaces for core .NET Framework objects and is mostly undocumented, one of the objects for which a COM interface is defined is the basic Exception object, namely, mscorlib::_Exception, in the above code we use this interface to extract the exception information.
Figure 7 bellow present a specialized class implementing ~delegate like~ functionality with the advantage of persisting the callback in GC as long as it is referred by the Native code, the argument list is dynamic and it is up to the developer to make sure managed and native code use the same callback argument types.
Figure 7
The attached sample code demonstrate the two callback models discussed in this article, to demonstrate the limitations discussed in the delegate model rapidly issue asynchronous operations by clicking ‘c’, this will cause the code to create numerous delegate based objects and to collect them prematurely, finally the Native code will except reporting Access Violation.
Rapidly clicking ‘C’ will cause the code to create numerous COM Interop based objects maintaining reference count and preventing premature collection.
Final words
We have presented the limitations of the PInvoke callback model and proposed an alternative model avoiding these limitations.
It is important to note that the COM based callback model is also adequate for Internet Explorer plug-ins to asynchronously call back on script methods ( having in mind apartment considerations ).