Now that we've seen both the client side and the server side of the puzzle, we can complete the picture and examine what COM does internally to make it all work. More precisely, our interest is in how COM takes a client's request for an object instance and asks the right server to create the object and return its interface pointer back to the client. Of course, because the structure of an in-process server and a local server differ, COM treats both differently, as illustrated in Figure 5-5 and Figure 5-6 on the following page. In these diagrams, the client itself is calling CoGetClassObject and IClassFactory::CreateInstance, an identical process regardless of the server used. This is an important idea of Local/Remote Transparency: the client code need not differentiate between server contexts, and the same client code works equally well for all types of server. COM automatically establishes the necessary communication elements so that the client can make interface calls across the boundaries involved.
Figure 5-5.
The creation process for an in-process server.
To be precise, the COM Library itself doesn't perform implementation location—finding the server that provides the implementation of a particular CLSID and setting up an RPC connection as necessary. This is the role of COM's SCM. When a client asks for an object of a CLSID, the COM Library contacts the local SCM (the one on the same machine) and requests that the appropriate server be located or launched, returning a "connection" to the COM Library.
Figure 5-6.
The creation process for a local server.
The actions taken by the local SCM and the "connection" it returns depend on the type of server that is registered for the CLSID:
Server | Description |
In-Process | The SCM returns the pathname of the DLL containing the object server implementation. The COM Library then loads the DLL and asks it for an interface pointer to the class factory. |
Local | The SCM starts the local executable, which registers a class factory on startup. The SCM then returns an RPC handle to the stub connected to that class factory. |
Remote | The local SCM contacts the SCM running on the appropriate remote machine and forwards the request to the remote SCM. The remote SCM brings that server into memory (be it DLL or EXE) and sets up an RPC connection to a remote stub. |
In any of these cases, the COM Library gets back a connection. If that connection is in-process, the COM Library loads the DLL directly and obtains the necessary interface pointer from DllGetClassObject. Otherwise, the COM Library locates the necessary proxy or handler implementation creates an instance of that proxy object, and gives it the RPC connection. In turn, that proxy provides an interface pointer that the COM Library returns to the client. The end result is that no matter what type of server is involved, the COM Library itself needs only to load some DLL and get an in-process interface pointer from it that it can then give to a client. The whole process the SCM follows is illustrated in Figure 5-7.
Figure 5-7.
The role of the SCM.
It is important to realize how a proxy or handler object completely encapsulates the RPC connection to the local or remote object behind its own interfaces. Local/Remote Transparency is, again, the idea that a client assumes it is always working with an in-process implementation of the object. Whether or not the object is fully or partially implemented inside some in-process module is irrelevant to the client because such a setup is entirely an object implementation issue. The one thing that is necessary to make such transparency work is that the client must transfer execution to whatever in-process element it has by making a function call. After that in-process element has control, it can perform whatever RPC it needs to complete the client's request. This is why COM does not support direct access of an object's data members—such access does not require any transfer of control because it is based on memory reads and writes to memory offsets. It is very difficult and impractical to have something monitor such memory access so that the request can be forwarded to a local or remote server. So, instead, OLE supports a number of data exchange interfaces, such as IDataObject (see Chapter 10) and IDispatch (see Chapter 14) through which data can be accessed by means of calls to interface members.