Introduction
It may be ridiculous to imagine that someone is still targeting COM, let alone writing an article on it. That's right, COM: Component Object Model, a Microsoft technology from the nineties that served as a first solid way to segregate software development tasks, allowing them to be distributed among teams that might not even be aware of each other's existence. Yet I would still like to share some of my experiences, even though I realize that the times when COM was at the height of its fame have irrevocably perished and the technology itself has given up the throne to more powerful players.
I develop a little utility named KO Approach whose main purpose is enhancing the Windows Shell. The main way to interact with the Shell is through the COM interfaces that it provides. So the subject of this article has been largely dictated by architectural conditions.
If you have tried to implement a COM interface by hand, you know how awkward it is. What drives me most crazy is the need to mention the interface twice:
- When specifying it as a class parent:
class TestClass :
public IClassFactory,
public ILog,
public IBindCtx,
public IAccessControl
{
...
};
- When implementing the
QueryInterface
method:
if (theIID == IID_IClassFactory)
* theOut = static_cast<IClassFactory *>(this);
else if (theIID == IID_ILog)
* theOut = static_cast<ILog *>(this);
else if (theIID == IID_IBindCtx)
* theOut = static_cast<IBindCtx *>(this);
else if (theIID == IID_IAccessControl)
* theOut = static_cast<IAccessControl *>(this);
else if (theIID == IID_IUnknown)
* theOut = static_cast<IUnknown *>( static_cast<IClassFactory *>(this) );
...
Another element to bring frustration is reference-counting. Every time you implement reference-counting, you need to repeat the exact same code, such as the following:
class TestClass
{
LONG myRefs;
public:
TestClass() : myRefs(0L) { }
};
Luckily, with little effort we can compact the code and re-use common functionality. Of course, you could use ATL, but in this article I wanted to introduce a minimalistic yet elegant pattern. Our code will be even more compact, and in addition we will get familiarized with the some cutting-edge features of the newest C++0x standard.
Prerequisites
- Necessary: A C++ compiler that supports variadic templates. In this article, we will use GCC 4.6.
- Optional: A Development Environment that can highlight text and visually build your project. For this article, I've chosen NetBeans because it provides a satisfactory level of integration with GCC.
- Optional: doxygen to generate HTML documentation.
Step-by-Step Pattern Explanation
Basic Building Block
First, let's introduce a class that provides COM interface discovery information. That is, it provides a function block to inform the caller about a single interface that the class supports:
template<typename tInterface, LPCGUID tIID = &__uuidof(tInterface)>
class ComEntryDiscoverable
{
public:
typedef tInterface Interface;
protected:
template<typename tObject>
static HRESULT InternalQueryInterface(REFIID theIID, tObject * theObj, void ** theOut)
{
if (theIID == *tIID)
*theOut = static_cast<tInterface *>(theObj);
else
return E_NOINTERFACE;
return S_OK;
}
}
Second, let's write a small class that actually implements a COM interface:
template<typename tInterface, LPCGUID tIID = &__uuidof(tInterface)>
class ComEntry :
public tInterface, public ComEntryDiscoverable<tInterface, tIID>
{ };
Why are we using templates? Because we want our code to run as fast as possible. To achieve that, our compiler will perform instantiation based on the template arguments that we will provide when writing a real class. Plus, the compiler may inline the call to InternalQueryInterface
so that our object-oriented code will produce literally the same instructions as hand-crafted code does.
As you can see, the two classes use the same template arguments:
- The type of the interface that is being implemented.
- The GUID of this interface. Because we are using GCC that does not support the Microsoft
__uuidof
operator, we will have to use some tricks as a workaround which I'll explain at the end.
IUnknown Discovery
What we are currently missing is discovery information about common parent interface – IUnknown
. If a class implements at least one COM interface, it implicitly implements IUnknown
and hence its QueryInterface
method must return a correct pointer when supplied IID_IUnknown
as the interface GUID. Let's add an auxiliary class to aid IUnknown
discovery:
template<typename tInterface,
typename tIntermediate, LPCGUID tIID = &__uuidof(tInterface)>
class ComEntry2StepDiscoverable
{
public:
typedef tInterface Interface;
protected:
template<typename tObject>
static HRESULT InternalQueryInterface(REFIID theIID, tObject * theObj, void ** theOut)
{
if (theIID == *tIID)
*theOut = static_cast<tInterface *>( static_cast<tIntermediate *>(theObj) );
else
return E_NOINTERFACE;
return S_OK;
}
};
Its only difference from ComEntryDiscoverable
is the way its InternalQueryInterface
works: the latter performs two casts, one to the intermediate type (denoted by the tIntermediate
template argument) and the other to the final type. The intermediate type can be any interface that has exactly one tInterface
in its inheritance chain, otherwise the compiler will complain on the ambiguous outer cast. Now let's embrace IUnknown
:
template <typename tIntermediate>
class ComEntryTerminator :
public ComEntry2StepDiscoverable<IUnknown, tIntermediate>
{ };
We have called this class ComEntryTerminator
because it will be the last entry in the COM interface discovery sequence…
Implementing Multiple Interfaces
"Wait a minute, what sequence?" So far we have only dealt with a rather crippled solution where we can implement one COM interface, plus provide discovery information about this interface and, mostly for the sake of code purity, its parent IUnknown
. So let's move on and introduce the following class:
template <typename tEntry1,
typename tEntry2 = ComEntryTerminator<typename tEntry1::Interface> >
class ComEntryCompound : public tEntry1, public tEntry2
{
public:
template<typename tObject>
static HRESULT InternalQueryInterface(REFIID theIID, tObject * theObj, void ** theOut)
{
HRESULT aRes = tEntry1::InternalQueryInterface(theIID, theObj, theOut);
if ( FAILED(aRes) )
aRes = tEntry2::InternalQueryInterface(theIID, theObj, theOut);
return aRes;
}
};
The ComEntryCompound
class enables us to have a sequence of two interface discovery blocks, denoted by tEntry1
and tEntry2
template arguments. Should we wish to implement one COM interface, the code will look as follows:
class TestClass : public ComEntryCompound< ComEntry<IClassFactory> >
{ };
But the real power comes when ComEntryCompound
is used recursively. So for two interfaces, we can write something like this:
class TestClass2 :
public ComEntryCompound
<
ComEntry<IClassFactory>,
ComEntryCompound< ComEntry<ILog> >
>
{ };
To make our code look prettier, we are now going to grasp variadic templates, a new and for many, a long awaited feature of C++. Briefly, variadic templates are templates that can have an arbitrary number of arguments, analogously to how the printf
function can have an arbitrary number of parameters. But the beauty of variadic templates is in the way they are implemented. While printf
needs a runtime that will examine the stack and retrieve the actual number of arguments, C++ variadic templates are processed at compile time. Hence safer, faster, less error prone code!
But first let's introduce a helper class:
template <typename tInterface1>
class ComEntry1 :
public ComEntryCompound < ComEntry<tInterface1> >
{ };
Its only template argument is the type of the interface that can be implemented, such as:
class TestClass : public ComEntry1<IClassFactory>
{ };
With the help of ComEntry1
we can finally write our variadic template versions. If you aren't familiar with C++0x, the syntax may at first seem a little weird, so I'll try to explain it step by step. First we declare a variadic template class:
template<typename ... tInterfaces>
class ComEntryV;
Because we don't know exactly the number of template arguments and yet we need to put some meaningful implementation into the class, we provide another template that will be used recursively, as many times as dictated by the code that will instantiate the template. To achieve this, we divide the variable-length list of template arguments into two parts: the head consisting of exactly one argument, and a variable-length tail.
template<typename tInterface, typename ... tTail>
class ComEntryV<tInterface, tTail...> :
public ComEntryCompound
<
ComEntry<tInterface>,
ComEntryV<tTail...>
>
{ };
As you can see, ComEntryV
implicitly derives from its own specialization whose number of template arguments is reduced by one. The argument in question is used to implement a COM interface and provide its discovery information. The only thing that we need is a class specialization that will terminate the recursion. Since it implements only one interface, we will utilize the helper ComEntry1
class introduced earlier:
template <typename tInterface>
class ComEntryV<tInterface> :
public ComEntry1<tInterface>
{ };
With ComEntryV
in place, we can write real pretty code for as many interfaces as we need:
class TestClass :
public ComEntryV<IClassFactory, ILog, IBindCtx, IAccessControl>
{
};
Instantiation
As you can see from the previous listing, our classes do not implement IUnkonwn
, even though they declare support for it via interface discovery. As a result, our TestClass
cannot be instantiated directly. Instead, we will introduce another template that will take care of instantiation via the following functionality:
- Providing a common mechanism for reference counting
- Providing a means to invoke an arbitrary constructor of the class being instantiated
- Implementing
IUnknown
First, let's introduce a class to be responsible for reference counting:
template<bool tThreadSafe = false>
struct ComRefCount
{
LONG myNumRefs;
ComRefCount() : myNumRefs(1L) { }
ULONG STDMETHODCALLTYPE AddRef()
{
if (!tThreadSafe)
return ++myNumRefs;
else
return InterlockedIncrement(&myNumRefs);
}
template<typename tObj>
ULONG STDMETHODCALLTYPE Release(tObj * theObj)
{
ULONG aNumRefs;
if (!tThreadSafe)
aNumRefs = --myNumRefs;
else
aNumRefs = InterlockedDecrement(&myNumRefs);
if (aNumRefs <= 0L)
delete theObj;
return aNumRefs;
}
};
Depending on the threading model, this class can be either thread safe or thread unsafe. Please note that the number of references is automatically set to one because there is at least one reference to the object, held by the caller where the object was created. Now, let's complete the instantiation pattern:
template<typename tImpl, bool tThreadSafe = false>
class ComInstance : public tImpl
{
protected:
ComRefCount<tThreadSafe> myRefs;
public:
ComInstance()
{ }
template<typename ... tParams>
ComInstance(tParams && ... theParams)
: tImpl( std::forward<tParams>(theParams)... )
{ }
ULONG STDMETHODCALLTYPE AddRef()
{ return myRefs.AddRef(); }
ULONG STDMETHODCALLTYPE Release()
{ return myRefs.Release(this); }
STDMETHODIMP QueryInterface(REFIID theIID, void ** theOut)
{
HRESULT aRes = tImpl::InternalQueryInterface(theIID, this, theOut);
if ( SUCCEEDED(aRes) )
AddRef();
return aRes;
}
};
In addition to the thread safety argument already discussed, this class expects the tImpl
template argument identifying the type that contains real functionality. This is the class that implements COM interfaces, such as the TestClass
mentioned in the previous listings.
Here we see the variadic template constructor that allows us to automatically invoke any constructor contained in the implementation class, including the so called move constructor introduced in C++0x.
This is it. Let's just see how instances can be created:
ComInstance<TestClass> aStackObj( );
TestClass * aHeapObj = new ComInstance<TestClass>( );
auto aHeapObj1 = new ComInstance<TestClass>( );
More Complex Interface Hierarchies
It is not rare for COM to have certain interface derived for something other than IUnknown
. A typical use case is the so called interface versioning where new functionality is contained in an interface whose name is suffixed with a version number. The interface itself would derive from its previous version. The longest chain I have found so far is ITaskbarList
– ITaskbarList2
– ITaskbarList3
– ITaskbarList4
. By the way, it is a good way to trace how the Windows Shell was progressing. But for the sake of brevity, let's deal with some home-made interfaces, rather than these bulky ones:
class IInterface : public IUnknown
{
public: STDMETHOD (InterfaceMethod) () = 0;
};
class IInterface2 : public IInterface
{
public: STDMETHOD (InterfaceMethod2) () = 0;
};
class IInterface3 : public IInterface2
{
public: STDMETHOD (InterfaceMethod3) () = 0;
};
class IInterface4 : public IInterface3
{
public: STDMETHOD (InterfaceMethod4) () = 0;
};
class Impl : public ComEntryV<IInterface4>
{
protected:
STDMETHODIMP InterfaceMethod ()
{ cout << "InterfaceMethod" << endl; }
STDMETHODIMP InterfaceMethod2 ()
{ cout << "InterfaceMethod2" << endl; }
STDMETHODIMP InterfaceMethod3 ()
{ cout << "InterfaceMethod3" << endl; }
STDMETHODIMP InterfaceMethod4 ()
{ cout << "InterfaceMethod4" << endl; }
};
For a class deriving from IInterface4
a hand-crafted QueryInterface
implementation would declare support for all parent interfaces, as well. But since we want to avoid having to explicitly write the method, we should address the issue in a different way.
There can be several ways to assure that. First, we can provide a partially hand-written implementation for theInternalQueryInterface
method:
static HRESULT InternalQueryInterface(
REFIID theIID, Impl * theObj, void ** theOut)
{
HRESULT aRes = ComEntryV<IInterface4>::InternalQueryInterface(
theIID, theObj, theOut);
if ( FAILED(aRes) )
{
aRes = S_OK;
if (theIID == IID_IInterface)
*theOut = static_cast<IInterface *>(theObj);
else if (theIID == IID_IInterface2)
*theOut = static_cast<IInterface2 *>(theObj);
else if (theIID == IID_IInterface3)
*theOut = static_cast<IInterface3 *>(theObj);
else
aRes = E_NOINTERFACE;
}
return aRes;
}
But the actual benefit of our infrastructure has been pretty much neglected. After all, why bother with all these classes if all we need now is adding similar hand-written blocks for two more interfaces: our top-level IInterface4
and IUnknown
?
This is why we have another, a lot more elegant solution where we won't even have to sneak in the interface discovery process. The elegance again comes from C++0x and template specializations. If we analyze the inheritance chain the Impl
class, we'll notice that it essentially derives from ComEntry<IInterface4>
. What if we could somehow give compiler a hint about the additional interfaces that ComEntry<IInterface4>
supports? Yes we can.
template<>
class ComEntry<IInterface4> :
public ComEntryWithParentDiscoveryV
<IInterface4, IInterface3, IInterface2, IInterface>
{ };
The above listing is a template specialization that will do the job for us. With this specialization in place, our Impl
class will have a modified logic of its interface discovery routine, simply because we have explicitly told the compiler how to do the override. No need to change a single line of code for our Impl
class! All we need is to see what the ComEntryWithParentDiscoveryV
class is all about. We will use already familiar concepts of the C++ variadic templates so that an arbitrary number of parent interfaces could be auto-discovered. First, let's define the logic for the case of one parent interface. This is very similar to discovery of IUnknown
. The only difference is that we now discover two interfaces: the one being implemented and its parent:
template<typename tInterface, typename tParent>
class ComEntryWithParentDiscovery1 :
public tInterface,
public ComEntryCompound
<
ComEntryDiscoverable<tInterface>,
ComEntry2StepDiscoverable<tParent, tInterface>
>
{
public:
typedef tInterface Interface;
};
Now let's define the variadic template versions:
template <typename tChild, typename ... tParents>
class ComEntryWithParentDiscoveryV;
template <typename tChild, typename tParent, typename ... tTail>
class ComEntryWithParentDiscoveryV<tChild, tParent, tTail...> :
public ComEntryCompound
<
ComEntry2StepDiscoverable<tParent, tChild>,
ComEntryWithParentDiscoveryV<tChild, tTail...>
>
{
public:
typedef tChild Interface;
};
template <typename tChild, typename tParent>
class ComEntryWithParentDiscoveryV<tChild, tParent> :
public ComEntryWithParentDiscovery1<tChild, tParent>
{ };
Again, the concept must be already familiar. First we define that the class can have an arbitrary number of parent interfaces for which we want to add discovery information. Second, we pick one class off the variable-length list of parent interfaces and define the actual discovery information logic in a recursive manner. And finally, we terminate the recursion with a class that only has one immediate parent.
The __uuidof Operator
In our build environment (GCC), special tricks need to be performed in order to emulate the __uuidof
operator. A series of preprocessor macros illustrates the approach:
#define DEFINE_UUIDOF_ID(Q, IID) template<> GUID hold_uuidof<Q>::__IID = IID;
#define DEFINE_UUIDOF (Q) DEFINE_UUIDOF_ID(Q, IID_##Q)
#define __uuidof (Q) hold_uuidof<Q>::__IID
These macros rely on the fact that most of the interfaces not only declare their GUID as an attribute, but also specify an explicit GUID constant, named IID_InterfaceNameGoesHere
. However, the developer will still need to map the interfaces used in a project to their GUIDs by placing these macros somewhere in a .CPP file:
DEFINE_UUIDOF(IUnknown)
DEFINE_UUIDOF(IClassFactory)
DEFINE_UUIDOF(ILog)
DEFINE_UUIDOF(IBindCtx)
DEFINE_UUIDOF(IAccessControl)
Sample Application
The sample provided is a basic console application that uses the Microsoft XML parser to extract the version number of the before mentioned KO Approach from a PAD file hosted on the KO Software website. PAD files are XML files of standardized structure that allow software sites to extract various information about a software product.
We will be using SAX for parsing. Firstly, because it's simple. But the most important reason is that with SAX we will have to implement a COM interface without the need to have a full-fledged COM server. Just in case you don't know, SAX is an event passed paradigm of XML parsing. As the parser detects various entities in the XML document, it raises events passing the data about found entities. It is up to the developer how these entities will be further processed. For this task, we will wait for the inner text inside a predefined XML element identifying the product version. Here's the principal code:
int main(int argc, char** argv)
{
CoInitialize(NULL);
ISAXXMLReader * aRdr = 0;
HRESULT aRes = CoCreateInstance
(
CLSID_SAXXMLReader30,
NULL,
CLSCTX_ALL,
IID_ISAXXMLReader,
(void **)&aRdr
);
if ( SUCCEEDED(aRes) && aRdr != 0)
{
ComInstance<XmlHandler> aH;
aRes = aRdr->putContentHandler(&aH);
if ( SUCCEEDED(aRes) )
{
aRes = aRdr->parseURL(L"http://www.ko-sw.com/pad/approach.xml");
if ( SUCCEEDED(aRes) )
wcout << L"The current version of KO Approach is "
<< aH.GetParsedCurrentVersion() << endl;
else
wcerr << L"Unable to parse the URL: " << aRes << endl;
}
else
wcerr << L"Unable to set the SAX content handler: " << aRes << endl;
if (aRdr != 0)
aRdr->Release();
}
else
wcerr << L"Unable to create XML reader: " << aRes << endl;
CoUninitialize();
return 0;
}
The class responsible for parsing is XmlHandler
. As it receives events, it accumulates information about the product version that we are interested in:
class XmlHandler : public ComEntryV<ISAXContentHandler>
{
private:
std::vector<std::wstring> myStack;
std::wstring myCurVersion;
public:
const std::wstring & GetParsedCurrentVersion() const
{ return myCurVersion; }
protected:
STDMETHODIMP startElement (LPCWSTR theLocalName, int theNumCharsLocalName)
{
std::wstring aName(theLocalName, theLocalName + theNumCharsLocalName);
myStack.push_back(aName);
return S_OK;
}
STDMETHODIMP endElement (LPCWSTR theLocalName, int theNumCharsLocalName)
{
std::wstring aName(theLocalName, theLocalName + theNumCharsLocalName);
if (myStack.back() != aName)
return E_FAIL;
myStack.pop_back();
return S_OK;
}
STDMETHODIMP characters (LPCWSTR theString, int theNumChars)
{
if (myStack.size() == 3)
if ( myStack[0] == L"XML_DIZ_INFO" &&
myStack[1] == L"Program_Info" &&
myStack[2] == L"Program_Version" )
{
myCurVersion += std::wstring(theString, theString + theNumChars);
}
return S_OK;
}
};
Here's what the app prints at the time of writing this article:
Conclusion
The concepts described here are an illustration of how a language feature that may seem highly theoretical, experimental, meaningful to C++ purists only, can help in a practical task of implementing an arbitrary number of COM interfaces with less code and yet very little (if no) performance overhead. These concepts are in no way a replacement for professional solutions for COM development, such as ATL. The latter has some really advanced features that many COM developers use, such as tear-off entries, class factory utilities, IDispatch
support, and many more.
It is also highly unlikely that the build environment chosen for this article will be used in an ATL project. For Visual Studio however, the code will still compile, with limitations caused by the former's limited support for C++0x:
- Instead of variadic-template
ComEntryV
, fixed-length template versions must be used: ComEntry1
.. ComEntry5
, with support for up to five interfaces. Implementing the concept for six and more interfaces is a matter of simple copy-paste. - Instead of variadic-template
ComEntryWithParentDiscoveryV
, fixed-length template versions must be used: ComEntryWithParentDiscovery1
.. ComEntryWithParentDiscovery5
, with support for up to five parent interfaces. Implementing the concept for six and more parent interfaces is a matter of simple copy-paste. - Instead of variadic-template constructors, the
ComInstance
class will have fixed-length versions. Up to five arguments are supported. Adding more arguments is even simpler than above.
I hope that with newer releases of Visual Studio 2010, support for C++0x will be more consistent and developers will be able to utilize the code in a more convenient build environment also.
History
- 2011-09-03: Initial release
- 2011-09-04: Minor code listing and text corrections