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.