COM: Component Category Manager wrapper classes

Download Source
Code and Example

The Problem

COM objects can be used to make an application easy to extend. Part of this
extensibility comes from being able to write many COM objects that all conform to an
interface. The application uses the interface and doesn’t care who implements it. The user
should be able to select the actual object that they want to do the work from a list of
objects which could be used… Luckilly the standard Component Category Manager gives you
the ability to group like COM objects together in a category and then easily manipulate
them. The problem is that it’s exposed as a series of slightly grungy interfaces…

A Solution

I like solving a problem once, wrapping up that solution into an easy to use package
and then using the package as often as is appropriate… When I started working with the
Component Category Manager I realised that there was a lot of boiler-plate code that I was
writing over and over again. I decided to wrap it all up in a class or two and make it all
a little nicer.

There are two interfaces to the Component Category Manager: the registration interface
and the information interface. The registration interface is used by COM objects during
their registration, or by setup programs to register an object as belonging to, or
requiring a particular category. The information interface is then used by the application
needing to use a category of objects. I wrote two classes to wrap these interfaces up.

CComCatRegister

CComCatRegister wraps an ICatRegister interface and provides a thin wrapper around the
standard functionality. The advantages of using the wrapper class are that for the
simplest registration requirements you can just instantiate it and call a single function
to register your object…

CComCatRegister catMgr;
catMgr.RegisterClassImplCategories(myGUID, myCATID);

If you also need to register the category, the above becomes…

CComCatRegister catMgr;
catMgr.RegisterCategory(myCATID, _T("This is a category"));
catMgr.RegisterClassImplCategories(myGUID, myCATID);

You can also register the class as belonging to multiple categories, or register
categories that the class requires, rather than implements.

That’s about all there is to using the registration category manager. Compare this to
the boiler-plate code required to initialise COM, get an ICatRegister interface, manage
its lifetime, provide the locale ID for the category descriptions, etc, etc… It’s
easier!

CComCatInformation

The second Component Category Manager interface is ICatInformation. This is used by
applications that want to discover which objects belong to which categories, which
categories an object requires, which categories it implements, etc.

As with CComCatRegister, CComCatInformation wraps the standard COM interface in a thin
wrapper. This class adds more value that CComCatRegister as the underlying interface is
more complex. Using the IEnumXXXX iterator wrappers that are explained here it neatly
wraps all of the IEnum interfaces available from ICatInformation and makes them easier to
use.

If you wanted to display a list of objects implementing a particular category then all
you need do is something like this…

CComCatInformation catMgr;

CIterateGUID start = catMgr.IterateClassesOfCategory(myCATID);
CIterateGUID end = CIterateGUID::End();

for (CIterateGUID &it = begin; it != end; ++it)
{
    LPOLESTR lpGUIDString;

    if (S_OK == StringFromIID(it, &lpGUIDString))
    {
        std::wcout << L" "<<
lpGUIDString << std::endl;
        CoTaskMemFree(lpGUIDString);
    }
}

Compare this code to that found in the IEnum sample that
doesn’t use the Component Category Manager.

Wrapping up

These wrapper classes make the Component Category Manager easier to use. It’s worth
using it as it makes your applications more easily extendable. Never tie your application
to a single instance of an object when you could, instead, make it dependant on a category
of objects that perform the task it requires. You can then allow the user to change the
actual object with some very simple code.

One thing that confuses me…

Though the Component Category Manager is, undoubtably, a Good Thing, there is one
aspect of it that confuses me. If you look in the registry, each object that is in a
category lists the category under its registry key. This makes it easy to determine if the
object is in the category. However, there appears to be no list of "objects that are
in a category" which implies that to find a list of all objects that are in a
particular category the Component Category Manager has to look at every object in the
system… This seems odd… But then, perhaps I’m missing something.

A bug in the Component Category Manager?

There appears to be a bug in implementation of IEnumCATID that is supplied with the
standard component category manager. I have version 4.71 of ComCat.dll on my machine and
calling Clone() on an IEnumCATID interface pointer which was obained from a call to either
EnumImplCategoriesOfClass() or EnumReqCategoriesOfClass() gives you a pointer which
appears to be linked to the original pointer you called Clone() on. Calling Release() on
either the cloned pointer or the original appears to invalidate the other… This is
certainly not the case with the other IEnum interfaces presented by the component category
manager.

The problem can be seen with the code below (there’s a complete test program available
for from the download page).

IEnumCATID *pIEnumCatid = 0;
hr = pICatInfo->EnumImplCategoriesOfClass(guid, &pIEnumCatid);

if (SUCCEEDED(hr))
{
   IEnumCATID *pIEnumCatidClone = 0;

   hr = pIEnumCatid->Clone(&pIEnumCatidClone);

   if (SUCCEEDED(hr))
   {
      pIEnumCatid->Release();
     
pIEnumCatidClone->Release();      // Doesn't matter which
order these are
                                       
// the second release causes an access
                                       
// violation...
   }
}

Apparantly a new version of the component category manager is available with VB6.0 the
version of ComCat.dll should be 5.0. I would be intestested to know if this bug is still
present in the latest version. Version 5.0 is also supposedly part of IE4sr1 but I have
that installed and still have 4.71

How this affects CComCatInformation

Admittedly, EnumImplCategoriesOfClass() and EnumReqCategoriesOfClass() are probably the
least used functions on the ICatInformation interface, and for most uses you wouldn’t need
to call Clone() on an interface pointer obtained from them. However, it causes problems
with my wrapper class as the iterators are returned by value and this causes the interface
pointer to be Clone()d in the copy constructor of the IEnumIterator…

If the test program fails on your machine, do not use
CComCatInformation::IterateImplCategoriesOfClass() or
CComCatInformation::IterateReqCategoriesOfClass().

So that this article and code can be kept up to date more easily I’ve provided a link
to where the article is located on my own web pages. Read the full article
and download the source code
.

More by Author

Must Read