Introduction
RGS files are a good concept for being able to do some nifty custom registrations in ATL without having to muck round with the registry API.
It appears however, that the writers of ATL never got round to finishing the registry resources. They even passed up on an opportunity to add yet another #define
macro map. The full power of the substitutions was just never realized for want of a couple of macros.
A very annoying consequence of this is that whenever you change the CLSID, LIBID, or other attribute of the IDL file, you need to reflect the change in the RGS file. Hands up, how many people have forgotten to do this more than once! (I certainly have).
This utility adds the missing macros, allowing the user to add an RGS substitution map which can include UUID
s as well as strings.
Usage
The extra functionality is enabled by using DECLARE_REGISTRY_RESOURCEID_EX(ResourceId)
in place of DECLARE_REGISTRY_RESOURCEID(ResourceId)
. The resource entry used can be of a more generic format, rather than the specific RGS file currently required. This goes, as usual, in the class declaration in the header.
Then, along the same lines as all of the Microsoft macro maps, a map section is required, beginning with BEGIN_REGISTRY_MAP(Class)
and ending with END_REGISTRY_MAP()
. Inside the registry map is a list containing entries in one of 3 forms:
REGMAP_ENTRY(var,subst)
Substitute the variable with the given string.
REGMAP_RESOURCE(var,resid)
Substitute the variable with the string given by the specified resource
REGMAP_UUID(var,clsid)
Substitute the variable with the string representation of the UUID
.
The RGS file for a standard COM library will look like this:
HKCR
{
%PROGID%.%VERSION% = s '%DESCRIPTION%'
{
CLSID = s '%CLSID%'
}
%PROGID% = s '%DESCRIPTION%'
{
CLSID = s '%CLSID%'
CurVer = s '%PROGID%.%VERSION%'
}
NoRemove CLSID
{
ForceRemove %CLSID% = s '%DESCRIPTION%'
{
ProgID = s '%PROGID%.%VERSION%'
VersionIndependentProgID = s '%PROGID%'
ForceRemove 'Programmable'
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s '%THREADING%'
}
'TypeLib' = s '%LIBID%'
}
}
}
The next step is to declare a substitution map. This uses the same format that most of the Microsoft declaration maps follow. If you use the 'RGS' file above, then the map will look something like this:
BEGIN_REGISTRY_MAP(CClassName)
REGMAP_ENTRY("PROGID", "MyLibrary.ClassName")
REGMAP_ENTRY("VERSION", "1")
REGMAP_ENTRY("DESCRIPTION", "ClassName Class")
REGMAP_UUID ("CLSID", CLSID_ClassName)
REGMAP_UUID ("LIBID", LIBID_MyLibraryLib)
REGMAP_ENTRY("THREADING", "Apartment")
END_REGISTRY_MAP()
This needs to go in the class declaration as well, so I normally place it just below the DECLARE_REGISTRY_RESOURCEID_EX
declaration.
Behind the scenes of the extra macros is a simple wrapper for struct _ATL_REGMAP_ENTRY
that does some resource management, which is particularly useful for managing the lifetime of the GUID description string.