The macros for declaring COM interfaces, revisited: C++ implementation

Raymond Chen

Raymond

Last time, we looked at the macros for declaring COM interfaces and how they expand when compiled for C++. Now we’ll look at the macros you use for implementing the interface.

class Class : public ISomething
{
public:
    // *** IUnknown ***
    IFACEMETHOD(QueryInterface)(REFIID riid, void** ppv);
    IFACEMETHOD_(ULONG, AddRef)();
    IFACEMETHOD_(ULONG, Release)();

    // *** ISomething ***
    IFACEMETHOD(Method1)();
    IFACEMETHOD_(int, Method2)();
    IFACEMETHOD(Method3)(int iParameter);
};

The IFACEMETHOD and IFACEMETHOD_ macros are used for declaring that your class implements a particular method. The result of the macro expansion is this:

class Class : public ISomething
{
public:
    // *** IUnknown ***
    /* IFACEMETHOD(QueryInterface)(REFIID riid, void** ppv); */
    __override virtual __declspec(nothrow)
    HRESULT QueryInterface(REFIID riid, void** ppv);

    /* IFACEMETHOD_(ULONG, AddRef)(); */
    __override virtual __declspec(nothrow)
    ULONG AddRef();

    /* IFACEMETHOD_(ULONG, Release)(); */
    __override virtual __declspec(nothrow)
    ULONG Release();

    // *** ISomething ***
    /* IFACEMETHOD(Method1)(); */
    __override virtual __declspec(nothrow)
    HRESULT Method1();

    /* IFACEMETHOD_(int, Method2)(); */
    __override virtual __declspec(nothrow)
    int Method2();

    /* IFACEMETHOD(Method3)(int iParameter); */
    __override virtual __declspec(nothrow)
    HRESULT Method3(int iParameter);
};

Analogous with the STDMETHOD and STDMETHOD_ macros, you use the underscore version of the IFACEMETHOD macro (IFACEMETHOD_) if the return value is not HRESULT.

The __override annotation is understood by static analysis tools like PREfast. The annotation means that the static analysis tool should verify that function declaration overrides an identical method in the base class.

The __override annotation was introduced as part of SAL, the Microsoft Standard Annotation Language. It has been made redundant by C++11’s override keyword, but the macros still generate them just the same.

In practice, therefore, you are probably going to write

    IFACEMETHOD(Method1)() override;

to inform both the static analysis tool and the C++ compiler of your intention to override a method from the base class.

When it comes time to define the method, you do it with the IFACEMETHODIMP macro:

IFACEMETHODIMP Class::Method1()
{
    ...
}

Use IFACEMETHODIMP_(T) if the return type is not HRESULT.

The above expands to

__override HRESULT __stdcall Class::Method1()
{
    ...
}

If you want to implement the method inline, then you can put the implementation directly after the declaration inside the class definition.

class Class : public ISomething
{
public:
    ...
    IFACEMETHOD(Method1)() override { ... }
    ...
};

6 comments

Leave a comment

  • Avatar
    c_linkage

    Examples like the one above showing how C++ implemented interfaces (especially the part about inline declaration of function bodies) was what I found to be confusing about COM back when I had only a year of C++ experience back in the late 90s.

    The COM specification and the Don Box book go to great lengths talking about how interfaces were separated from implementation. The fact that the reference count was attached to the interface and not the implementation was repeatedly hammered into the reader, especially during the parts about being careful not to leak references.

    But in my mind the C++ examples blended the implementation and interface because the C++ object instance was both the interface and the implementation. After all, how could the interface IUnknown have a reference count if there wasn’t an object instance to hold that reference count?

    Coming from a C background (and assembly before that), the C implementation of COM just ended up making more sense to me. I could see where the actual functions lived, and I could see that the interface “vtable” was just a struct with function pointers. I could even see how the interface could be separated from the implementation via tiny structs with interface pointers and reference counts. And that really made understanding how tear-off interfaces could be implemented, and that was cool!