Component Object Model (COM)

Component Object Model (COM) is a simple Microsoft specification method that defines a binary standard for exchanging code between two systems, regardless of the Operating System (OS) or programming language. COM provides access to distributed client object services and is used to share cross-platform binary code and programming languages. Portability - COM's primary objective - is achieved through well-defined COM object interfaces.[1]

COM objects are discrete components, each with a unique identity, which exposes interfaces that allow applications and other components to access their features. COM objects are more versatile than Win32 DLLs because they are completely language-independent, have built-in interprocess communications capability, and easily fit into an object-oriented program design. COM was first released in 1993 with OLE2, largely to replace the interprocess communication mechanism DDE used by the initial release of OLE. ActiveX also is based on COM.[2]

To understand COM (and therefore all COM-based technologies), it is crucial to understand that it is not an object-oriented language but a standard. Nor does COM specify how an application should be structured; language, structure, and implementation details are left to the application developer. Rather, COM specifies an object model and programming requirements that enable COM objects (also called COM components, or sometimes simply objects) to interact with other objects. These objects can be within a single process, in other processes, and can even be on remote computers. They can be written in different languages, and they may be structurally quite dissimilar, which is why COM is referred to as a binary standard; a standard that applies after a program has been translated to binary machine code. The only language requirement for COM is that code is generated in a language that can create structures of pointers and, either explicitly or implicitly, call functions through pointers. Object-oriented languages such as C++ and Smalltalk provide programming mechanisms that simplify the implementation of COM objects, but languages such as C, Java, and VBScript can be used to create and use COM objects. COM defines the essential nature of a COM object. In general, a software object is made up of a set of data and the functions that manipulate the data. A COM object is one in which access to an object's data is achieved exclusively through one or more sets of related functions. These function sets are called interfaces, and the functions of an interface are called methods. Further, COM requires that the only way to gain access to the methods of an interface is through a pointer to the interface. Besides specifying the basic binary object standard, COM defines certain basic interfaces that provide functions common to all COM-based technologies, and it provides a small number of functions that all components require. COM also defines how objects work together over a distributed environment and have added security features to help provide a system and component integrity.[3]

Definitions of the Basic Elements in COM[4]
Let's go from the bottom up. An interface is simply a group of functions. Those functions are called methods. Interface names start with I, for example IShellLink. In C++, an interface is written as an abstract base class that has only pure virtual functions.

  • Interfaces may inherit from other interfaces. Inheritance works just like single inheritance in C++. Multiple inheritance is not allowed with interfaces.
  • A coclass (short for component object class) is contained in a DLL or EXE, and contains the code behind one or more interfaces. The coclass is said to implement those interfaces. A COM object is an instance of a coclass in memory. Note that a COM "class" is not the same as a C++ "class", although it is often the case that the implementation of a COM class is a C++ class. A COM server is a binary (DLL or EXE) that contains on or more coclasses.
  • Registration is the process of creating registry entries that tell Windows where a COM server is located. Unregistration is the opposite - removing those registry entries.
  • A GUID (rhymes with "fluid", and stands for globally unique identifier) is a 128-bit number. GUIDs are COM's language-independent way of identifying things. Each interface and coclass has a GUID. Since GUIDs are unique throughout the world, name collisions are avoided (as long as you use the COM API to create them). You will also see the term UUID (which stands for universally unique identifier) at times. UUIDs and GUIDs are, for all practical purposes, the same. A class ID, or CLSID, is a GUID that names a coclass. An interface ID, or IID, is a GUID that names an interface. There are two reasons GUIDs are used so extensively in COM:
    1. GUIDs are just numbers under the hood, and any programming language can handle them.
    2. Every GUID created, by anyone on any machine, is unique when created properly. Therefore, COM developers can create GUIDs on their own with no chance of two developers choosing the same GUID. This eliminates the need for a central authority to issue GUIDs.
  • An HRESULT is an integral type used by COM to return error and success codes. It is not a "handle" to anything, despite the H prefix.
  • Finally, the COM library is the part of the OS that you interact with when doing COM-related stuff. Often, the COM library is referred to as just "COM,"

Attributes of Interfaces[5]
Given that an interface is a contractual way for a COM component to expose its services, there are several very important points to understand:

  • An interface is not a class. Although an instance of a class can be created (instantiated) to form a COM component, an interface cannot be instantiated by itself because it carries no implementation. A COM component must implement that interface and that COM component must be instantiated in order for an interface to exist. Furthermore, different COM component classes may implement an interface differently, so long as the behavior conforms to the interface definition for each associated component (such as two COM components that implement IStack where one uses an array and the other a linked list). Thus the basic principle of polymorphism fully applies to COM components.
  • An interface is not a COM component. An interface is just a related group of functions and is the binary standard through which clients and COM components communicate. The COM component can be implemented in any language with any internal state representation, so long as it can provide pointers to interface member functions.
  • COM clients only interact with pointers to interfaces. When a COM client has access to a COM component, it has nothing more than a pointer through which it can access the functions in the interface, called simply an interface pointer. The pointer is said to be opaque because it hides all aspects of internal implementation. You cannot see the COM component's data, as opposed to C++ object pointers through which a client may directly access the object's data. In COM, the client can only call methods of the interface to which it has a pointer. This encapsulation is what allows COM to provide the efficient, safe, robust binary standard that enables local or remote transparency.
  • COM components can implement multiple interfaces. A COM component can - and typically do - implement more than one interface. That is, the COM class has more than one set of services to provide. For example, a COM class might support the ability to exchange data with COM clients, as well as the ability to save its persistent state information (the data it would need to reload to return to its current state) in a file at the client's request. Each of these abilities is expressed through a different interface (IDataObject and IPersistFile), so the COM component must implement two interfaces.
  • Interfaces are strongly typed. Every interface has its own interface identifier (a GUID, which is described later), thereby eliminating any chance of a collision that would occur with human-readable names. The difference between components and interfaces has two important implications. When you create a new interface, you must also create a new identifier for that interface. When you use an interface, you must use the identifier for the interface to request a pointer to the interface. This explicit identification improves robustness by eliminating naming conflicts that would result in run-time failure.
  • Interfaces are immutable. COM interfaces are never versioned, which means that version conflicts between new and old components are avoided. A new version of an interface, created by adding more functions or changing semantics, is an entirely new interface and is assigned a new, unique identifier. Therefore a new interface does not conflict with an old interface even if all that changed is one operation or the semantics (but not even the syntax) of an existing method. Note that, as an implementation efficiency, it is likely that two very similar interfaces can share a common internal implementation. For example, if a new interface adds only one method to an existing interface, and you as the component author wish to support both old-style and new-style clients, you would express both collections of capabilities through two interfaces, yet internally implement the old interfaces as a proper subset of the implementation of the new.

Threading in COM[6]
In COM, threading is addressed through a concept known as apartments.[18] All COM objects live in exactly one apartment, which might either be single-threaded or multi-threaded. There are three types of apartments in COM: Single-Threaded Apartment (STA), Multi-Threaded Apartment (MTA), and Thread Neutral Apartment (NA). Each apartment represents one mechanism whereby an object's internal state may be synchronized across multiple threads. A process can consist of multiple COM objects, some of which may use STA and others of which may use MTA. All threads accessing COM objects similarly live in one apartment. The choice of apartment for COM objects and threads is determined at run-time, and cannot be changed.

Component Object Model
source: Wikipedia

Threads and objects which belong to the same apartment follow the same thread access rules. Method calls that are made inside the same apartment are therefore performed directly without any assistance from COM. Method calls made across apartments are achieved via marshaling. This requires the use of proxies and stubs.

See Also


Further Reading