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 { ... } ... };
😌
Next: “Declaring COM interfaces, revisited: C# implementation”
Yeah, it is quite unfortunate that while C++ has the ability to provide a nice C# like experience, see C++ Builder, Microsoft teams insist in forcing us into this kind of boilerplate (C++/WinRT hardly improved it, on the contrary with the primitive tooling for IDL files).
Given that C# has COM integration, that’s actually a good idea.
Last I checked that was literally just marking the class as
[ComVisible(true)]
albeit with some gotchas as far as what should and shouldn’t be done. That said… there are lots of ways for the developer to control what is and isn’t exported, and how all that happens.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...