Lesson 1: COM Architecture

COM is a binary standard that defines a way for software objects, developed in different languages or operating on different platforms, to communicate. A COM component is a reusable software object that conforms to COM specifications.

In this lesson, you will learn about the basic architecture of Win32 operating systems, which allows you to create and use COM components in your programs. You will learn about the essential features of the COM specification, and how COM components are registered on a computer so that client programs can identify and make use of the functionality they offer.

After this lesson, you will be able to:

  • Describe the architecture that enables a client to use a COM object.
  • Describe the role of interfaces in COM.
  • Describe the purpose of the IUnknown interface.
  • Describe how globally unique identifiers (GUIDs) uniquely identify components and interfaces.
  • Describe the registry entries required to register a COM object.
  • Describe how to use the CoCreateInstance() function to create COM objects.
Estimated lesson time: 50 minutes

Using COM Objects

COM objects allow you to construct an application comprising a set of distinct cooperating components. One benefit of using COM objects is that they make interoperability available, irrespective of where the binary object is physically located. An important aspect of COM objects is that a program may attach dynamically to the object during execution.

For example, an application hosted in an executable file may link dynamically to a COM component hosted in a DLL. Because the component and executable are dynamically linked, they do not need to reside in the same location. To dynamically link to a binary object during execution, a program and the operating system cooperate to locate the object. COM defines all elements that both you and the operating system use in cooperation to ensure that the dynamic linking process works correctly.

Figure 8.1 shows the elements that participate in the dynamic linking process.

click to view at full size.

Figure 8.1 COM elements used to support dynamic linking

COM objects are implemented as .dll or .exe files. Every COM object that resides on your system must be registered with the Windows operating system. Registration information appears in the Windows registry. In the registry, you find a class identifier (ClassID) that identifies the object uniquely across all computers. The ClassID is a specially formatted globally unique identifier, or GUID. You will learn more about GUIDs later in this lesson.

When your client wants to link dynamically to a registered COM object, the client program uses the ClassID to identify the binary object. The COM run-time libraries, an integral component of the Windows operating system, provide the means for clients to locate and instantiate COM objects.

Once the COM libraries receive a ClassID, they search the HKEY_CLASSES_ROOT portion of the registry in an attempt to find the location of the COM object that owns that ClassID. If the search is successful, the COM libraries create an instance of the object and return a pointer to the object's interface. The client uses this pointer to call the methods that the object provides. Otherwise, an error is reported to the client. An application will typically interact with this dynamically linked binary object by executing methods listed in its interface pointer. If the object needs to initiate interaction with your client program, you register event handler methods with the object. Under appropriate and well-defined conditions, the object fires events that are processed by these handler methods.

One of the features of COM is location transparency. Client applications can be written without concern as to whether the objects used run within their process, in a different process on the same computer, or in a process on a separate computer.

COM Interfaces

Your COM object provides access to its services through one or more interfaces. A COM interface is a logical grouping of related methods identified by a GUID, known as an Interface Identifier (IID). COM components are often described in terms of the client/server model, wherein a COM server is a component that provides services to client programs through the methods it exposes. Using this model, an interface can be understood as a description of the services provided by a component.

You gain access to the methods provided by the interface by obtaining a pointer to the interface, which references a table of function pointers known as the vtable. Each function pointer in the vtable enables you to access a single method provided by the interface.

Figure 8.2 shows a COM server object named Encoder that exposes three interfaces. IUnknown (discussed in the following section) is the interface that must be implemented by every COM component. The IEncoder and ICommunicate interfaces are specified by the designer of the component to expose the Encoder object's services. Figure 8.2 illustrates the default notation for illustrating COM components. We have shown the methods provided by the interfaces to the left of the diagram.

click to view at full size.

Figure 8.2 Components and interfaces

Figure 8.2 shows that components and interfaces have separate GUIDs. The names of the GUIDs shown illustrate the kind of #defined IDs that you use in your code to refer to the different types of GUIDs. As you will learn later in this chapter, GUIDs are large hexadecimal numbers.

An interface is a logically separate entity from the component that implements the component. In other words, the same interface can be implemented by many different components. An interface is analogous to a C++ abstract class definition. The identity of an interface is established by its unique IID. Once you publish your interface specification to the world, you are guaranteeing to anyone who might want to use or implement your interface that it will not change. The number and order of the methods, and the data types of the arguments and return values, are guaranteed to remain the same. If you want to add or alter methods, you must define a new interface with a different IID.

The fact that an interface is obtained as a pointer to a function table (the vtable), would seem to imply that a COM object could provide only methods to client applications. However, many languages that support COM (Microsoft Visual Basic, for example) support the notion of properties—public data members of components that are analogous to the member variables of a C++ class. Components written in C++ implement properties as pairs of methods used to set and get the value of encapsulated class data. This technique is illustrated in Chapter 9.

NOTE
In this book we use the terms property and method to refer specifically to features of a COM interface. For regular C++ objects we use the terms member variable and member function.

IUnknown

The IUnknown interface, illustrated in Figure 8.3, must be implemented by every COM component.

click to view at full size.

Figure 8.3 The IUnknown interface

IUnknown contains three methods that you implement for your COM object. When you implement the QueryInterface() method, you provide a mechanism for a client program to access any of the interfaces supported by your COM object.

Because a single instance of a component may service requests from a number of different clients, you must implement a reference counter for your COM object. This private data member serves to track the number of connected client applications. The AddRef() and Release() methods operate on the reference counter. When the reference counter reaches zero, the component terminates itself.

You must implement your COM components so that the IUnknown methods are always the first three methods in the vtable. The methods exposed by the other interfaces are listed sequentially after these methods.

Figure 8.4 illustrates a possible vtable layout for the Encoder object illustrated in Figure 8.2.

Figure 8.4 The structure of a vtable

At the beginning of the vtable are the IUnknown methods, followed by the methods exposed by the IEncoder interface, and then followed by the ICommunicate methods. When the COM run-time library instantiates the Encoder object, it obtains a pointer to the beginning of the vtable (shown as pVtbl in Figure 8.4). This pointer can be used to call the first function in the table—the QueryInterface() function. Your object's implementation of the QueryInterface() function must return a pointer to the requested interface.

GUIDs

GUIDs are 128-bit numeric identifiers that uniquely distinguish each COM object and the specific interfaces supported by COM objects. GUIDs are guaranteed to be unique across the world and to remain unique for a very long time. You use the Windows command-line utility UUIDGEN.EXE (or the graphical user-interface version GUIDGEN.EXE) to generate GUIDs for your components and interfaces.

A detailed characterization of a GUID appears in Figure 8.5.

click to view at full size.

Figure 8.5 The construction of a GUID

A GUID actually exists in two formats: string and numeric. A string format appears in various locations in the registry. Numeric representations of GUIDs are necessary when using the GUID within client applications and within the actual COM object implementation.

As you can see in Figure 8.5, the numeric representation of a GUID is 128 bits in length. Within the structure _GUID, an unsigned long field named Data1 is 32 bits long. The fields named Data2 and Data3 are unsigned short values, each consuming 16 bits. Eight unsigned char values each require 8 bits. If you add all of this together, you find that the numeric representation of a GUID consists of a 128-bit representation.

When you use the numeric representation within your COM object or within C++ client code, you declare a variable and use a specific macro to initialize the variable to the associated numeric value. The macro that you use is named DEFINE_GUID and appears in the header file initguid.h. Typically, the variable name that you use begins with either the prefix CLSID or IID. These prefixes indicate whether the GUID refers to a COM object or to an interface supported by a COM object.

COM Registry Entries

When you install a COM object onto a computer, you must register the object by creating entries in the computer's registry.

Figure 8.6 illustrates the registry entries required for a typical COM object.

click to view at full size.

Figure 8.6 Registering the COM object

To register your COM object, you create entries under the HKEY_CLASSES_ ROOT subtree, under the predefined CLSID key. These entries enable the COM libraries to locate your COM object and load it into memory. Beneath the CLSID key, you type a subkey that is the string form of your CLSID COM object. Beneath this subkey, you attach the subkey that provides COM with the path to the component server, as shown here:

HKEY_CLASSES_ROOT\CLSID\{64CE33A0-6B03-11d3-9352-0080C7FA0C3E}\     InprocServer32 = c:\Encoder\debug\Encoder.dll

The name of the subkey—InprocServer32—indicates that the component server is a DLL on the local computer.

Immediately beneath HKEY_CLASSES_ROOT, you can provide a key value representing a string name for your COM object. This string name is a version-independent programmatic identifier (ProgID). Associated with this string name, you type a subkey explicitly named CLSID whose value indicates the GUID for your COM object in its string format. A path representation for this entry looks like this:

HKEY_CLASSES_ROOT\Encoder\CLSID = {64CE33A0-6B03-11d3-9352-0080C7FA0C3E}

By using a function named CLSIDFromProgID(), a client application can retrieve the CLSID from the ProgID. By providing the ProgID, you enable a client application developer to create an instance of your COM object without having to go through the error-prone process of typing a CLSID into the source code. Visual Basic client code will always specify COM objects as ProgIDs, so you will need to register a ProgID if you are intending to use your objects with non-C++ clients.

Creating Objects Using CoCreateInstance()

Once the CLSID is obtained using the CLSIDFromProgID() function, a client application submits the CLSID to the COM run-time library to load the COM object and retrieve an interface pointer. The function used to perform this operation is CoCreateInstance(). Using the CLSID and the registry, CoCreateInstance() locates the specified object, creates an instance of that object, and returns an interface pointer to that object.

The signature of the CoCreateInstance() function is shown below:

STDAPI CoCreateInstance (REFCLSID rclsid,     LPUNKNOWN pUnkOuter,     DWORD dwClsContext,     REFIID riid,     LPVOID * ppv) ;

The first argument to the function is the CLSID of the object. The second argument is used to aggregate the object as part of another object. Aggregation is explained in detail in Lesson 2 of Chapter 10.

The third argument specifies the execution context of the object. The possible values for this argument are:

CLSCTX_INPROC_SERVER CLSCTX_INPROC_HANDLER CLSCTX_LOCAL_SERVER CLSCTX_REMOTE_SERVER

Execution contexts are discussed in the following lesson.

The riid argument is the IID of the requested interface. If the COM object is created successfully and supports the interface requested by the client, an interface pointer is returned through the ppv argument.

A detailed operational analysis of COM object creation that results from executing the CoCreateInstance() function appears in Figure 8.7.

click to view at full size.

Figure 8.7 The COM object creation sequence

This sequencing diagram shows the elements used in the COM object creation process. The specific entities involved in COM object creation are across the top of the diagram. The sequence of interactions among the participating entities proceeds from the top of the diagram down to the bottom of the diagram.

A client application requests access to a specific interface supported by the COM object using the CoCreateInstance() function. This function accesses the COM library, requesting that the COM object be loaded. The COM library queries the registry along the indicated path, obtaining the path to and the name of the component server (in this case, a DLL).

When the component server is initialized, a class factory object is instantiated. A class factory is an object that creates other objects of a specific class, which you implement as part of your COM component. A class factory implements the IClassFactory interface. A pointer to the class factory interface then propagates all the way back to the COM library. Using this interface pointer, the COM library executes the CreateInstance() method supported by the class factory object. Your implementation of this method creates an instance of the COM class that actually provides all the methods that might be accessed by a client application. The act of creating this class initializes the vtable so a client application can execute the supported methods. A pointer to the IUnknown interface is returned to the COM library. When the COM library receives this interface pointer, it releases the class factory object and returns the IUnknown pointer to the client application. This pointer now references the component vtable, enabling the client to execute the methods provided by the COM object.

The entity interaction sequence outlined previously occurs in its entirety only the first time a client instantiates a COM object. If another client wants to use methods supported by this COM object, an interface pointer to the same COM object returns to the new client, and the server increments the reference count by calling IUnknown::AddRef(). When the client has finished using the component interface, it must call the IUnknown::Release() method to decrement the reference count. If any client fails to perform this action, the reference count will never reach zero, and the component will not be destroyed, leading to serious memory leakage problems.

Lesson Summary

COM allows dynamic linking of binary software components that can reside on a local computer or on a computer across a network. The COM run-time library uses registry entries to locate and to load a COM object. Every COM object supports the IUnknown interface and additional component-specific interfaces. Each interface is a collection of one or more methods. Separate GUIDs identify the COM object and every interface exposed by the object. Within your class implementation you provide an object known as a class factory that creates an instance of your COM object.



Microsoft Press - Desktop Applications with Microsoft Visual C++ 6. 0. MCSD Training Kit
Desktop Applications with Microsoft Visual C++ 6.0 MCSD Training Kit
ISBN: 0735607958
EAN: 2147483647
Year: 1999
Pages: 95

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net