| Lesson 5 | Course project |
| Objective | Describe the course project. |
In COM Fundamentals II (Advanced COM), you will develop two new COM objects that demonstrate the two primary COM reuse mechanisms: containment/delegation and aggregation. Both objects reuse an existing COM component from COM Fundamentals I, allowing you to compare the two techniques side by side using the same underlying inner object.
The course project consists of three components:
Memo in server InfoMgr: reuses PhBookObj
from the PhBook project in COM Fundamentals I through
containment and delegationMemoAgg in server InfoMgrAgg: reuses
PhBookObj from the same PhBook project via aggregationInfoMgrCli: a COM client that consumes both Memo
and MemoAgg, demonstrating the difference between containment and
aggregation from the client's perspectiveCOM Fundamentals II builds on the project you developed in
Basic COM
(COM Fundamentals I). If you do not have the PhBook project from that
course, it is available from the Resources page. You will receive instructions on
how to download and install the files when you need them.
Each component in the course project teaches a distinct set of COM development skills.
Building Memo in InfoMgr teaches containment and
delegation in practice. You will implement an outer COM object that holds a private
PhBookObj member, forwards selected IPhBook interface calls
to it via delegation, and handles other calls with its own implementation. The outer
object maintains its own IUnknown identity throughout: clients always interact with
Memo's own QueryInterface, AddRef, and Release. The key skill is deciding
which calls to delegate and which to handle directly, and implementing the forwarding
code correctly without leaking the inner object's IUnknown to the client.
Building MemoAgg in InfoMgrAgg teaches aggregation and
the controlling unknown mechanism. You will implement an outer object that passes its
own controlling IUnknown to PhBookObj at construction time, allowing
PhBookObj's IPhBook interface to be returned directly to
clients via MemoAgg's QueryInterface. You will implement the
non-delegating IUnknown that MemoAgg uses for its own reference counting
during construction and teardown, before the aggregation relationship is fully
established. In Visual Studio 2022 with ATL, the
DECLARE_AGGREGATABLE macro generates the controlling unknown plumbing
automatically when aggregation support is selected in the ATL COM wizard.
Building InfoMgrCli teaches how a COM client consumes objects from
multiple servers, issues QueryInterface calls to discover interface support, and
manages reference counting using CComPtr to avoid manual AddRef and
Release calls. The client demonstrates that from its perspective, both Memo
and MemoAgg provide access to the same IPhBook interface.
The difference between containment and aggregation is entirely in how the outer object
is implemented, not in how the client consumes it.
The course examples were originally written for Visual C++ 6 (1998). The code compiles in Visual Studio 2022 with minor adjustments that are noted in each lesson and exercise where they apply. Install Visual Studio 2022 with the Desktop development with C++ workload:
visualstudio.microsoft.com/vs/features/cplusplus
During installation, ensure the following components are selected under Individual Components:
ATL is required. The ATL COM wizard generates the COM server project scaffolding,
IDL file, registration code, and the IUnknown and IClassFactory infrastructure for
both InfoMgr and InfoMgrAgg. Without ATL selected in the
installer, the wizard is not available and the ATL headers are not present.
Every COM component and every COM interface is identified by a globally unique
identifier (GUID): a 128-bit value that is statistically unique across all machines
and all time. The CLSID (Class Identifier) identifies a coclass and maps to the
server DLL or EXE that implements it via the Windows registry. The IID (Interface
Identifier) identifies a specific interface and maps to its vtable layout definition.
GUIDs are generated by the MIDL compiler during IDL compilation or by the
guidgen tool in the Windows SDK.
The standard COM object creation function is CoCreateInstance(). It
takes the CLSID of the component to create, the context in which to create it (in-process
DLL, out-of-process EXE, or remote via DCOM), and the IID of the interface to return.
Internally, CoCreateInstance() locates the registered server for the CLSID,
loads it if necessary, asks the server's Class Factory for a new instance via
IClassFactory::CreateInstance, and returns the requested interface pointer.
CoCreateInstanceEx() extends this for scenarios involving multiple interfaces
or remote activation.
In the course project, InfoMgrCli uses CoCreateInstance()
to create instances of both Memo and MemoAgg, then issues
QueryInterface calls to obtain the specific interface pointers it needs from each object.
CComPtr is the ATL smart pointer template for COM interface management.
It wraps a raw COM interface pointer, calls AddRef when initialized or
assigned, and calls Release when it goes out of scope or is reassigned.
Using CComPtr throughout InfoMgrCli eliminates the most
common source of COM memory leaks: forgetting to call Release on an
interface pointer before it goes out of scope or on an error return path.
The following example shows the pattern used in the course project to create a
Memo instance and obtain its IMemo interface using
CComPtr:
#include <windows.h>
#include <comdef.h>
#include <atlbase.h>
int main() {
CoInitialize(nullptr);
{
// Create an instance of Memo via the InfoMgr in-process server
CComPtr<IUnknown> pUnk;
HRESULT hr = CoCreateInstance(
CLSID_Memo,
nullptr,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
reinterpret_cast<void**>(&pUnk)
);
if (SUCCEEDED(hr)) {
CComPtr<IMemo> pMemo;
hr = pUnk->QueryInterface(IID_IMemo,
reinterpret_cast<void**>(&pMemo));
if (SUCCEEDED(hr)) {
// Use pMemo here.
// Calls to IPhBook methods are forwarded to PhBookObj
// via containment/delegation inside Memo.
}
// pMemo goes out of scope here: Release called automatically
}
// pUnk goes out of scope here: Release called automatically
}
CoUninitialize();
return 0;
}
The same pattern applies when creating a MemoAgg instance from
InfoMgrAgg. The client code is identical: the difference between
containment and aggregation is entirely inside the server implementation, invisible
to InfoMgrCli.
COM defines a threading model that specifies how method calls are dispatched to COM objects when clients and objects reside in different threads or processes. The threading model is declared by the component and registered in the Windows registry under the component's CLSID. The three primary threading models are:
The InfoMgr and InfoMgrAgg servers in the course project
use the STA threading model. In ATL, the threading model is declared using the
DECLARE_THREADING_MODEL macro or via the threading model selection in the
ATL COM wizard.
The course project was designed around COM, but the binary interface model it uses has evolved through several generations of Windows component technology. Understanding this timeline provides context for the advanced topics covered in later modules:
| Technology | Year | Purpose |
|---|---|---|
| OLE | 1991-93 | Object Linking and Embedding: the COM precursor for compound documents |
| COM | 1993 | Core binary component model: vtable interfaces, IUnknown, CLSID/IID |
| DCOM | 1996 | Distributed COM: extends COM across network boundaries via RPC |
| ActiveX | 1996 | COM-based browser-hosted controls: deprecated in modern browsers |
| COM+ | 1997-2000 | Transactional extension to COM: object pooling, queuing, security |
| .NET Interop | 2002+ | Runtime Callable Wrapper (RCW) and COM Callable Wrapper (CCW) |
| WinRT | 2012 | COM-derived ABI for Universal Windows Platform: IInspectable extends IUnknown |
COM remains foundational in the Windows platform. Shell extensions, DirectX, Office automation, and WinRT all use COM conventions at their interface boundary. Modern .NET applications consume COM objects via the RCW and expose .NET objects to COM clients via the CCW. The IUnknown rules and vtable-based interface model introduced in 1993 underpin all of these technologies.