| C & C++ Programming | MFC Programming | Download | Site Index |


 

 

 

 

Supplementary Note For MFC Programming Module 28

 

 

 

 

 

 

 

What do we have in this Module?

  1. Marshaling

  2. Marshaling Details

  3. Inter-Object Communication

  4. Proxy

  5. Stub

  6. Channel

  7. Microsoft RPC

  8. IMarshal

    1. When to Implement

    2. When to Use

    3. Methods in VTable Order

 

Marshaling

 

The COM technique of marshaling allows interfaces exposed by an object in one process to be used in another process. In marshaling, COM provides code (or uses code provided by the interface implementor) both to pack a method's parameters into a format that can be moved across processes (as well as, across the wire to processes running on other machines) and to unpack those parameters at the other end. Likewise, COM must perform these same steps on the return from the call. Marshaling is typically not necessary when an interface provided by an object is being used in the same process as the object. However, marshaling may be needed between threads.

 

Marshaling Details

 

If you use standard marshaling, COM handles all of the details described in this section for you. This section is provided for those few programmers who need these details and for those interested in the underlying information. Marshaling is the process of packaging and un-packaging parameters so a remote procedure call can take place. Different parameter types are marshaled in different ways. For example, marshaling an integer parameter involves simply copying the value into the message buffer.

 (Although even in this simple case, there are issues such as byte ordering to deal with in cross-machine calls.) Marshaling an array, however, is a more complex process. Array members are copied in a specific order so that the other side can reconstruct the array exactly. When a pointer is marshaled, the data that the pointer is pointing to is copied following rules and conventions for dealing with nested pointers in structures. Unique functions exist to handle the marshaling of each parameter type. With standard marshaling, the proxies and stubs are systemwide resources for the interface and they interact with the channel through a standard protocol. Standard marshaling can be used both by standard COM-defined interfaces and by custom interfaces, as follows:

  1. In the case of most COM interfaces, the proxies and stubs for standard marshaling are in-process component objects which are loaded from a systemwide DLL provided by COM in ole32.dll.

  2. In the case of custom interfaces, the proxies and stubs for standard marshaling are generated by the interface designer, typically with MIDL. These proxies and stubs are statically configured in the registry, so any potential client can use the custom interface across process boundaries. These proxies and stubs are loaded from a DLL that is located via the system registry, using the interface ID (IID) for the custom interface they marshal.

  3. An alternative to using MIDL to generate proxies and stubs for custom interfaces, a type library can be generated instead and the system provided, type-library–driven marshaling engine will marshal the interface.

As an alternative to standard marshaling, an interface (standard or custom) can use custom marshaling. With custom marshaling, an object dynamically implements the proxies at run time for each interface that it supports. For any given interface, the object can select COM-provided standard marshaling or custom marshaling. This choice is made by the object on an interface-by-interface basis. Once the choice is made for a given interface, it remains in effect during the object's lifetime. However, one interface on an object can use custom marshaling while another uses standard marshaling. Custom marshaling is inherently unique to the object that implements it. It uses proxies implemented by the object and provided to the system on request at run time. Objects that implement custom marshaling must implement the IMarshal interface, whereas objects that support standard marshaling do not.

If you decide to write a custom interface, you must provide marshaling support for it. Typically, you will provide a standard marshaling DLL for the interface you design. You can use the tools contained in the Platform SDK CD to create the proxy/stub code and the proxy/stub DLL. Alternatively, you can use these tools to create a type library which COM will use to do data-driven marshaling (using the data in the type library). For a client to make a call to an interface method in an object in another process involves the cooperation of several components. The standard proxy is a piece of interface-specific code that resides in the client's process space and prepares the interface parameters for transmittal. It packages, or marshals, them in such a way that they can be re-created and understood in the receiving process. The standard stub, also a piece of interface-specific code, resides in the server's process space and reverses the work of the proxy. The stub unpackages, or un-marshals, the sent parameters and forwards them to the object application. It also packages reply information to send back to the client. Note   Readers more familiar with RPC than COM may be used to seeing the terms client stub and server stub. These terms are analogous to proxy and stub.

The following diagram shows the flow of communication between the components involved. On the client side of the process boundary, the client's method call goes through the proxy and then onto the channel, which is part of the COM library. The channel sends the buffer containing the marshaled parameters to the RPC run-time library, which transmits it across the process boundary. The RPC run time and the COM libraries exist on both sides of the process. The distinction between the channel and the RPC run time is a characteristic of this implementation and is not part of the programming model or the conceptual model for COM client/server objects. COM servers see only the proxy or stub and, indirectly, the channel. Future implementations may use different layers below the channel or no layers.

 

Figure 1: Components of interprocess communications

 

Figure 1: Components of interprocess communications

 

Inter-Object Communication

 

COM is designed to allow clients to communicate transparently with objects, regardless of where those objects are running—in the same process, on the same machine, or on a different machine. This provides a single programming model for all types of objects, and for both object clients and object servers.

From a client's point of view, all objects are accessed through interface pointers. A pointer must be in-process. In fact, any call to an interface function always reaches some piece of in-process code first. If the object is in-process, the call reaches it directly, with no intervening system-infrastructure code. If the object is out-of-process, the call first reaches what is called a "proxy" object provided either by COM or by the object (if the implementor wishes). The proxy packages call parameters (including any interface pointers) and generate the appropriate remote procedure call (or other communication mechanism in the case of custom generated proxies) to the other process or the other machine where the object implementation is located. This process of packaging pointers for transmission across process boundaries is called marshaling.

From a server's point of view, all calls to an object's interface functions are made through a pointer to that interface. Again, a pointer has context only in a single process, and the caller must always be some piece of in-process code. If the object is in-process, the caller is the client itself. Otherwise, the caller is a "stub" object provided either by COM or by the object itself. The stub receives the remote procedure call (or other communication mechanism in the case of custom generated proxies) from the "proxy" in the client process, unmarshals the parameters, and calls the appropriate interface on the server object. From the points of view of both clients and servers, they always communicate directly with some other in-process code.

COM provides an implementation of marshaling, referred to as standard marshaling. This implementation works very well for most objects and greatly reduces programming requirements, making the marshaling process effectively transparent. The clear separation of interface from implementation of COM's process transparency can, however, get in the way in some situations. The design of an interface that focuses on its function from the client's point of view can sometimes lead to design decisions that conflict with efficient implementation of that interface across a network. In cases like this, what is needed is not pure process transparency but "process transparency, unless you need to care." COM provides this capability by allowing an object implementor to support custom marshaling (also called IMarshal marshaling). Standard marshaling is, in fact, an instance of custom marshaling, it is the default implementation used when an object does not require custom marshaling.

You can implement custom marshaling to allow an object to take different actions when used from across a network than it takes under local access, and it is completely transparent to the client. This architecture makes it possible to design client/object interfaces without regard to network performance issues and then later to address network performance issues without disrupting the established design.

COM does not specify how components are structured; it specifies how they interact. COM leaves the concern about the internal structure of a component to programming languages and development environments. Conversely, programming environments have no set standards for working with objects outside of the immediate application. Microsoft® Visual C++®, for example, works extremely well for manipulating objects inside an application but has no support for working with objects outside the application. Generally, all other programming languages are the same in this regard. Therefore, to provide network wide interoperability, COM, through language-independent interfaces, picks up where programming languages leave off.

The double indirection of the vtbl structure means that the pointers in the table of function pointers do not need to point directly to the real implementation in the real object. This is the heart of process transparency.

For in-process servers, where the object is loaded directly into the client process, the function pointers in the table point directly to the actual implementation. In this case, a function call from the client to an interface method directly transfers execution control to the method. However, this cannot work for local, let alone remote, objects because pointers to memory cannot be shared between processes. Nevertheless, the client must be able to call interface methods as if it were calling the actual implementation. Thus, the client uniformly transfers control to a method in some object by making the call.

A client always calls interface methods in some in-process object. If the actual object is local or remote, the call is made to a proxy object, which then makes a remote procedure call to the actual object. So what method is actually executed? The answer is that whenever there is a call to an out-of-process interface, each interface method is implemented by a proxy object. The proxy object is always an in-process object that acts on behalf of the object being called. This proxy object knows that the actual object is running in a local or remote server.

The proxy object packages up the function parameters in some data packets and generates an RPC call to the local or remote object. That packet is picked up by a stub object in the server's process on the local or a remote machine, which unpacks the parameters and makes the call to the real implementation of the method. When that function returns, the stub packages up any out-parameters and the return value and sends it back to the proxy, which unpacks them and returns them to the original client. Thus, client and server always talk to each other as if everything was in-process. All calls from the client and all calls to the server are, at some point, in-process. But because the vtbl structure allows some agent, like COM, to intercept all function calls and all returns from functions, that agent can redirect those calls to an RPC call as necessary. Although in-process calls are faster than out-of-process calls, the process differences are completely transparent to the client and server.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Proxy

 

A proxy resides in the address space of the calling process and acts as a surrogate for the remote object. From the perspective of the calling object, the proxy is the object. Typically, the proxy's role is to package the interface parameters for calls to methods in its object interfaces. The proxy packages the parameters into a message buffer and passes the buffer onto the channel, which handles the transport between processes. The proxy is implemented as an aggregate, or composite, object. It contains a system-provided, manager piece called the proxy manager and one or more interface-specific components called interface proxies. The number of interface proxies equals the number of object interfaces that have been exposed to that particular client. To the client complying with the component object model, the proxy appears to be the real object.

With custom marshaling, the proxy can be implemented similarly or it can communicate directly with the object without using a stub.

Each interface proxy is a component object that implements the marshaling code for one of the object's interfaces. The proxy represents the object for which it provides marshaling code. Each proxy also implements the IRpcProxyBuffer interface. Although the object interface represented by the proxy is public, the IRpcProxyBuffer implementation is private and is used internally within the proxy. The proxy manager keeps track of the interface proxies and also contains the public implementation of the controlling IUnknown interface for the aggregate. Each interface proxy can exist in a separate DLL that is loaded when the interface it supports is materialized to the client.

The following diagram shows the structure of a proxy that supports the standard marshaling of parameters belonging to two interfaces: IA1 and IA2. Each interface proxy implements IRpcProxyBuffer for internal communication between the aggregate pieces. When the proxy is ready to pass its marshaled parameters across the process boundary, it calls methods in the IRpcChannelBuffer interface, which is implemented by the channel. The channel in turn forwards the call to the RPC run-time library so that it can reach its destination in the object.

 

Figure 2: Structure of the Proxy

 

Figure 2: Structure of the Proxy

 

Stub

 

The stub, like the proxy, is made up of one or more interface pieces and a manager. Each interface stub provides code to unmarshal the parameters and code that calls one of the object's supported interfaces. Each stub also provides an interface for internal communication. The stub manager keeps track of the available interface stubs. There are, however, the following differences between the stub and the proxy:

  1. The most important difference is that the stub represents the client in the object's address space.

  2. The stub is not implemented as an aggregate object because there is no requirement that the client be viewed as a single unit; each piece in the stub is a separate component.

  3. The interface stubs are private rather than public.

  4. The interface stubs implement IRpcStubBuffer, not IRpcProxyBuffer.

  5. Instead of packaging parameters to be marshaled, the stub unpackages them after they have been marshaled and then packages the reply.

The following diagram shows the structure of the stub. Each interface stub is connected to an interface on the object. The channel dispatches incoming messages to the appropriate interface stub. All the components talk to the channel through IRpcChannelBuffer, the interface that provides access to the RPC run-time library.

 

Figure 3: Structure of the Stub

 

Figure 3: Structure of the Stub

 

Channel

 

The channel has the responsibility of transmitting all messages between client and object across the process boundary. The channel has been designed to work transparently with different channel types, is compatible with OSF DCE standard RPC, and supports single and multithreaded applications.

 

Microsoft RPC

 

Microsoft RPC is a model for programming in a distributed computing environment. The goal of RPC is to provide transparent communication so that the client appears to be directly communicating with the server. Microsoft's implementation of RPC is compatible with the Open Software Foundation (OSF) Distributed Computing Environment (DCE) RPC. You can configure RPC to use one or more transports, one or more name services, and one or more security servers. The interfaces to those providers are handled by RPC. Because Microsoft RPC is designed to work with multiple providers, you can choose the providers that work best for your network. The transport is responsible for transmitting the data across the network. The name service takes an object name, such as a moniker, and finds its location on the network. The security server offers applications the option of denying access to specific users and/or groups. In addition to the RPC run-time libraries, Microsoft RPC includes the Interface Definition Language (IDL) and its compiler. Although the IDL file is a standard part of RPC, Microsoft has enhanced it to extend its functionality to support custom COM interfaces. The Microsoft Interface Definition Language (MIDL) compiler uses the IDL file that describes your custom interface to generate several files.

 

IMarshal

 

The IMarshal interface enables a COM object to define and manage the marshaling of its interface pointers. The alternative is to use the COM default implementation, the preferred choice in all but a few special cases. Marshaling is the process of packaging data into packets for transmission to a different process or machine. 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. This interface declared in objidl.h

 

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:

  1. 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.

  2. 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.

  3. Objects those 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.

  4. 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.

 

Methods in VTable Order

 

IUnknown Methods

Description

QueryInterface()

Returns pointers to supported interfaces.

AddRef()

Increments reference count.

Release()

Decrements reference count.

 

Table 1.

 

 

IMarshal Methods

Description

GetUnmarshalClass()

Returns CLSID of unmarshaling code.

GetMarshalSizeMax()

Returns size of buffer needed during marshaling.

MarshalInterface()

Marshals an interface pointer.

UnmarshalInterface()

Unmarshals an interface pointer.

ReleaseMarshalData()

Destroys a marshaled data packet.

DisconnectObject()

Releases all connections.

 

 Table 2.

 

 

 

 

 

 

 

 


| C & C++ Programming | MFC Programming | Download | Site Index |