Sunday, November 05, 2006

COM: Serializing an interface pointer using CoMarshalInterface/CoUnmarshalInterface

Background:
~~~~~~~~~~
COM exposes two sets of API functions to serialize an interface pointer into an apartment neutral representation.

1. CoMarshalInterThreadInterfaceInStream (and its counterpart for unmarshalling)
2. CoMarshalInterface (and its counterpart for unmarshalling)

The former is simply a wrapper around the latter.

CoMarshalInterface is the more primitive form and provides for optimizations to the serialized state based on the distance of the importing apartment (different apartment same process, different process same host, remote host)

A stream (into which the interface state is written) is just a blob of memory that's wrapped around by a COM object that implements the IStream interface to work with the stream. CoMarhalInterface expects it to be provided with such a stream, Though it is smart enough to allocate the buffer if the stream contains no memory.

Rewind the stream before unmarshalling an interface pointer:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

So I did a simple exercise to serialize an interface pointer into a stream and then deserializing it back into the same apartment (so i get a raw pointer back). The code is pretty straight forward. I was however having trouble unmarshalling the stream and was hitting errors that showed no indication of the real nature of the problem.

Here's the code: (Error checks ignored for brevity)


IStream *pStream = NULL;

HGLOBAL hGlobal = NULL; // CASE 1
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE,1000); // CASE 2

HRESULT hr = CreateStreamOnHGlobal(hGlobal,TRUE,&pStream);

ISafeObject *pSafeObject = static_cast(this);

hr = CoMarshalInterface(pStream,__uuidof(ISafeObject),(IUnknown*)(pSafeObject),
MSHCTX_INPROC,NULL,MSHLFLAGS_NORMAL);

ISafeObject *pUnmarshaledSafeObject = NULL;
hr = CoUnmarshalInterface(pStream,__uuidof(ISafeObject), (void **)&pUnmarshaledSafeObject);


Case 1: I pass NULL as the HGLOBAL handle to CreateStreamOnHGlobal (assuming CoMarshalInterface should autoalloc)
Result : 0x8003001e A disk error occurred during a read operation.

Case 2: I allocate memory.
Result : 0x8001011d The marshaled interface data packet (OBJREF) has an invalid or unknown format.

It turned out that i had to unwind the stream to which CoMarshalInterface wrote to, before i could call CoUnmarshalInterface.
Sticking the following two lines of code fixed the problem.


// Before calling CoUnmarshalInterface
LARGE_INTEGER disp = {0};
pStream->Seek(disp,STREAM_SEEK_SET,NULL); // REWIND

Lesson learnt:
~~~~~~~~~~~~~
The next time (in the rare case) you ever need to use the CoMarshalInterface API for serializing interface pointers, remember to rewind the stream pointer before you rehydrate your serialized interface.

About Me

Among the few things that interest me, are the painful details of COM, DCOM's wire protocol, C++, Java, their interoperability, operating systems and programming languages. Luckily i get to apply some of my interests and add some value at work. I hope to disseminate some of the interesting concepts that i learn at work and which i feel are worth sharing.