Click here to Skip to main content
15,860,972 members
Articles / Desktop Programming / MFC
Article

Component Category Manager wrapper classes

Rate me:
Please Sign up or sign in to vote.
4.20/5 (2 votes)
24 Feb 2000CPOL 96.2K   1.6K   42   9
COM objects can be categorised using the Component Category Manager. The code here makes it easier to use these categories in your code.
  • Download source files - 38 Kb
  • Download sample application - 113 Kb
  • 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().

    See the article on Len's homepage for the latest updates.

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


    Written By
    Software Developer (Senior) JetByte Limited
    United Kingdom United Kingdom
    Len has been programming for over 30 years, having first started with a Sinclair ZX-80. Now he runs his own consulting company, JetByte Limited and has a technical blog here.

    JetByte provides contract programming and consultancy services. We can provide experience in COM, Corba, C++, Windows NT and UNIX. Our speciality is the design and implementation of systems but we are happy to work with you throughout the entire project life-cycle. We are happy to quote for fixed price work, or, if required, can work for an hourly rate.

    We are based in London, England, but, thanks to the Internet, we can work 'virtually' anywhere...

    Please note that many of the articles here may have updated code available on Len's blog and that the IOCP socket server framework is also available in a licensed, much improved and fully supported version, see here for details.

    Comments and Discussions

     
    GeneralCustom Component Categories Creation Pin
    Anonymous17-Jul-02 11:23
    Anonymous17-Jul-02 11:23 
    GeneralList of Component Categories Pin
    Zac Howland17-Apr-02 5:24
    Zac Howland17-Apr-02 5:24 
    GeneralRe: List of Component Categories Pin
    Len Holgate17-Apr-02 5:35
    Len Holgate17-Apr-02 5:35 
    GeneralRe: List of Component Categories Pin
    Zac Howland17-Apr-02 6:22
    Zac Howland17-Apr-02 6:22 
    GeneralRe: List of Component Categories Pin
    Len Holgate17-Apr-02 6:33
    Len Holgate17-Apr-02 6:33 
    GeneralRe: List of Component Categories Pin
    Zac Howland17-Apr-02 7:57
    Zac Howland17-Apr-02 7:57 
    GeneralRe: List of Component Categories Pin
    Len Holgate17-Apr-02 8:41
    Len Holgate17-Apr-02 8:41 
    QuestionCategories on Win98? Pin
    moliate5-Oct-01 20:07
    moliate5-Oct-01 20:07 
    I would like to use this technique in an App required to function under
    NT and Win9x. However, browsing MSDN it seems like the interfaces are
    supported only by NT5. Is there another way to find components supporting
    a specific interface?

    I am rather new to COM, so any help would be appreciated.
    AnswerRe: Categories on Win98? Pin
    cagey14-May-02 7:13
    cagey14-May-02 7:13 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.