The Component Object Model (COM) is a binary standard that defines how objects are created and destroyed and, most importantly, how they interact with each other. As long as applications follow the COM standard, different applications from different sources can communicate with each other across process boundaries. People use COM to make communication with other applications easy.
Because COM is a binary standard, it is language independent. You do not have to use C++ to implement COM. You can use any language that supports tables of function pointers.
A COM interface is a collection of logically related methods that express a single functionality. For example, the IAsyncReader interface enables reading of MEDIATYPE_Stream data. All COM interfaces derive from IUnknown, and all are named by a globally unique interface identifier (IID).
A COM class is an implementation of one or more COM interfaces, and a COM object is an instance of a COM class. A Microsoft® DirectShow™ filter, for example, is a COM object. Each object has a globally unique class identifier (CLSID).
Globally unique identifiers (GUIDs) are extremely long integers that identify COM interfaces and objects, and are used to eliminate name collisions across applications.
All access to a COM object is through pointers to its interfaces. Interface methods are purely virtual and are stored in a table called a vtable. The interface pointer points to the vtable's beginning. A COM interface defines the parameter types and the syntax for each of its methods. The COM class provides an implementation for each method of the interface.
Once a COM class has been defined and assigned a CLSID, you can create an instance of the object. There are several ways to create an instance of the class, including using the COM CoCreateInstance or IClassFactory::CreateInstance methods , or the C++ new operator.
When you create an instance of an object, the call returns a pointer to one of the object's interfaces. Once you have an initial pointer to an interface on the object, you can use the IUnknown::QueryInterface method to find out whether the object supports another specific interface, and, if so, to get a pointer to that interface. COM supplies many standard interfaces that support data storage and transfer, notification, and basic connectivity with other objects, including IStream, IPropertyPage, and IMoniker. DirectShow, in turn, adds its own COM interfaces, such as IAMDirectSound, that clients of DirectShow objects can query for to determine if the object supports a particular functionality. To use COM interfaces, clients must know the interface definitions and the IID to query for (IID_interfacename). For example, assume you have a pointer to a COM object's IUnknown interface in the pUnknown variable. You can query to see if the object supports IAMDirectSound with the following code.
hr=pUnknown->QueryInterface(IID_IAMDirectSound, (void **)&pIAMDSound);
IUnknown is the basic COM interface on which all others are based. IUnknown has three methods—QueryInterface, AddRef, and Release—that implement interface querying and reference counting. All COM interfaces inherit these three methods from IUnknown.
Reference counting is the technique by which an object (or, strictly, an interface) decides when it is no longer being used and can therefore destroy itself. COM objects are dynamically allocated from within the object and multiple clients can use them simultaneously. To avoid wasting memory, the COM object must keep track of the number of clients using it, and destroy itself when clients no longer need it. The number of clients using the object is maintained in the reference count. Every time a new interface pointer to the COM object is created, the client using the object must increase the reference count by calling AddRef on the interface pointer. Every time a client destroys an interface pointer to the object, it must first decrease the reference count by calling Release on the interface pointer.
Binding associates a method with a pointer to its memory location. At compile time, a COM object's client is bound to the vtable locations of the object's interface methods. This is called early binding. With some languages, such as Microsoft® Visual Basic®, a vtable interface is difficult to access. Dispatch interfaces, identified by dispatch identifiers (DISPIDs), allow clients to access member functions not by position in a vtable, but by a human-readable name. Dispatch interfaces are accessed through the COM IDispatch interface and its Invoke method, which converts the names of the dispatch interface's functions to DISPIDs. The client retrieves the DISPIDs at run time. This is called late binding. To allow late binding, a COM object must implement the IDispatch interface and a mapping of function names and function parameters to a set of DISPIDs. In DirectShow, CBaseDispatch implements the IDispatch interface.
Marshaling is the process of passing function arguments and return values among processes and machines. An in-process proxy packages arguments for the member function of an object in another process, and generates a remote procedure call to the other process. In the other process, a stub receives the call and unpacks the data, and calls the object through its interface. Dispatch interfaces do not need proxies and stubs and so are easier to use than vtable interfaces in out-of-process applications. Vtable interfaces, however, can be considerably faster, particularly in in-process applications. You can also write dual interfaces that have both tables of function pointers and dispatch interfaces. Dual interfaces can be nearly as fast as vtable interfaces, while allowing the flexibility of dispatch interfaces.
For more information about how DirectShow uses COM, see DirectShow and COM. For general information about COM, see the "COM" section in the Microsoft Platform SDK, or an introductory book such as ActiveX OLE by David Chappell.
© 1997 Microsoft Corporation. All rights reserved. Terms of Use.