Enables a COM object to define and manage the marshaling of its interface pointers.
When To Implement
Implement IMarshal only when you believe that you can realize significant optimizations to the COM default implementation. In practice, this will rarely be the case. However, there are occasions, such as the following, where implementing IMarshal may be preferred:
- The objects you are writing keep their state in shared memory. In this case, both the original process and the client process use proxies that refer to the shared memory. This type of custom marshaling is possible only if the client process is on the same machine as the original process. COM-provided implementations of IStorage and IStream are examples of this type of custom marshaling.
- The objects you are writing are immutable—that is, their state does not change after creation. Instead of forwarding method calls to the original objects, you simply create copies of those objects in the client process. This technique avoids the cost of switching from one process to another. Some monikers are examples of immutable objects; if you are implementing your own moniker class, you should evaluate the costs and benefits of implementing IMarshal on your moniker objects.
- Objects that themselves are proxy objects can use custom marshaling to avoid creating proxies to proxies. Instead, the existing proxy can refer new proxies back to the original object. This capability is important for the sake of both efficiency and robustness.
- Your server application wants to manage how calls are made across the network without affecting the interface exposed to clients. For example, if an end user were making changes to a database record, the server might want to cache the changes until the user has committed them all, at which time the entire transaction would be forwarded in a single packet. Using a custom proxy would enable the caching and batching of changes in this way.
When you choose to implement IMarshal, you must do so for both your original object and the proxy you create for it. When implementing the interface on either object or proxy, you simply return E_NOTIMPL for the methods that are not implemented.
COM uses your implementation of IMarshal in the following manner: When it's necessary to create a remote interface pointer to your object (that is, when a pointer to your object is passed as an argument in a remote function call), COM queries your object for the IMarshal interface. If your object implements it, COM uses your IMarshal implementation to create the proxy object. If your object does not implement IMarshal, COM uses its default implementation.
How you choose to structure the proxy is entirely up to you. You can write the proxy to use whatever mechanisms you deem appropriate for communicating with the original object. You can also create the proxy as either a stand-alone object or as part of a larger aggregation such as a handler. However you choose to structure the proxy, it must implement IMarshal to work at all. You must also generate a CLSID for the proxy to be returned by your implementation of GetUnmarshalClass on the original object.
When To Use
COM calls this interface as part of system-provided marshaling support. The COM calls are wrapped in calls to CoMarshalInterface and CoUnmarshalInterface. Your code typically will not need to call this interface. Special circumstances where you might choose to do so are discussed in the "Notes to Callers" section for each method.
The IMarshal interface inherits from the IUnknown interface. IMarshal also defines the following types of members:
The IMarshal interface defines the following methods.
Retrieves the CLSID of the unmarshaling code.
Retrieves the maximum size of the buffer that will be needed during marshaling.
Marshals an interface pointer.
Unmarshals an interface pointer.
Destroys a marshaled data packet.
Releases all connections to an object.
Marshaling is the process of packaging data into packets for transmission to a different process or computer. Unmarshaling is the process of recovering that data at the receiving end. In any given call, method arguments are marshaled and unmarshaled in one direction, while return values are marshaled and unmarshaled in the other.
Although marshaling applies to all data types, interface pointers require special handling. The fundamental problem is how client code running in one address space can correctly dereference a pointer to an interface on an object residing in a different address space. The COM solution is for a client application to communicate with the original object through a surrogate object, or proxy, which lives in the client's process. The proxy holds a reference to an interface on the original object and hands the client a pointer to an interface on itself. When the client calls an interface method on the original object, its call is actually going to the proxy. Therefore, from the client's point of view, all calls are in-process.
On receiving a call, the proxy marshals the method arguments and through some means of interprocess communication, such as RPC, passes them along to code in the server process, which unmarshals the arguments and passes them to the original object. This same code marshals return values for transmission back to the proxy, which unmarshals the values and passes them to the client application.
IMarshal provides methods for creating, initializing, and managing a proxy in a client process; it does not dictate how the proxy should communicate with the original object. The COM default implementation of IMarshal uses RPC. When you implement this interface yourself, you are free to choose any method of interprocess communication you deem to be appropriate for your application—shared memory, named pipe, window handle, RPC—in short, whatever works.
IMarshal Default Implementation
COM uses its own internal implementation of the IMarshal interface to marshal any object that does not provide its own implementation. COM makes this determination by querying the object for IMarshal. If the interface is missing, COM defaults to its internal implementation.
The COM default implementation of IMarshal uses a generic proxy for each object and creates individual stubs and proxies, as they are needed, for each interface implemented on the object. This mechanism is necessary because COM cannot know in advance what particular interfaces a given object may implement. Developers who do not use COM default marshaling, electing instead to write their own proxy and marshaling routines, know at compile time all the interfaces to be found on their objects and therefore understand exactly what marshaling code is required. COM, in providing marshaling support for all objects, must do so at run time.
The interface proxy resides in the client process; the interface stub resides in the server. Together, each pair handles all marshaling for the interface. The job of each interface proxy is to marshal arguments and unmarshal return values and out parameters that are passed back and forth in subsequent calls to its interface. The job of each interface stub is to unmarshal function arguments and pass them along to the original object and then marshal the return values and out parameters that the object returns.
Proxy and stub communicate by means of an RPC (remote procedure call) channel, which utilizes the system's RPC infrastructure for interprocess communication. The RPC channel implements a single interface, IRpcChannelBuffer, to which both interface proxies and stubs hold a pointer. The proxy and stub call the interface to obtain a marshaling packet, send the data to their counterpart, and destroy the packet when they are done. The interface stub also holds a pointer to the original object.
For any given interface, the proxy and stub are both implemented as instances of the same class, which is listed for each interface in the system registry under the label ProxyStubClsid32. This entry maps the interface's IID to the CLSID of its proxy and stub objects. When COM needs to marshal an interface, it looks in the system registry to obtain the appropriate CLSID. The server identified by this CLSID implements both the interface proxy and interface stub.
Most often, the class to which this CLSID refers is automatically generated by a tool whose input is a description of the function signatures and semantics of a given interface, written in some interface description language. While using such a language is highly recommended and encouraged for accuracy's sake, doing so is not required. Proxies and stubs are merely COM components used by the RPC infrastructure and, as such, can be written in any manner desired, as long as the correct external contracts are upheld. The programmer who designs a new interface is responsible for ensuring that all interface proxies and stubs that ever exist agree on the representation of their marshaled data.
When created, interface proxies are always aggregated into a larger proxy, which represents the object as a whole. This object proxy also aggregates the COM generic proxy object, which is known as the proxy manager. The proxy manager implements two interfaces: IUnknown and IMarshal. All of the other interfaces that may be implemented on an object are exposed in its object proxy through the aggregation of individual interface proxies. A client holding a pointer to the object proxy "believes" it holds a pointer to the actual object.
A proxy representing the object as a whole is required in the client process so that a client can distinguish calls to the same interfaces implemented on entirely different objects. Such a requirement does not exist in the server process, however, where the object itself resides, because all interface stubs communicate only with the objects for which they were created. No other connection is possible.
Interface stubs, by contrast with interface proxies, are not aggregated because there is no need that they appear to some external client to be part of a larger whole. When connected, an interface stub is given a pointer to the server object to which it should forward method invocations that it receives. Although it is useful to refer conceptually to a stub manager, meaning whatever pieces of code and state in the server-side RPC infrastructure that service the remoting of a given object, there is no direct requirement that the code and state take any particular, well-specified form.
The first time a client requests a pointer to an interface on a particular object, COM loads an IClassFactory stub in the server process and uses it to marshal the first pointer back to the client. In the client process, COM loads the generic proxy for the class factory object and calls its implementation of IMarshal to unmarshal that first pointer. COM then creates the first interface proxy and hands it a pointer to the RPC channel. Finally, COM returns the IClassFactory pointer to the client, which uses it to call IClassFactory::CreateInstance, passing it a reference to the interface.
Back in the server process, COM now creates a new instance of the object, along with a stub for the requested interface. This stub marshals the interface pointer back to the client process, where another object proxy is created, this time for the object itself. Also created is a proxy for the requested interface, a pointer to which is returned to the client. With subsequent calls to other interfaces on the object, COM will load the appropriate interface stubs and proxies as needed.
When a new interface proxy is created, COM hands it a pointer to the proxy manager's implementation of IUnknown, to which it delegates all QueryInterface calls. Each interface proxy implements two interfaces of its own: the interface it represents and IRpcProxyBuffer. The interface proxy exposes its own interface directly to clients, which can obtain its pointer by calling QueryInterface on the proxy manager. Only COM, however, can call IRpcProxyBuffer, which is used to connect and disconnect the proxy to the RPC channel. A client cannot query an interface proxy to obtain a pointer to the IRpcProxyBuffer interface.
On the server side, each interface stub implements IRpcStubBuffer. The server code acting as a stub manager calls IRpcStubBuffer::Connect and passes the interface stub the IUnknown pointer of its object.
When an interface proxy receives a method invocation, it obtains a marshaling packet from its RPC channel through a call to IRpcChannelBuffer::GetBuffer. The process of marshaling the arguments will copy data into the buffer. When marshaling is complete, the interface proxy invokes IRpcChannelBuffer::SendReceive to send the marshaled packet to the corresponding interface stub. When IRpcChannelBuffer::SendReceive returns, the buffer into which the arguments were marshaled will have been replaced by a new buffer containing the return values marshaled from the interface stub. The interface proxy unmarshals the return values, invokes IRpcChannelBuffer::FreeBuffer to free the buffer, and then returns the return values to the original caller of the method.
It is the implementation of IRpcChannelBuffer::SendReceive that actually sends the request to the server process and that knows how to identify the server process and, within that process, the object to which the request should be sent. The channel implementation also knows how to forward the request on to the appropriate stub manager in that process. The interface stub unmarshals the arguments from the provided buffer, invokes the indicated method on the server object, and marshals the return values back into a new buffer allocated by a call to IRpcChannelBuffer::GetBuffer. The channel then transmits the return data packet back to the interface proxy, which is still in the middle of IRpcChannelBuffer::SendReceive, which returns to the interface proxy.
A particular instance of an interface proxy can be used to service more than one interface, as long as the following conditions are met:
- The IIDs of the affected interfaces must be mapped to the appropriate ProxyStubClsid in the system registry.
- The interface proxy must support calls to QueryInterface from one supported interface to the other interfaces, as usual, as well as from IUnknown and IRpcProxyBuffer.
A single instance of an interface stub can also service more than one interface, but only if that set of interfaces has a strict single-inheritance relationship. This restriction exists because the stub can direct method invocations to multiple interfaces only where it knows in advance which methods are implemented on which interfaces.
At various times, proxies and stubs will have need to allocate or free memory. Interface proxies, for example, will need to allocate memory in which to return out parameters to their caller. In this respect, interface proxies and interface stubs are just normal COM components, in that they should use the standard task allocator. (See CoGetMalloc.)
Minimum supported client
|Windows 2000 Professional|
Minimum supported server
|Windows 2000 Server|
|IID_IMarshal is defined as 00000003-0000-0000-C000-000000000046|
Build date: 4/12/2010