Marshaling

OVERVIEW

Marshaling: The process by which parameters are packaged and sent across apartment, process or machine boundaries. In COM, marshaling is implemented by interface-specific proxies/stubs.

Proxy: An interface-specific COM object that runs in the address space of the caller (sender). Its purpose is to package the parameters specified by the client and then to transmit them to a corresponding stub that lives in the address space of the receiver.

Stub: An interface-specific COM object that runs in the address space of the callee (receiver). Its purpose is to un-package the parameters for that interface after they were marshaled across the process boundary, and then to make the actual method call on the actual object.

Marshaling Mechanism: Marshaling involves two basic steps

  1. Take an interface pointer in the server’s own process and make this pointer available to code in the client process. This will have to use some from of IPC
  2. Take the arguments to an interface method as passed from the client and transfer those arguments to the remote object’s implementation.
TYPES OF MARSHALING

In all cases of marshaling, there will be a proxy object on the client side and a stub object on the server side.

Type Notes
Standard
  • A proxy object is required in the client and a stub object is required in the object apartment. The marshaling layer takes care of loading these objects.

  • The proxy and stub objects need to be built and registered (using nmake on the *ps.mk file.)

  • Registering the proxy/stub component adds the following registry entries - 
    <Interface>
         ProxyStubCldid32 = CLSID of proxy/stub DLL
    <CLSID>
         InProcServer32 = path to the proxy/stub DLL

    When an interface needs to be marshaled, COM looks up its IID to determine the CLSID of the proxy/stub DLL. From this CLSID, the path to the proxy/stub DLL is determined. COM then takes care of loading the component.

Type Library
  • This is a special case of the Standard marshaling: rather than building a proxy/stub DLL that is specific to the interfaces defined in the COM object, the proxy and stub in type library marshaling are generic and implemented in OLEAUT32.DLL.
  • Registration code for ATL objects (DLLRegisterServer) will register the type library using CComModule::RegisterServer(TRUE).
  • If you want the ATL object to use standard marshaling instead of type library marshaling, change the call in (DLLRegisterServer) to CComModule::RegisterServer(FALSE).
  • Type library registration means that interfaces referenced in the library section of an IDL will be registered as using the Universal Marshaler (OLEAUT32.DLL). The interfaces referenced in the library section of an IDL must be [oleautomation]. That is, they must be dual, dispinterfaces, or custom interfaces that explicitly specify the [oleautomation] attribute.
Manual
  • Manual marshaling is needed whenever we need to pass an interface pointer from one thread to another across apartment boundary without doing it via a COM method call.
  • Typical Situation: Having two threads, each in an apartment, and each with an already created object. If one of these objects is to access the other, we need to use custom marshaling. Therefore, manual marshaling is only every needed in multithreaded servers. See the BasicSTA project for an example.
  • Note that manual marshaling still requires that a type library or a proxy/stub DLL be used.
  • Manual marshaling is explained in detail below.
Custom
  • In custom marshaling, the object takes care of all aspects of marshaling. A great way to understand and learn about marshaling!
  • Great advantage of custom marshaling, is that we can use whatever inter-process method we wish. We can chose to use TCP/IP, named pipes or HTML in order for the objects to communicate with each other.
  • Custom marshaling is explained in detail below.

 

MANUAL MARSHALING

Manual marshaling can be implemented in four different ways:

  1. Using CoMarshalInterface / CoUnmarshalInterface
  2. Using ATL helper functions
  3. Using CoMarshalInterThreadInterfaceInStream / CoGetInterfaceAndReleaseStream
  4. Using the Global Interface Table (GIT)
    Using CoMarshalInterface / CoUnmarshalInterface
    Overall: In the apartment that has access to the object, create a new stream object in the global memory using CreateStreamOnHGlobal(). The returned stream object supports reading and writing. Then marshal the interface into this stream using CoMarshalInterface(). This apartment thread can then set an event object to indicate that marshaling has been performed. The other apartment waiting on this event object can now unmarshal the interface packet by calling CoUnmarshalInterface() on the global stream object

// Thread 1 ( has access to the object)

/* Create an  object exposing an IThreadInfo interface */
IThreadInfoPtr spOb( __uuidof(ThreadInfo) );

/* Create a global stream object */
IStream *g_pMarshalPtr;
hr = CreateStreamOnHGlobal
                ( 
                    NULL,          // Allocate a new shared mem block of size 0
                    TRUE,          // Release mem hndl when stream is released
                    &g_pMarshalPtr // output that gets IStream pointer
                 ); 
if (FAILED(hr))
    _com_issue_error( hr );

/* Marshal the interface into the global stream object. Note that the
MSHLFLAGS_NORMAL flag means that the interface can be unmarshaled only once */
hr = CoMarshalInterface( g_pMarshalPtr, 
                         __uuidof(spOb), 
                         spOb, 
                         MSHCTX_INPROC, 
                         NULL, 
                         MSHLFLAGS_NORMAL );

if ( FAILED(hr) )
{
    g_pMarshalPtr->Release();
    g_pMarshalPtr = NULL;
    _com_issue_error( hr );
}

/* Let the other thread know that we’re done marshaling the interface */
SetEvent( g_hEvent );

...

// Thread 2 is requesting an interface on an object from thread 1 (above)

// Wait until the first thread finished marshaling
WaitForSingleObject( g_hEvent, INIFINITE);

// Unmarshal the interface pointer. First point to the beginning of the stream
IThreadInfoPtr    spOb;
LARGE_INTEGER    l;
l.QuadPart       = 0;
g_pMarshalPtr->Seek( l, STREAM_SEEK_SET, NULL);

// Use the stream to unmarshal the interface pointer
hr = CoUnmarshalInterface( g_pMarshalPtr, __uuidof( spOb ), (void**) &spOb);

// Clean up marshaling code
g_pMarshalPtr->Release();
g_pMarshalPtr = NULL;

if (FAILED(hr))
    _com_issue_error( hr );

...


Using ATL helper functions

ATL wraps the two pieces of code above with AtlMarshalInProc() and AtlUnmarshalPtr() functions. Internally, AtlMarshalInProc() uses the MSHLFLAGS_TABLESTRONG flag for CoMarshalInterface() which means that the interface can be unmarshaled from its stream many times. Note that AtlMarshalInProcc() an only be used on a pointer to an in-proc object.

// Thread 1 has access to the object

// Create the object
IThreadInfoPtr spOb( __uuidof(ThreadInfo) );

// Marshal the interface
AtlMarshalInProc( spOb, __uuidof( soOb ), & g_pMarshalPtr );

// Let the other thread know that we’re done marshaling the interface
SetEvent( g_hEvent );

// Thread 2 is requesting an interface on an object from thread 1

// Wait until the first thread finished marshaling
WaitForSingleObject( g_hEvent , INFINITE);

// Unmarshal the interface pointer. First point to the beginning of the stream
IThreadInfoPtr spOb;
AtlUnmarshalPtr( g_pMarshalPtr, , __uuidof( spOb ), (IUnknown**) &spOb)

// Use the object
spOb->SomeMethod();

// When you don’t want to marshal the interface any more, release the stream
AtlFreeMarshalStream( g_pMarshalPtr );


Using CoMarshalInterThreadInterfaceInStream / CoGetInterfaceAndReleaseStream

CoMarshalInterThreadInterfaceInStream() is the same as AtlMarshlPtrInProc() except that the former uses normal marshaling as opposed to strong table marshaling by the latter. CoGetInterfaceAndReleaseStream() is equivalent to calling AtlMarshalPtr() then  AtlFreeMarshalStream().

Using the Global Interface Table (GIT)
CUSTOM MARSHALING

When a client request that an object be created, COM will always QI for IMarshal. When a client queries for another interface on an object, COM will also QI for IMarshal.

If no interface is returned, COM uses standard marshaling (proxy/stub DLL or Universal Marshaler). 

If IMarshal was supported, then COM will marshal the interface using the custom marshaling implemented by the object. This custom marshaling goes something like this –

  1. After creating the object and querying for the requested interface, COM calls CoMarshalInterface.
  2. CoMarshalInterface calls CoGetStandardMarshal to create the stub side of the call. This stub has an identifier and is put in the stream to enable the client to identify the stub it should connect to.
  3. CoMarshalInterface then queries the object for a pointer to the IMarshal interface. If the object does not implement IMarshal, meaning that it relies on COM to provide marshaling support, CoMarshalInterface gets a pointer to COM's default implementation of IMarshal
  4. If the object returns a valid IMarshal, CoMarshalInterfacegets the CLSID of the object's proxy by calling I IMarshal::GetUnmarshalClass, using whichever IMarshal interface pointer has been returned. The CLSID of the proxy is then written to the stream to be used for marshaling. 
  5. CoMarshalInterface then marshals the interface pointer by calling IMarshal::MarshalInterface
  6. The Service Control Manager, SCM, sends this stream packet to the client
  7. The client needs to transform the CLSID and the marshaling packet in the stream into an interface pointer it can use. This is accomplished with CoUnmarshalInterface as follows
    1. CoUnmarshalInterface calls CoCreateInstance() on the given CLSID to create the proxy. This proxy must implement IMarshal.
    2. CoUnmarshalInterface then passes the marshaling packet (stream) into the proxy’s IMarshal::UnMarshalInterface. The proxy uses the information in the stream to establish a connection with its stub.
    3. CoUnmarshalInterfacethen calls IMarshal::ReleaseMarshalData to release data.
    4. CoUnmarshalInterface then returns the pointer from IMarshal::UnMarshalInterface to the caller. This pointer is ultimately returned to the client.
  8. Now the client has its own interface pointer to the object. When the client calls a method on the interface, the proxy will marshal the method’s arguments into a packet and then transmits them over to the stub. The stub unmarshals them and calls the actual object method.
STANDARD MARSHALING ARCHITECTURE

The purpose of this section is to provide an overview of the standard marshaling architecture. This overview is for reading only to know what is going on under the hood. It helps to understand the overall picture.

Standard marshaling is used when we have built & registered the proxy/stub DLL or we are using type library marshaling. We cannot control it. It all happens automatically. How it happens is summarized below

OVERALL STANDARD MARSHALING ARCHITECTURE

TERMS

This section will identify all components used in standard marshaling. How these components interact will then be discussed.

Interface Proxy: Interface-specific piece of code living in the address space of the client. It is responsible for marshaling arguments, and for unmarshaling return values and out parameters, that are passed back and forth in subsequent calls to its remote interface.

Proxy Manager: A COM object provided by the COM library that manages the client object identity and which dynamically loads the interface proxies as needed (as per QueryInterface calls). The object exposes IUnknown and IMarshal interfaces.

Object Proxy: A COM object created by the COM library that results from aggregating the interface proxies and the proxy manager. All the interfaces exposed by the interface proxies and the proxy manager are therefore, exposed by the object proxy through aggregation. A client holding a pointer to the object proxy “believes” it is holding a pointer to the actual object

Interface Stub: Interface-specific piece of code living in the address space of the server. It is responsible for unmarshaling arguments and pass them along to the original object. It also marshals the return values and out parameters that the object returns.

For any interface, the proxy and stub are implemented in the same class which is listed for each interface in the system registry under ProxyStubClsid32. This entry maps the interface’s IID to the CLSID of its proxy/stub object. When COM needs to marshal an interface, it looks up the CLSID identified by ProxyStubClsid32 and then it loads it up. This class can be either the one generated by the MIDL if proxy/stub DLL was built from ATL’s code, or it can be OLEAUT32.DLL is type library marshaling was used.

Stub Manager: A conceptual COM object created by COM to uniquely identify a COM object across a network. Therefore, each COM object using standard marshaling will have a stub manager.

RPC Channel: The proxy and stub communicate with each other using the RPC channel which is a piece of code that provides infrastructure for interprocess communication. The RPC channel implements IRpcChannelBuffer interface which is used by both the proxy and stub to exchange marshaling data.

INTERACTION MECHANISM
Action : The client creates an object
Client  Server
COM library loads the object server into memory
Action: Client requests a pointer to an interface for the first time
Client Server
1. COM creates an IClassFactory stub and uses it to marshal the pointer back to the client.
1. COM loads the proxy manager for the class factory. COM then calls its IMarshal implementation to unmarshal the IClassFactory pointer.
2. COM creates an interface proxy for the class factory and hands it a pointer to the RPC channel. The combination of steps 1 & 2 are aggregated to give us the proxy object. Note: The interface proxy exposes its own interface (IClassFactory) directly to the client.
3. COM returns the IClassFactory pointer to the client that uses it to call IClassFactory::CreateInstance() passing it the required IID  
2. COM now creates a new instance of the object.
3. COM also creates an interface stub for the requested interface. The stub is now used to marshal the interface pointer back to the client.
Note: The above three steps are new repeated for the requested interface

4. COM loads the proxy manager for the COM object that exposes the requested interface. COM then calls its IMarshal implementation to unmarshal the requested pointer.

5. COM creates an interface proxy for the requested interface and hands it a pointer to the RPC channel. The combination of steps 4 & 5 are aggregated to give us the proxy object. 
Note: The interface proxy exposes its own interface (the requested interface)) directly to the client.
6. COM returns the requested interface back to the client which can now use to invoke methods on the object.
Action: Client invokes a method on the interface
Client Server
1. The interface pointer that the client has is exposed directly from the interface proxy which also has an unexposed interface, IRpcChannelBuffer. The interface proxy calls IRpcChannelBuffer::GetBuffer to obtain a marshaling packet from its RPC channel 
2. The interface proxy marshals the method arguments into this buffer
3. When marshaling is complete, the interface proxy invokes IRpcChannelBuffer::SendReceive to send the marshaling packet to the stub (IRpcChannelBuffer::SendReceive knows how to identify the server process and the object within the server process.) 
1. The interface stub receives the packet sent by IRpcChannelBuffer::SendReceive and it unmarshals the arguments.
2. The interface stub then calls the actual object method, and marshals the return values into a new buffer allocated by IRpcChannelBuffer::GetBuffer. The channel then transmits the data into the interface proxy which is still in the middle of the IRpcChannelBuffer::SendReceive function call.
4. When IRpcChannelBuffer::SendReceive returns, its buffer would have been replaced by a new buffer containing values marshaled by the interface stub. The interface proxy now unmarshals these values and invokes IRpcChannelBuffer::FreeBuffer to free the buffer. 
5.The interface proxy finally returns the unmarshaled data to the client.