COM Collection Template Class

    I spend a lot of my programming time
writing small COM Objects with the Active Template Library, and
then using them inside of Visual Basic.  One situation that
I come across often is: 1) Having collections of objects, and 2)
Enumerating these collections.  I initially dealt with the
collection by using the VB built-in Collection class, and with
Enumeration by wrapping all the requested data into a SAFEARRAY
and stuffing that into a Variant.  Both of these solution
felt very clumsy, so I decided to try to create a template class
that would handle both the collection duties for any set of COM
Objects, and provide a more efficient way to enumerate them,
which meant implementing the IEnumVARIANT interface.

    I decided to have my CComCollection Class
store its Object pointers inside of a vector.  To ensure
proper reference counting, I felt that the vector should actually
store SmartPointers.  So if you had a class, CWidget, which
implemented IWidget, and wanted to have a collection of them they
would be stored:


vector< CComPtr<IWidget> >

So at this point the Class declaration looks like:


template<T>
class CComCollection
{
typedef vector< CComPtr<T> > COLLECTION_VECTOR;
typedef COLLECTION_VECTOR::iterator COLLECTION_ITERATOR;
}

Next, I added the typical Collection methods and property:
Add, Item, Remove, Count, and the devilish _NewEnum.  The
first four are fairly trivial, but the fourth one isn’t. 
(At least not to the novice…)  Let’s examine how the
_NewEnum property is used. You’ve created your IWidget Object and
are using it inside VB; now you want a Collection of them:
Widgets.  The typical syntax is:


For Each Widget In Widgets
‘Do some work with each Widget
Next

    Under the hood, VB is doing some
interesting stuff, hence the property _NewEnum. (Which by the
way, requires its own special id in the IDL file:
DISPID_NEWENUM)  Visual Basic will do a get for the _NewEnum
property on IWidgets.   It expects to be returned an
IUnknown pointer.  It will the do a QueryInterface on the
IUnknown pointer for IEnumVARIANT.  At this point, we need
to implement the IEnumVARIANT interface:  Next, Skip, Reset
and Clone. 

    Lucky for us, we already have a tidy
collection in our vector of precisely the objects that VB is
looking for, in this case IWidget.  So we return an IUnknown
pointer to our CSmartCollection, and prepare to deal with
incoming IEnumVARIANT calls.  The implementation is fairly
straight forward once the context of when and why you’re being
called is understood.

    So how does one use this Template
class?  After you’ve decided what interface you want to be a
collection of:, IBars might be a collection of IBar, or IFoos a
collection of IFoo, you need to tell the world, via your
TypeLibrary via your IDL file, what interfaces and methods you
support.  For instance, when the user wants to call the Add
method, they know that you specifically expect them to pass an
object of type IWidget.  So in order for the IDL file to
reflect this, I created a handy little expansion macro to
simplify things (Don’t forget to #include the IComCollection.idl
file).  Here’s an example of IWidgets:


interface IWidgets : IDispatch
{
DECLARE_COM_COLLECTION(IWidget)
}

That’s it.  (Of course, you need to specify your object
with the UUID and add it to your Library, but if you’ve any
experience with IDL that won’t be a problem.)  Now the only
thing left to do is deal with the header file for CWidgets. 
After inserting a simple ATL Object into your app, you’ll notice
all the template classes that you inherit from.  I reduced
this down to just the CComCollection class in order to prevent
some nasty circular inheritance.  After that, you need to
add the IEnumVARIANT with a COM_INTERFACE_ENTRY macro, so you’ll
support a query for the IEnumVARIANT interface, and that’s it.
  No cluttering of your header file, no implementation left
to work out.  Your entire collection class of ATL Objects is
complete.

    Included is a Demo project that includes an
IWord class with one property: Text, and its collection class:
IWords.  And since this was inspired by a VB situation, I
included a VB project which demos the collection.

This project was developed on a Win98 machine using VC6 and
Vb6. However, aside from some extraneous warnings, it should be
compatible with VC5 and VB5.

Download demo Vb project -13
KB
— (If you just run the demo, make sure you register the
CollectionTest.dll first)

Download VC source – 17
KB

Download just CComCollection
Template class files – 3 KB

    As an exercise for the casual reader (or
CodeGuru), you might explain to the author some better techniques
for building template classes.  Also, the Clone method needs
a bit of help. 

More by Author

Must Read