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. 



Comments

  • Some correction

    Posted by Legacy on 05/10/2003 12:00am

    Originally posted by: Vladimir Sysoev

    I've a problem to use this code with VB debug because in watch window tree of objects do not expand correctly. I make some changes in IEnumVARIANT::Next() function to correct this problem. May be it will be usefull.
    
    

    STDMETHOD(Next)(ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
    {
    if (pCeltFetched)
    {
    *pCeltFetched = 0;
    }

    /* if (celt > 1)
    {
    return E_INVALIDARG;
    }
    */
    ULONG ul_count = 0;
    if (m_ColIterator != m_ColVector.end()){
    while ((m_ColIterator != m_ColVector.end())&&(ul_count<celt))
    {
    VariantInit(&rgVar[ul_count]);
    rgVar[ul_count].vt = VT_DISPATCH;
    T * pT = *m_ColIterator;
    pT->AddRef();
    rgVar[ul_count].pdispVal = pT;


    m_ColIterator++;
    ul_count++;
    }
    }
    else
    {
    rgVar[0].vt = VT_EMPTY;

    if (pCeltFetched)
    {
    *pCeltFetched = 0;
    }

    return S_FALSE;
    }
    if (pCeltFetched)
    *pCeltFetched = ul_count;
    return S_OK;
    }

    Reply
  • Adding Words object to a collection in constructor ?

    Posted by Legacy on 07/16/2001 12:00am

    Originally posted by: dede

    How can I add several "Word" objects in constructor if Words object ?

    Reply
  • Using CAdapt with CComPtr!?

    Posted by Legacy on 01/09/2001 12:00am

    Originally posted by: Claudiu Tomescu

    Mark,
    
    Your collection is very useful. Thanks.
    However I suggest using CAdapt class when working with STL containers and ATL smart pointer classes.

    Your typedef should be something like that:

    [ccode]
    typedef vector< CAdapt< CComPtr<T> > > COLLECTION_VECTOR;
    [/ccode]

    The rest of the code remains the same.

    Claudiu

    Reply
  • Very Very Useful ! but could you post some improvement ?

    Posted by Legacy on 09/19/2000 12:00am

    Originally posted by: BBreaker

    The code you supplied us is very very great !
    
    

    Could you post another version with small enhancement like :
    - multiple item get in Next() method
    - Clone() methode implementation
    - multiple item skipping in Skip() method

    Thanks again !

    Reply
  • One OF the Best Code

    Posted by Legacy on 02/24/2000 12:00am

    Originally posted by: Pedro Miranda

    Thank you for this tip, this is one of the best I've hever seen.
    I'm implementing using SAFEARRY (my boss doese't belive much in STL )what are the advantege of one over another.
    Tahank again.

    Reply
  • Fixes

    Posted by Legacy on 10/27/1999 12:00am

    Originally posted by: Alaric Dailey

    I have created a "map" version that doesn't do the sorting Marks class does. Also, other comments have stated that it doesn't compile with any setting other than DEBUG, by removing the _ATL_MIN_CRT define from those settings ( under preprocessor definintions ) it will then compiles correctly.

    If anyone is really interested in the non-sorting mapversion please email me.


    Alaric Dailey

    Reply
  • Limitation - 'next' only fetches one element at a time

    Posted by Legacy on 06/14/1999 12:00am

    Originally posted by: Colin Grant

    Its worth stating that he implementation currently only supports retrieving the elements one by one, whereas the semantics of 'Next' allow several elements to be fetched at once.

    Reply
  • Nice little piece of collection class

    Posted by Legacy on 03/18/1999 12:00am

    Originally posted by: Gajanan Naik

    Me and Vijay had a tough time to implent this collection class, What we were actually explicitly release the interface pointer of the class which you are adding to the collection object in VC++ 5

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • Instead of only managing projects organizations do need to manage value! "Doing the right things" and "doing things right" are the essential ingredients for successful software and systems delivery. Unfortunately, with distributed delivery spanning multiple disciplines, geographies and time zones, many organizations struggle with teams working in silos, broken lines of communication, lack of collaboration, inadequate traceability, and poor project visibility. This often results in organizations "doing the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds