Basic COM  «Prev  Next»

Lesson 2 The COM specification
Objective Define COM.

COM Specification

Before going any further into COM programming, it is worth answering the question directly: what is COM? The Component Object Model is a specification, not a piece of code you install and not a library you link against in the way you would link against a standard C++ library. It is a set of binary-level rules that describe how software components expose functionality to one another, plus a collection of operating system services from Microsoft that implement and enforce those rules at runtime. That distinction between "specification" and "implementation" matters throughout this course, and we will return to it in a moment.
At its core, COM provides a specification and a set of services for supplying client-server functionality through method (function) calls. A COM method follows a set calling procedure and uses a specific return type. Rather than existing on their own, COM methods are grouped together by placing them into a COM interface, and a COM interface is simply a group of related functions that belong together conceptually, the way a file-handling interface might group open, read, write, and close. In COM notation, we use a straight line with a circle at the end to denote an interface, a small piece of shorthand you will see repeated across diagrams throughout the rest of this course:
COM interface
COM interface

Interface navigation

COM requires every interface to implement specific functionality that supports two things: interface navigation, meaning the ability to ask an object whether it supports some other interface and get a pointer to it if so, and lifetime management through reference counting, meaning the ability to track how many active users of an object exist so it can be destroyed safely once nobody needs it anymore. This requirement is expressed by saying that every COM interface implements IUnknown. IUnknown is an interface specification, not an implementation, that defines the functions needed to support finding other interfaces and managing an object's lifetime.
The three methods that make this work, QueryInterface, AddRef, and Release, are worth naming now even though we examine them properly in a later lesson. QueryInterface is how navigation happens: a caller holding one interface pointer asks the object for a different interface pointer, and the object either hands one back or reports that it does not support that interface. AddRef and Release are how lifetime management happens: AddRef increments a count each time a new reference to the object is taken, and Release decrements it each time a reference is given up, with the object destroying itself once that count reaches zero. Getting these three methods right, and specifically making sure QueryInterface behaves symmetrically (if interface A can reach interface B, B must be able to reach A) and transitively (if A can reach B and B can reach C, A must be able to reach C directly), is the single most important discipline in COM programming. The full rules, with worked examples of what goes wrong when they are violated, are covered in IUnknown and Information Exchange.

COM is a specification. That means it is available in the form of documentation and can be printed and read like any other formal standard. The complete COM specification, along with a substantial amount of system code implementing it, is documented on Microsoft's own developer resources. Reading the specification directly is not required to work through this course, but it exists, and experienced COM developers do consult it when they hit edge cases that tutorials do not cover.


Semantics of an interface within the context of Microsoft's "Component Object Model"

Having defined COM at a high level, it is useful to define its central building block, the interface, with the same precision. In the context of Microsoft's Component Object Model, an interface is a contract that defines a set of methods a COM object must implement. The semantics of a COM interface can be understood through the following points.
  1. Contractual Nature: An interface in COM is a strict contract between the client, the code that uses the COM object, and the server, the COM object that implements the interface. The contract is defined entirely by the methods the interface declares. Once defined, an interface cannot be changed; it remains constant for as long as it exists.
  2. Interface Identifier (IID): Each COM interface is uniquely identified by a globally unique identifier, or GUID, known as an Interface Identifier (IID). Clients use this IID to request and interact with a specific interface on a COM object, typically by passing it as an argument to QueryInterface. Because an IID is just a 128-bit value guaranteed (statistically) never to collide with another one generated anywhere else, two interfaces can share a name in casual conversation while remaining completely distinct to the runtime, since it is the IID, not the name, that actually identifies them.
  3. Pure Abstract Class: In C++ terms, a COM interface is typically represented as a pure abstract class, meaning it contains only pure virtual methods and no data members. Every method in a COM interface is virtual and must be implemented by whichever concrete object claims to support that interface.
  4. Multiple Interfaces: A single COM object can implement multiple interfaces at once. Each interface provides a different functionality or a different view of the same underlying object. Clients query an object for the interfaces it supports using the QueryInterface method described above, rather than assuming support in advance.
  5. Interface Inheritance: COM supports interface inheritance, allowing a new interface to be defined based on an existing one. The derived interface inherits every method of the base interface and can add new methods on top. This is how COM extends functionality over time while preserving backward compatibility: rather than editing an existing interface, you derive a new one.
  6. Binary Standard: COM interfaces are defined at the binary level, which means any language capable of following the COM binary standard can implement or consume COM interfaces. This is what makes COM language-independent in practice, letting components written in C++, Visual Basic, or other languages interact freely. Because that binary contract has to work identically regardless of which language is on either end, COM also defines its own portable data types for anything that would otherwise be language-specific, most notably BSTR for strings, so that a string produced by one language's runtime can be read correctly by a completely different runtime on the other side of the call.
  7. Reference Counting: Interfaces in COM use reference counting to manage an object's lifetime, typically through the AddRef and Release methods introduced above. When a client acquires a pointer to an interface, it calls AddRef to increment the reference count. When it is finished with that interface, it calls Release, which decrements the count. Once the count drops to zero, the object is free to destroy itself.
  8. Interface Methods: The methods declared in an interface are purely abstract; the interface itself provides no implementation. The COM object that implements the interface supplies the concrete behavior. By convention, each method in a COM interface returns an HRESULT value to indicate success or failure, and may take additional parameters as required by that method's functionality. Checking the returned HRESULT after every call, rather than assuming success, is a habit worth building early, since silently ignored failures are a common source of hard-to-diagnose COM bugs.
  9. Versioning: Once an interface is published, it cannot be modified. If new functionality or changed behavior is needed, a new interface with a new IID is created instead. This guarantees that existing clients relying on the original interface continue to function correctly indefinitely, even as newer interfaces are introduced alongside it.

Example: IUnknown Interface

The IUnknown interface is the foundational interface in COM, from which all other interfaces must inherit, either directly or indirectly. It defines the three essential methods already introduced above, QueryInterface, AddRef, and Release, which together handle interface querying and reference counting and form the base on which COM's entire interface-based architecture rests. Every interface you will encounter in this course, no matter how specialized, is an IUnknown underneath.
Summary Pulling this together into a working definition: COM is a language-independent binary specification for building software components that communicate through interfaces. An interface, in turn, is a fixed contract, a pure abstract class in C++ terms, identified by a unique IID, whose methods a COM object must implement and whose lifetime the object manages through reference counting via IUnknown. Interface-based programming of this kind is fundamental to building modular, reusable, and interoperable software components, and it is the foundation the rest of this module builds on.

SEMrush Software