NGWS SDK Documentation  

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

The Custom Marshaler

The custom marshal is a class that activates and deactivates a specific custom wrapper as necessary. The marshaler class must implement the ICustomMarshaler interface defined in the System.Runtime.InteropServices namespace.

The ICustomMarshaler Interface

The custom marshaler implements the ICustomMarshaler interface in order to provide the runtime’s Interop service with the appropriate wrappers. In order to use a custom marshaler, an attribute is associated with a parameter or field being marshaled. The attribute identifies the custom marshaler that knows how to activate the appropriate wrapper. The runtime’s Interop service then examines the attribute and creates the custom marshaler the first time the argument needs to be marshaled. It calls on the custom marshaler’s MarshalNativeToManaged and MarshalManagedToNative methods to activate the correct wrapper to handle the call.

The ICustomMarshaler Inteface
namespace System.Runtime.InteropServices;

public interface ICustomMarshaler {
       Object *MarshalNativeToManaged( int UnmanagedType );
       int MarshalManagedToNative( Object *ManagedType );
       void CleanUpNativeData( int UnmanagedType );
       void CleanUpManagedData( Object *ManagedType );
       static Guid GetNativeTypeGuid();
       static Type GetManagedType();
      static ICustomMarshaler *GetInstance(String *pstrCookie);
}

The MarshalNativeToManaged method returns a custom RCW that can marshal the unmanaged interface passed as an argument. The marshaler should return an instance of the custom RCW for that type. This method is called by the runtime in order to obtain the wrapper the first time a call requiring marshaling is made from managed code into unmanaged code.

The MarshalManagedToNative method returns a custom CCW that can marshal the managed interface passed as an argument. The marshaler should return an instance of the custom CCW for that type. This method is called by the runtime in order to obtain the wrapper the first time a call requiring marshaling is made from unmanaged code into managed code.

The CleanUpNativeData method provides an opportunity for the custom marshaler to perform any necessary cleanup when the marshaler is no longer needed.

The CleanUpManagedData method provides an opportunity for the custom marshaler to perform any necessary cleanup when the marshaler is no longer needed.

In addition, the custom marshaler must also implement three static methods. The GetInstance method is called to retrieve an instance of the custom marshaler. This method can either create a new instance of the custom marshaler every time or it can always use the same one if the custom marshaler is stateless. The method is called by the marshaling service to create the marshaler whenever a parameter of field is marked with the UnmanagedType.CustomMarshaler (see below)

The static methods GetNativeTypeGuid and GetManagedType return information about the managed and unmanaged types that the marshaler knows how to bridge. The managed type information is returned as a System.Type object. The unmanaged type information is returned as a Guid. The Guid that’s returned from GetNativeTypeGuid should be the IID of the COM interface being marshaled.

The cookie passed to GetInstance is explained later.

Sample Custom Marshaler

The runtime uses the custom marshaler to create custom wrappers as they are needed. A separate custom marshaler is typically used for each type being marshaled. For example, the NewOldMarshaler shown below would be used to activate the NewToOldWrapper or the OldToNewWrapper defined above.

In NewOldMarshaler.cpp
#using <mscorlib.dll>
#include “New.h”
#include “Old.h”
#include “Wrappers.h”

__gc class NewOldMarshaler :public ICustomMarshaler 
{
private:
   NewOldMarshaler() {};
   static NewOldMarshaler *m_pMarshaler = NULL;

   Object *MarshalNativeToManaged( int pUnmanagedObj)   {
      NewToOldWrapper *crcw = new NewToOldWrapper(pUnmanagedObj);
      return dynamic_cast<Object *>(crcw);
   };

   int MarshalManagedToNative( Object *pManagedObj )    {
      IOld *pio;
      OldToNewWrapper *cccw = 
               new OldToNewWrapper(dynamic_cast<INew *>(pManagedObj));
      HRESULT hr = cccw->QueryInterface(IID_IOld, (void **)&pio);
      return (int)(pio);
   };   

   void CleanUpNativeData(int i) {
   };

   void CleanUpManagedData(Object *o) {
   };

   static Guid GetNativeTypeGuid() {
      return Guid(IID_IOLD);
   };

   static Type GetManagedType() {
      return Type.GetType(“INew”);
   };

   static ICustomMarshaler *GetInstance(String *pstrCookie)   {
      if (!m_pNewOldMarshaler)
         m_pNewOldMarshaler = new NewOldMarshaler ;
      return m_pNewOldMarshaler;
   };
}

The static method GetInstance is called by the runtime when an instance of the custom marshaler is needed. The implementation shown above ensures that only a single instance of the marshaler is created. As long as the marshaler does not retain state, using this approach is recommended.

The constructor for the marshaler is private to ensure that class is not created anywhere other than thru the GetInstance method.

The MarshalNativeToManaged method is responsible for creating a wrapper that implements the managed interface. The implementation shown above simply constructs an instance of the NewToOldWrapper and returns it to the caller.

The MarshalManagedToNative method creates a wrapper that implements the unmanaged interface. The implementation shown above constructs an instance of the OldToNewWrapper. It then queries the wrapper of its IOld interface and returns the interface to the caller.