COM servers are not required to load into the same process as the client that makes use of their services. The client and the server do not even have to reside on the same computer. This has implications for the way that data is passed between client and server. You have to ensure that when a COM client calls a method, the COM server will receive the parameters in a format that it can understand, no matter where the server is running, or on which platform.
The transferring of data across process boundaries is called marshaling. In this lesson, you will learn about the different ways in which COM components marshal data passed to and from methods defined by the component interfaces.
After this lesson, you will be able to:Estimated lesson time: 30 minutes
- Describe the different execution contexts in which COM components can be run.
- Describe the different techniques that can be used to marshal your interfaces.
A COM component can be run in one of three ways—as an in-process server, as an out-of-process server, or as a remote server. Figure 8.8 summarizes the characteristics and implementations of using each of these execution environments.
An in-process COM server is implemented as a DLL, and executes within the same process address space as the object's client application. Since the COM object resides in the same address space, your COM server responds more quickly than either an out-of-process server or a remote server can. In-process servers can be developed more quickly since you have less code to implement. However, with this server, you must register a copy of the COM object on every computer on which you intend to run the client application.
Out-of-process COM servers are implemented as .exe files that reside on the same computer as the client application, but execute in a different process address space. As a result, you have to ensure that the arguments passed to a method transfer correctly across process boundaries—that is to say, you have to marshal your interfaces. Since you have to write some extra code to accomplish the marshaling of interfaces, your COM object is going to take a little more time to respond to method calls. Developing the extra code to pass arguments across process boundaries does require a bit more coding. Since the COM object executes on the same computer as the application client, you must also store and register a copy of the COM object on every client computer.
Figure 8.8 Execution contexts for COM objects
You can also implement your out-of-process COM object so it executes on a remote computer. This kind of object is known as a remote server. In this case, you transmit from the client computer to the server computer across a network. Response times for executing server methods are considerably longer. Executing a method across a network connection is a non-deterministic process. Every time you invoke a remote method, even if it is the same method, your client application experiences a different response time. If the network is clogged with traffic, your client application may experience longer response times. The code that you generate to support marshaling of method arguments for a local server also supports marshaling of method arguments for a remote server. If you use a remote server, then you need to have only a single copy of the server loaded and registered on the server computer. Many remote clients can use this single COM server copy. Since only one server exists, updating the behavior of the COM component is a relatively painless, low-effort process.
The act of marshaling data between a client and a server is a key issue when implementing a COM object or server. Depending upon the relationship between the client and the server, you use different programming technologies to marshal data for method calls between a client application and a COM server.
Table 8.1 identifies the programming technologies employed to perform marshaling of data for method calls on a COM object, and also shows the types of marshaling, the boundaries across which you marshal the data, and the specific approach for marshaling the data.
Table 8.1 Marshaling Technologies for Data Transfer to COM Objects
Type of marshaling | Boundaries | Approach |
---|---|---|
No marshaling | DLL | Global addressing |
Standard marshaling | Process | Interface Definition Language |
Automation marshaling | Programming language | Automation marshaler |
Custom marshaling | Process | Special software, protocol |
When you host your COM object as an in-process server, you package the COM object in a DLL, which means that the COM object loads into the process space of the client. All of the COM object's methods can be called directly by the client, and data can be passed freely to and from the object.
When a client calls interface methods from a COM component hosted on a local or a remote server, data is transferred across process boundaries or between computer nodes on a network. To accomplish this transfer, you must implement marshaling code to ensure that the client and server know how to communicate. Visual C++ provides a utility—the MIDL compiler—that enables you to produce a DLL that implements standard marshaling between an out-of-process object and its client. To implement standard marshaling, you specify your interfaces using the Interface Definition Language (IDL). IDL is a strongly typed language, similar to Visual C++ in its syntax, that allows you to define your interfaces precisely.
The MIDL compiler compiles your IDL code and generates C source code that implements two components, a proxy and a stub. The proxy attaches to the client application, and the COM server uses the stub. You compile this code to create a proxy/stub DLL, which is used by COM to handle the mechanics of moving data between the client application and the COM server across process or computer boundaries.
The Automation marshaler is a COM server (oleaut32.dll) that provides marshaling services for the COM-based technology known as Automation (formerly known as OLE Automation). Automation allows client code written in languages other than C++ to access COM components. Automation is implemented by the COM interface IDispatch, which is discussed in greater detail in the next lesson. To use the Automation marshaler, you do not have to implement a dispatch interface. You can specify that your component interfaces use the Automation marshaler by defining them with the IDL attribute oleautomation, as shown in the following code:
[ oleautomation, object, uuid(A84DA762-6486-11D3-9347-0080C7FA0C3E), helpstring("IHello Interface"), pointer_default(unique) ] interface IHello : IUnknown { [propget, helpstring("property String")] HRESULT String([out, retval] BSTR *pVal); }; |
To make communication available between different languages, Automation defines a standard set of data types that can be packaged into a union data structure called a VARIANT. When you use the Automation marshaler, you must use Automation-compatible data types. The BSTR data type used in the previous code snippet is a string type used by Automation.
The Automation marshaler is not as efficient as the standard marshaling code generated by MIDL.
In some cases, standard marshaling may not be appropriate for an application. In such cases, a server object can provide support for custom marshaling.
To support custom marshaling, your server should implement the IMarshal interface. When a server supports custom marshaling, it does not use the standard marshaling infrastructure. Instead, COM requests that the server object form a packet, containing the data to be marshaled, and transmit the packet to the client context. COM instantiates an object-specified handler to receive the packet and act as a smart proxy to the client. The smart proxy unmarshals the packet to the client.
Communication can now occur across process boundaries just as with standard marshaling. The client is never aware that custom marshaling (or any form of marshaling) is occurring.
In general, marshaling is not an issue for developers of COM objects. MIDL generates marshaling code for standard COM interfaces, and the Automation marshaler provides marshaling for objects that implement dispatch interfaces. However, there are times when you may have specific reasons to enable an object to support custom marshaling, which include the following:
Every COM object executes within a specific context. In-process servers execute with the process address space of the client application. When you perform method calls from client to server, you marshal these calls from the client context to the server context. Standard marshaling consists of using an interface definition language and the MIDL compiler to generate a proxy/stub DLL to perform marshaling from a client in one process to a server in another process. Marshaling across languages employs the Automation marshaler. When you use the Automation marshaler you must use Automation-compatible data types. By implementing the IMarshal interface, you can create a custom marshaling mechanism to meet program-specific needs.