Basic COM  «Prev  Next»

Lesson 1

Component Object Model Basics

A software component is a modular, reusable, and self-contained unit of software that encapsulates a specific piece of functionality. It is designed to interact with other components, often inside a larger system or application, through well-defined interfaces rather than through direct access to its internals. This distinction matters more than it might first appear: a component is not simply "a chunk of code that does something." It is code that has been packaged so that other code, possibly written by a different team, in a different language, and compiled years apart, can use it safely without knowing how it works on the inside. The idea did not originate with COM. It grew out of decades of frustration with tightly coupled systems, where changing one part of a program meant recompiling, retesting, and often breaking several other parts. Component thinking is an answer to that fragility. If a component exposes a stable contract, the code behind that contract can change freely as long as the contract itself does not. That single idea underlies everything else in this module.
The key characteristics of a software component include:
  1. Encapsulation: A software component hides its internal implementation details, exposing only what is necessary through its interface or interfaces. The internal workings, whatever data structures, algorithms, or third-party libraries it relies on, are invisible to the outside world. Only the behavior defined by the interface is reachable. This is what allows a component's author to rewrite the entire implementation, for performance or correctness, without breaking anyone who consumes it.
  2. Reusability: Components are built to be reused across different applications or systems, not just within the project that first created them. Because they are self-contained and interact only through standardized interfaces, they can be dropped into different environments with little or no modification. This is the property that makes a component economically worthwhile: the cost of building it well is paid once, and the benefit is collected many times.
  3. Interoperability: Software components can interact with each other largely regardless of the programming language or platform used to build them. This is achieved through well-defined interfaces and binary standards, such as COM on Windows, which allow components written in C++, Visual Basic, or other languages to work together at the binary level rather than the source level. That binary-level agreement is the technical heart of COM, and it is what the rest of this module unpacks.
  4. Modularity: Components represent a modular approach to software construction. Each component typically implements one coherent piece of functionality, which makes the overall system easier to develop, test, maintain, and replace one part at a time without disturbing the rest. It is worth noting that "module" here refers to this organizational, component-level sense of the word, the kind of modularity software architects have talked about for decades, and not the newer C++20 module system (import and export), which solves a related but different problem at the compiler and build-system level. If you are working in a modern C++20 codebase and want the compiler's own module facilities, that is a separate topic covered in Building C++ Classes, not something COM itself provides.
  5. Interfaces: A software component exposes one or more interfaces that define the methods and properties other components or applications can use to interact with it. These interfaces act as a contract the component agrees to honor. Once published, a COM interface is expected to never change; if new functionality is needed, a new interface is added alongside the old one rather than modifying it. This "interfaces are immutable" rule is one of the more unusual disciplines COM enforces, and it is the reason COM software from the 1990s can still, in principle, talk to software written today.
  6. Independent Deployment: Components are generally designed to be deployed independently of one another. This means a single component can be developed, tested, updated, and shipped on its own schedule, separately from the other components or systems it interacts with. In practice this is what let large Windows applications ship incremental updates to individual DLLs rather than recompiling and redistributing the whole program.

Examples of Software Components

  • COM (Component Object Model) Components: Binary components that can be used across different languages and environments in Windows, the primary subject of this course.
  • JavaBeans: Reusable software components in Java that can be manipulated visually inside a builder tool, following a different but philosophically similar contract-based model.
  • .NET Assemblies: Components in the .NET framework that can be shared across multiple applications, and which in many respects succeeded COM as Microsoft's preferred component model for managed code.
  • Web Services: Components accessed over a network via standard protocols such as HTTP and SOAP, extending the same reuse-through-interface idea across machine boundaries rather than within a single process.

In Summary A software component is a building block of software, designed to be self-contained, reusable, and interoperable. It simplifies development by promoting modularity and letting developers assemble complex systems out of smaller, well-tested parts that can work together without needing to understand one another's internals.

Component Software

COM, as its name indicates, is a specification and a set of development conventions that support the construction of software components. But what, concretely, is a software component in COM terms?
In general, a software component is a module that follows some component specification; in this course, that specification is COM. The two most historically prominent component technologies are COM and JavaBeans, and while their mechanics differ, both are built around the same idea of functionality exposed through a set of functions or methods, not through shared source code. Unlike a C++ library, where you need the class definition and typically a static or dynamic link to the actual code, a component consumer needs neither. All that is required is a definition of the component's services and a way to locate and load it.
That loading happens at runtime, when a service provider, such as the COM library itself, loads the component on demand. Once loaded, its services become available to the calling code. This is called binary-level integration, and it is the mechanism that lets a Visual Basic application call into a component written in C++ without either side needing the other's source. A common metaphor for describing a component's surface is to talk about its properties, methods, and events (or exceptions). Properties are exposed indirectly, through paired "get" and "set" methods rather than direct field access. A component performs an action when one of its methods is called. Events or exceptions are notifications the component sends back to whatever code is using it, typically to report either useful state changes or failures.
Application developers build applications on top of components by loading them, getting and setting their properties, calling their methods, and handling whatever events or exceptions the components raise. This pattern, load, configure, call, listen, repeats at every layer of a COM-based system, from the smallest utility component up through full application frameworks.

COM Module Introduction

To be an effective COM programmer, you need to spend real time on COM fundamentals before touching higher-level tooling. This module and the next one cover those core concepts, which form the foundation for understanding how the Active Template Library (ATL) supports COM development later in the course. Time invested here pays off directly: nearly every ATL class you will use later is a thin, convenience-oriented wrapper around the raw mechanisms introduced in this module. Skipping ahead to ATL without this grounding tends to produce code that compiles, but that the programmer cannot actually debug or extend with confidence.
In this module, we explore COM's binary standard through its method and interface requirements. We examine the IUnknown interface, which every COM interface must ultimately derive from, and we describe interfaces using:
  1. interface definition language (IDL),
  2. COM objects,
  3. interface navigation, and
  4. reference counting.
A note on terminology before continuing: the IDL used here is Microsoft Interface Definition Language, the dialect COM uses to describe interfaces and generate type libraries. It is a distant cousin of CORBA IDL, and the two are sometimes confused because they share a name and a similar surface syntax. If you have prior exposure to CORBA and want a precise comparison of terms, the CORBA Fundamentals Glossary is the right reference; for this course, treat Microsoft IDL as the only IDL in scope and expect the two syntaxes to diverge in meaningful ways once you get past the basics.
Finally, we use IDL to describe a COM object and its interfaces, and to produce a type library from that description. A type library is essentially a compiled, language-neutral description of a component's interfaces, and it is what lets tools such as Visual Basic or scripting hosts discover a component's methods and properties without needing a C++ header file.
At the end of the module you will be able to:
  1. Develop IUnknown methods for interface navigation and reference counting
  2. Develop your own custom COM interfaces
  3. Use IDL to describe COM interfaces and COM objects
  4. Use IDL to produce a type library


IUnknown interface

The IUnknown interface deserves a preview here, since almost every mistake beginners make in COM traces back to misunderstanding it. IUnknown exists to guarantee three things about any COM object: deterministic lifetime management through reference counting, via its AddRef and Release methods, and correct, symmetric, transitive behavior in QueryInterface, the method used to ask an object whether it supports a given interface. "Symmetric" means that if interface A can be queried to reach interface B, then B must be able to reach A again. "Transitive" means that if A can reach B, and B can reach C, then A must also be able to reach C directly. These rules sound abstract in isolation, but violating them is one of the most common sources of subtle, hard-to-reproduce COM bugs, the kind that surface as crashes or memory leaks far from where the actual mistake was made. A full treatment of these rules, with worked examples, is covered separately in IUnknown and Information Exchange, which builds directly on what you learn here.

Getting the most out of this Module

To get the most out of this module, you should already be comfortable with the following C++ concepts:
  1. classes and structures,
  2. constructors,
  3. inheritance,
  4. multiple inheritance,
  5. virtual functions, and
  6. pure virtual functions.

Virtual functions in particular are worth a moment of attention before you move on, since COM's interface model leans on them heavily: every COM interface is implemented in C++ as an abstract base class made up entirely of pure virtual functions, and every concrete component class derives from one or more of those interfaces. If your background with virtual functions is a little rusty, the two rules that matter most going into this module are to always provide a virtual destructor on any polymorphic base class, and to avoid calling virtual functions from within constructors or destructors, since the vtable is not fully set up at those points and the call will not resolve the way you expect. The Guidelines for Virtual Functions page covers both of these in more depth, along with several related pitfalls.
You do not have to be an expert in any of these areas going in. If you need a refresher, check out one of the books recommended on the Resources page before starting the next lesson, where we begin working through IUnknown and interface navigation directly.

SEMrush Software