Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

The Active Template Library (ATL) is a set of template-based C++ classes that enable you to create small, fast Component Object Model (COM) objects with minimal boilerplate. Where MFC provides a full application framework, ATL focuses specifically on COM — providing implementations of IUnknown, IDispatch, class factories, apartment threading models, connection points, and ActiveX control support, all with near-zero runtime overhead. ATL relies heavily on C++ templates and compile-time polymorphism, so the code generated is lean and does not require the MFC runtime.
ATL continues to be supported in Visual Studio, but like MFC, it is no longer receiving new features or documentation updates. It remains the standard way to write lightweight COM servers, shell extensions, and ActiveX controls in C++.

Why ATL for COM?

Writing COM objects manually requires implementing QueryInterface, AddRef, and Release on every object, building class factories, registering the object in the Windows registry, and handling threading correctly. ATL automates all of this through template classes while keeping the generated binary small.

CComObject

Template that adds reference counting (AddRef/Release) and QueryInterface routing to any ATL class. It is the standard way to create heap-allocated COM objects.

CComPtr / CComQIPtr

RAII smart pointers for COM interface pointers. CComPtr<IFoo> automatically calls AddRef on assignment and Release on destruction. CComQIPtr additionally calls QueryInterface.

CComBSTR

RAII wrapper around the BSTR string type used in COM Automation. Automatically frees the string on destruction and provides conversion to/from LPCWSTR.

CComVariant

RAII wrapper around VARIANT, the polymorphic value type used in COM Automation. Supports automatic type coercion and proper VariantClear on destruction.

ATL Smart Pointers vs std::shared_ptr

ATL’s CComPtr and C++‘s std::shared_ptr both manage object lifetimes but serve different purposes:
FeatureCComPtr<T>std::shared_ptr<T>
ProtocolCOM AddRef/ReleaseCustom deleters (default: delete)
Type systemCOM interfaces (IUnknown)Any C++ type
Thread safetyInterlocked ref count (per COM threading model)Atomic ref count
QueryInterfaceVia CComQIPtrNot applicable
Suitable forCOM objects, Windows Shell, OLEGeneral C++ objects
#include <atlbase.h>
#include <atlcom.h>

void UseCComPtr() {
    // CComPtr auto-releases when it goes out of scope
    CComPtr<IStream> pStream;
    HRESULT hr = CreateStreamOnHGlobal(nullptr, TRUE, &pStream);
    if (SUCCEEDED(hr)) {
        // Write to stream
        const char data[] = "Hello COM!";
        ULONG written = 0;
        pStream->Write(data, sizeof(data) - 1, &written);
    }
    // pStream->Release() called automatically at scope exit
}

void UseCComQIPtr() {
    CComPtr<IUnknown> pUnk;
    // ... obtain pUnk from somewhere ...

    // CComQIPtr calls QueryInterface automatically
    CComQIPtr<IStream> pStream(pUnk);
    if (pStream) {
        // Use pStream
    }
}

Key ATL Classes

CComObject<CYourClass> wraps your implementation class to provide stack or heap COM objects. For heap objects use CComObject<T>::CreateInstance().
#include <atlbase.h>
#include <atlcom.h>

class ATL_NO_VTABLE CMyCalculator :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CMyCalculator, &CLSID_MyCalculator>,
    public IMyCalculator
{
public:
    DECLARE_REGISTRY_RESOURCEID(IDR_MYCALCULATOR)
    BEGIN_COM_MAP(CMyCalculator)
        COM_INTERFACE_ENTRY(IMyCalculator)
    END_COM_MAP()

    // IMyCalculator implementation
    STDMETHODIMP Add(long a, long b, long* pResult) {
        if (!pResult) return E_POINTER;
        *pResult = a + b;
        return S_OK;
    }
};

OBJECT_ENTRY_AUTO(CLSID_MyCalculator, CMyCalculator)
CComBSTR eliminates the need to manually call SysAllocString and SysFreeString.
#include <atlbase.h>

void DemoCComBSTR() {
    // Construct from a wide string literal
    CComBSTR name(L"Hello, ATL!");

    // Pass to a COM method expecting BSTR
    // pObject->SetName(name);  // CComBSTR is implicitly convertible to BSTR

    // Append more text
    name.Append(L" World");

    // Get length in characters
    UINT len = name.Length();

    // name.SysFreeString() called automatically at scope exit
}
CComVariant wraps VARIANT with constructors for common types and auto-calls VariantClear on destruction.
#include <atlbase.h>

void DemoCComVariant() {
    CComVariant vInt(42);            // VT_I4
    CComVariant vStr(L"Hello");      // VT_BSTR
    CComVariant vBool(true);         // VT_BOOL
    CComVariant vDbl(3.14);          // VT_R8

    // Type coercion
    CComVariant vCoerced;
    vCoerced.ChangeType(VT_R8, &vInt); // Converts 42 → 42.0

    // Comparison
    bool equal = (vDbl == CComVariant(3.14));
}

Simple ATL COM Server Example

The following shows the minimal structure of an in-process COM server (DLL) built with ATL. In practice, you would generate this scaffolding using the ATL Project and ATL Simple Object wizards in Visual Studio.
1

Create the ATL Module

Every ATL server has exactly one CAtlDllModuleT or CAtlExeModuleT instance. For a DLL server:
// Module.cpp
#include <atlbase.h>
#include <atlcom.h>

class CMyServerModule : public CAtlDllModuleT<CMyServerModule> {
public:
    DECLARE_LIBID(LIBID_MyServerLib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYSERVER, "{YOUR-APPID-GUID}")
};

CMyServerModule _AtlModule;

// Standard DLL COM entry points
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) {
    return _AtlModule.DllMain(dwReason, lpReserved);
}
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
    return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}
STDAPI DllCanUnloadNow() {
    return _AtlModule.DllCanUnloadNow();
}
STDAPI DllRegisterServer() {
    return _AtlModule.DllRegisterServer();
}
STDAPI DllUnregisterServer() {
    return _AtlModule.DllUnregisterServer();
}
2

Implement the COM Object

// MyGreeter.h
#include <atlbase.h>
#include <atlcom.h>

class ATL_NO_VTABLE CMyGreeter :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CMyGreeter, &CLSID_MyGreeter>,
    public IMyGreeter  // Your custom interface from the IDL
{
public:
    CMyGreeter() {}

    DECLARE_REGISTRY_RESOURCEID(IDR_MYGREETER)

    BEGIN_COM_MAP(CMyGreeter)
        COM_INTERFACE_ENTRY(IMyGreeter)
    END_COM_MAP()

    // IMyGreeter::Greet
    STDMETHODIMP Greet(BSTR name, BSTR* pResult) {
        if (!pResult) return E_POINTER;
        CComBSTR greeting(L"Hello, ");
        greeting.Append(name);
        greeting.Append(L"!");
        *pResult = greeting.Detach(); // Transfer ownership to caller
        return S_OK;
    }
};

OBJECT_ENTRY_AUTO(CLSID_MyGreeter, CMyGreeter)
3

Use the COM Object with CComPtr

// Client code (could be in another process or the same DLL)
#include <atlbase.h>
#include <atlcom.h>

void UseGreeter() {
    CoInitialize(nullptr);

    CComPtr<IMyGreeter> pGreeter;
    HRESULT hr = pGreeter.CoCreateInstance(CLSID_MyGreeter);

    if (SUCCEEDED(hr)) {
        CComBSTR name(L"World");
        CComBSTR result;
        hr = pGreeter->Greet(name, &result);
        if (SUCCEEDED(hr)) {
            wprintf(L"%s\n", (BSTR)result);
        }
    }

    CoUninitialize();
}

Threading Models

ATL supports all COM apartment threading models through template parameters:
ClassThreading ModelUse Case
CComSingleThreadModelSingle-threaded apartment (STA)UI objects, legacy COM
CComMultiThreadModelMulti-threaded apartment (MTA)Background services, thread pools
CComMultiThreadModelNoCSMTA without critical sectionsPerformance-critical inner objects
// STA object (safe from one thread at a time)
class CMySTAObject :
    public CComObjectRootEx<CComSingleThreadModel>, ...

// MTA object (safe from multiple threads simultaneously)
class CMyMTAObject :
    public CComObjectRootEx<CComMultiThreadModel>, ...

Build docs developers (and LLMs) love