Title: Windows 98/2000 Component Categories
Question: How to determine which specified COM objects to use ?
Answer:
In many situations, it is necessary to enumerate the COM objects that can be used in a certain context. Further more we need information about all COM objects that was specially developed to solve one particular problem.
For example, we are in the middle of developing some COM based plug-in framework for our application. Surely this question will soon come up : 'What is the standard way to determine what plug-ins are available for a given host ?'
Everytime the host app run, it need to find out its available (compatible) plug-ins.
In Windows 98/2000 this is where Component Categories come to rescue.
In short Component Categories is windows way to group several COM objects that shared some common functionality into one category. That way client application can easily determine which COM objects are available to use. Just like interfaces, coclasses and other COM related stuff, each category is uniquely identified by a GUID. CATID (Category ID) to be precise.
Windows define two interfaces ICatRegister and ICatInformation to help developer use Component Categories service. Both ICatRegister and ICatInformation are both implemented by a COM coclass defined by CLSID_StdComponentCategoryMgr.
We use ICatRegister RegisterCategories to register one or more categories. RegisterCategories receive two paramter. First to determine how many category will be registered, the second is the array (pointer) to TCategoryInfo. TCategoryInfo is defined as follows :
TCATEGORYINFO = record
catid: TGUID; //category ID
lcid: UINT; //locale ID, for multi-language support
szDescription: array[0..127] of WideChar; //category desc
end;
To register one specified COM object (class) to one category we use ICatRegister RegisterClassImplCategories. RegisterClassImplCategories used three parameter. CLSID of the coclass that implement the category, number of category to be list to and the array of category (TCategoryInfo) itself.
Lastly, for the host application to scanned all available COM objects that fits into one category, we use ICatInformation EnumClassesOfCategories. EnumClassesOfCategories used 5 parameters, but we only used 3 parameters. First is to indicate how much category we interest to. Second the array of category itself, and last the CLSID/GUID enumerator of matching coclasses.
All above function will be used in this following unit :
_________________________________________________________________
unit uhdshake;
interface
uses
Windows,
ActiveX,
ComObj;
type
TImplementedClasses = array [0..255] of TCLSID;
function GetImplementedClasses (var ImplementedClasses : TImplementedClasses) : integer;
procedure RegisterClassImplementation (const CATID, CLSID : TCLSID; const sDescription : String; bRegister : boolean);
implementation
function GetImplementedClasses (CategoryInfo : TCategoryInfo; var ImplementedClasses : TImplementedClasses) : integer;
var
CatInfo : ICatInformation;
Enum : IEnumGuid;
Fetched : UINT;
begin
Result := 0;
CatInfo := CreateComObject (CLSID_StdComponentCategoryMgr) as ICatInformation;
OleCheck (CatInfo.EnumClassesOfCategories (1, @CategoryInfo, 0, nil, Enum));
if (Enum nil) then
begin
OleCheck (Enum.Reset);
OleCheck (Enum.Next (High (ImplementedClasses), ImplementedClasses [1], Fetched));
Result := Fetched;
end;
end;
procedure RegisterClassImplementation (const CATID, CLSID : TCLSID; const sDescription : String; bRegister : boolean);
var
CatReg : ICatRegister;
CategoryInfo : TCategoryInfo;
begin
CoInitialize (nil);
CategoryInfo.CATID := CATID;
CategoryInfo.LCID := LOCALE_SYSTEM_DEFAULT; //dummy
StringToWideChar(sDescription, CategoryInfo.szDescription, Length(sDescription) + 1);
CatReg := CreateComObject (CLSID_StdComponentCategoryMgr) as ICatRegister;
if (bRegister) then
begin
OleCheck (CatReg.RegisterCategories (1, @CategoryInfo));
OleCheck (CatReg.RegisterClassImplCategories (CLSID, 1, @CategoryInfo));
end
else
begin
OleCheck (CatReg.UnregisterClassImplCategories (CLSID, 1, @CategoryInfo));
DeleteRegKey ('CLSID\' + GuidToString (CLSID) + '\' + 'Implemented Categories');
end;
CatReg := nil;
CoUninitialize;
end;
end.
_________________________________________________________________
The host application will use GetImplementedClasses to retrieve the CLSIDs of all COM objects that fits into the category it provided. Notice that the code used TImplementedClasses as a container for all CLSID retrieved. TImplementedClasses is defined simply as array of 256 CLSID. I Think that should be enough even for all extreme classes.
RegisterClassImplementation with parameter bRegister set to True is used in ActiveX DLL DLLRegisterServer. This will register the category and the coclass that implemented in the Component Categories.
RegisterClassImplementation with parameter bRegister set to False used in ActiveX DLL DLLUnregisterServer will unregister the coclass that implemented the given category.