Architecture of Custom Object Marshaling

Imagine that we are presently in a piece of code whose job it is to marshal an interface pointer that it has in hand. For clarity, in what follows we'll refer to this piece of code as the original marshaling stub. The general case is that the original marshaling stub does not statically3. know the particular interface identifier (IID) to which the pointer conforms; the IID may be passed to this code as a second parameter. This is a common paradigm in the Component Object Model. Extant examples of this paradigm include:


IUnknown::QueryInterface(REFIID riid, void** ppvObject);
IOleItemContainer::GetObject(..., REFIID riid, void** ppvObject);
IClassFactory::CreateInstance(..., REFIID riid, void** ppvNewlyCreatedObject);

Let us assume the slightly less general case where the marshaling stub in fact does know a little bit about the IID: that the interface in fact derives from IUnknown. This is a requirement for remoting: it is not possible to remote interfaces which are not derived from IUnknown.

To find out whether the object to which it has an interface supports custom marshaling, the original marshaling stub simply does a QueryInterface() for the interface IMarshal. That is, an object signifies that it wishes to do custom marshaling simply by implementing the IMarshal interface. IMarshal is defined as follows:


[
    local,
    object,
    uuid(00000003-0000-0000-C000-000000000046)
]
interface IMarshal : IUnknown {
   HRESULT GetUnmarshalClass ( [in] REFIID riid, [in, unique] void *pv, 
      [in] DWORD dwDestContext, [in, unique] void *pvDestContext,
      [in] DWORD mshlflags, [out] CLSID *pCid);
   HRESULT GetMarshalSizeMax ([in] REFIID riid, [in, unique] void *pv,
      [in] DWORD dwDestContext, [in, unique] void *pvDestContext,
       [in] DWORD mshlflags, [out] DWORD *pSize);
   HRESULT MarshalInterface ([in, unique] IStream *pStm, [in] REFIID riid, [in, unique] void *pv,
      [in] DWORD dwDestContext, [in, unique] void *pvDestContext, [in] DWORD mshlflags);
   HRESULT UnmarshalInterface ( [in, unique] IStream *pStm, [in] REFIID riid, [out] void **ppv);
   HRESULT ReleaseMarshalData ( [in, unique] IStream *pStm);
   HRESULT DisconnectObject ( [in] DWORD dwReserved);
}

The idea is that if the object says "Yes, I do want to do custom marshaling" that the original marshaling stub will use this interface in order to carry out the task. The sequence of steps that carry this out is:

  1. Using GetUnmarshalClass, the original marshaling stub asks the object which kind of (such as which class of) proxy object it would like to have created on its behalf in the client process.
  2. (optional on the part of the marshaling stub) Using GetMarshalSizeMax, the stub asks the object how big of a marshaling packet it will need. When asked, the object will return an upper bound on the amount of space it will need.4.
  3. The marshaling stub allocates a marshaling packet of appropriate size, then creates an IStream* which points into the buffer. Unless in the previous step the marshaling stub asked the object for an upper bound on the space needed, the IStream* must be able to grow its underlying buffer dynamically as IStream::Write calls are made.
  4. The original marshaling stub asks the object to marshal its data using MarshalInterface.
We will discuss the methods of this interface in detail later in this chapter.

At this point, the contents of the memory buffer pointed to by the IStream* together with the class tag returned in step 1 comprises all the information necessary in order to be able to create the proxy object in the client process. It is the nature of remoting and marshaling that original marshaling stubs such as we have been discussing know how to communicate with the client process; recall that we are assuming that an initial connection between the two processes had already been established.

The marshaling stub now communicates to the client process, by whatever means is appropriate, the class tag and the contents of the memory that contains the marshaled interface pointer. In the client process, the proxy object is created as an instance of the indicated class using the standard COM instance creation paradigm. IMarshal is used as the initialization interface; the initialization method is IMarshal::UnmarshalInterface(). The unmarshaling process looks something like the following:


void ExampleUnmarshal(CLSID& clsidProxyObject, IStream* pstm, IID& iidOriginallyMarshalled, void** ppvReturn)
{
IClassFactory* pcf;
IMarshal* pmsh;
CoGetClassObject(clsidProxyObject, CLSCTX_INPROC_HANDLER,  NULL, IID_IClassFactory, (void**)&pcf);
pcf->CreateInstance(NULL, IID_IMarshal, (void**)pmsh);
pmsh->UnmarshalInterface(pstm, iidOriginallyMarshalled, ppvReturn);
pmsh->ReleaseMarshalData(pstm)
pmsh->Release();
pcf->Release();
}

There are several important reasons why an object may choose to do custom marshaling.