How Interface Remoting Works

The crux of the problem to be addressed in interface remoting can be stated as follows:

"Given an already existing remoted-interface connection between a client process and a server process, how can a method invocation through that connection return a new interface pointer so as to create a second remoted-interface connection between the two processes?"

We state the problem in this way so as to avoid for the moment the issue of how an initial connection is made between the client and the server process; we will return to that later.

Let's look at an example. Suppose we have an object in a server process which supports an interface IFoo, and that interface of the object (and IUnknown) has sometime in the past been remoted to a client process through some means not here specified. In the client process, there is an object proxy which supports the exact same interfaces as does the original server object, but whose implementations of methods in those interfaces are special, in that they forward calls they receive on to calls on the real method implementations back in the server object.

We say that the method implementations in the object proxy marshal the data, which is then conveyed to the server process, where it is unmarshaled. That is, marshaling refers to the packaging up of method arguments for transmission to a remote process; unmarshaling refers to the unpackaging of this data at the receiving end. Notice that in a given call, the method arguments are marshaled and unmarshaled in one direction, while the return values are marshaled and unmarshaled in the other direction.

For concreteness, let us suppose that the IFoo interface is defined as follows:


interface IFoo : IUnknown {
   IBar *   ReturnABar();
   };

If the in the client process pFoo->ReturnABar() is invoked, then the object proxy will forward this call on to the IFoo::ReturnABar() method in the server object, which will do whatever this method is supposed to do in order to come up with some appropriate IBar*. The server object is then required to return this IBar* back to the client process. The act of doing this will end up creating a second connection between the two processes:

It is the procedure by which this second connection is established which is the subject of our discussion here. This process involves two steps:

  1. On the server side, the IBar* is packaged or marshaled into a data packet.
  2. The data packet is conveyed by some means to the client process, where the data it contains is unmarshaled to create the new object proxy.
The term marshaling is a general one that is applied in the industry to the packaging of any particular data type, not just interface pointers, into a data packet for transmission through an RPC infrastructure. Each different data type has different rules for how it is to marshaled: integers are to be stored in a certain way, strings are to be stored in a certain way, and so forth.1. Likewise, marshaled interface pointers are to be stored in a certain way; the Component Object Model function CoMarshalInterface() contains the knowledge of how this is to be done (note that we will not mention further in this document any kind of marshaling other than marshaling of interface pointers; that subject is well-explored in existing RPC systems).

The process begins with the code doing the marshaling of the returned IBar* interface. This code has in hand a pointer to an interface that it knows in fact to be an IBar* and that it wishes to marshal. To do so it calls CoMarshalInterface(). The first step in CoMarshalInterface() involves finding out whether the object of which this is an interface in fact supports custom object marshaling (often simply referred to as custom marshaling). Custom object marshaling is a mechanism that permits an object to be in control of creation of remote object proxies to itself. In certain situations, custom object marshaling can be used to create a more efficient object proxy than would otherwise be the case.2.

Use of custom marshaling is completely optional on the object's part; if the object chooses not to support custom marshaling, then standard interface marshaling is used to marshal the IBar*. Standard interface marshaling uses a system-provided object proxy implementation in the client process. This standard implementation is a generic piece of code, in that it can be used as the object proxy for any interface on any object. However, the act of marshaling (and unmarshaling) method arguments and return values is inherently interface-specific, since it is highly sensitive to the semantics and data types used in the particular methods in question. To accommodate this, the standard implementation dynamically loads in interface-specific pieces of code as needed in order to do the parameter marshaling.

We will discuss in great detail in a moment how standard interface marshaling works. First, however, we will review custom object marshaling, as this provides a solid framework in which standard marshaling can be better understood.