Click here to Skip to main content
Click here to Skip to main content

Component Category Manager wrapper classes

By , 24 Feb 2000
 
  • 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)

    About the Author

    Len Holgate
    Software Developer (Senior) JetByte Limited
    United Kingdom United Kingdom
    Member
    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.

    Sign Up to vote   Poor Excellent
    Add a reason or comment to your vote: x
    Votes of 3 or less require a comment

    Comments and Discussions

     
    You must Sign In to use this message board.
    Search this forum  
        Spacing  Noise  Layout  Per page   
    GeneralCustom Component Categories CreationsussAnonymous17 Jul '02 - 11:23 
    I am a little confused.
     
    Is the only way to create a custom Component Category is to use ICatRegister or can I use a registry script (.RGS)?
     

    GeneralList of Component CategoriesmemberZac Howland17 Apr '02 - 5:24 
    The list of the CATIDs is stored in the following location (in the registry).
     
    HKEY_CLASSES_ROOT\Component Categories
     
    Zac
    GeneralRe: List of Component CategoriesmemberLen Holgate17 Apr '02 - 5:35 
    I know, and your point is?
    GeneralRe: List of Component CategoriesmemberZac Howland17 Apr '02 - 6:22 
    >>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.
     
    It was in response to this paragraph.
     
    Zac
    GeneralRe: List of Component CategoriesmemberLen Holgate17 Apr '02 - 6:33 
    I thought it might be.
     
    Notice that although the categories are listed under the registry key that you cited the objects belonging to each category are not listed under the category (if you see what I mean).
     
    So, for example, although you can find the category "Desktop Bands" at HKEY_CLASSES_ROOT\Component Categories\{00021492-0000-0000-C000-000000000046} you cant then look under that key to find all the components that implement the "Desktop Bands" category, you have to check each component under HKEY_CLASSES_ROOT\CLSID to see if it has an "Implemented categories" subkey that has a {00021492-0000-0000-C000-000000000046} subkey...
     
    This seems to me to be an inefficient method of locating objects that are a member of a category....
     
    Len
    GeneralRe: List of Component CategoriesmemberZac Howland17 Apr '02 - 7:57 
    Actually, it is very efficient. The registry is a relational database, and thus, allows you to (assuming you have a list of the categories your application supports) quickly obtain all the components installed on your system that support a given interface. If you want to be even more effiecent (at least in reporting errors back), you can see if the category exists in the registry first (if there is not category, there is no component in that category installed).
     
    I would imagine that the Component Category Manager does something similar. You give it a CATID, and it quickly gives you all the components that claim to support it.
     
    Zac
    GeneralRe: List of Component CategoriesmemberLen Holgate17 Apr '02 - 8:41 
    Hmm, if you say so. It's been a long time since I've used the Component Category Manager but my last tests showed that it was considerably slower to retrieve a list of components requring a single category than it was to iterate a list of subkeys... Of course, the problem comes when you have a component that is in several categories and requires several categories, simple iteration cant work in that case. One would assume that the optimisation that you suggest would be performed inside the com cat manager as that way the user doesn't have to know anything about how it works inside...
     
    Len
     

    QuestionCategories on Win98?membermoliate5 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?memberKevin Glannon14 May '02 - 7:13 
    While the documentation indicates that this technique is only available on NT5.0+, I've actually tried it on Win98 and NT4 SP6. On Win98, adding a category appears to work, however, enumerating that category does not. Your CATID will appear in the registry, but it will not be listed during enumeration. The ability to list a class supporting a specific interface, however, actually works even if the CATID of that interface does not appear while enumerating all CATIDs.
     
    Everything I have tried has worked as I hoped on NT4 SP6.
     
    Of course, all of these results come with a large disclaimer. This is just what I've seen on my particular systems and it may be unwise to build software using capabilities that Microsoft does not specifically say it supports.

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

    Permalink | Advertise | Privacy | Mobile
    Web03 | 2.6.130523.1 | Last Updated 25 Feb 2000
    Article Copyright 2000 by Len Holgate
    Everything else Copyright © CodeProject, 1999-2013
    Terms of Use
    Layout: fixed | fluid