[Previous] [Next]

Self-registration involves adding keys and values to the Registry to let COM know that your server module is available and how to launch it. Well-behaved servers also remove those entries when the object is no longer available. ATL supports this capability through the functions CComModule::RegisterServer and CComModule::UnregisterServer. This abstraction is used for both DLL and EXE modules through the _Module global instance. EXE servers register themselves when they are run with the /RegServer option on the command line and unregister themselves when invoked with /UnregServer. DLL servers perform these functions when the exported functions DllRegisterServer and DllUnregisterServer are called. The ATL COM AppWizard generates the necessary code in either case. The following code shows the registration and cleanup for a DLL server:

 STDAPI DllRegisterServer(void) {     // Registers object, typelib and all interfaces in typelib     return _Module.RegisterServer(TRUE); } STDAPI DllUnregisterServer(void) {     return _Module.UnregisterServer(TRUE); } 

CComModule::RegisterServer takes a BOOL as its first argument. If the argument is TRUE, the type library for the server should be registered along with the coclasses and interfaces for the objects themselves. The Microsoft Interface Definition Language (MIDL) compiler generates the type library from the project IDL file when the project is compiled. The IDL file for the project is listed in the FileView tab under Source Files. We won't get into the details of IDL here, but be aware that by default ATL uses the helper function AtlModuleRegisterTypeLib to perform the necessary registration for your type library. CComModule::RegisterServer has an optional second argument—the CLSID (class ID) of the object to register. By default, all objects in the object map are registered, so normally this argument isn't necessary. If you specify a CLSID, only the object that has a matching CLSID will be registered. The same rule applies to CComModule::UnregisterServer, which takes a CLSID as its only argument.

Registry Scripts

Let's examine a section of the ATL COM AppWizard-generated registration code for an EXE server in _tWinMain.

 if(lstrcmpi(lpszToken, _T("UnregServer"))==0) {     _Module.UpdateRegistryFromResource(IDR_ATLExeServer, FALSE);     nRet = _Module.UnregisterServer(TRUE);     bRun = FALSE;     break; } if(lstrcmpi(lpszToken, _T("RegServer"))==0) {     _Module.UpdateRegistryFromResource(IDR_ATLExeServer, TRUE);     nRet = _Module.RegisterServer(TRUE);     bRun = FALSE;     break; } 

In addition to the expected _Module.RegisterServer and _Module.UnregisterServer entries, notice the calls to _Module.UpdateRegistryFromResource. When executed, this helper function adds or removes Registry entries based on a resource script. A resource script is a custom resource that is a text-based representation of the entries to make for an object or an application. The first argument to _Module.UpdateRegistryFromResource is the resource ID of the script, and the second argument indicates whether the entries should be registered (TRUE) or unregistered (FALSE). The script for the preceding code section looks like this:

 HKCR {     NoRemove AppID     {         {EA5E30E1-C133-11D2-B18B-D0554FC10001} = s `ATLExeServer'         `ATLExeServer.EXE'         {             val AppID = s {EA5E30E1-C133-11D2-B18B-D0554FC10001}         }     } } 

The script shown adds the Registry key HKEY_CLASSES_ROOT\AppID if one doesn't exist. The NoRemove attribute ensures that the key won't be removed during the unregistration process. This is a good thing, since many applications depend on the presence of the AppID key. Under this key, a new GUID subkey is made, representing the AppID for this EXE module. This key is then given the default string value "ATLExeServer." So far we've covered the script down to the "ATLExeServer.EXE" line. The next three lines create another subkey, HKCR\AppID\ATLExeServer.EXE, and add a named string value (AppID) under the new subkey with the GUID as a string value.

Your compiled project contains Registry scripts as resources, and they can be viewed from the FileView tab under Resource Files or in the ResourceView tab under the custom type "REGISTRY". ATL COM projects have a separate script resource for each object contained in the server. EXE servers have an additional script for the AppID, similar to the one shown earlier.

The ATL team didn't invent a new notation for Registry scripts. Registry scripts are in Backus-Naur form (BNF), a standard notation used to describe syntax for interpretation. This is but one application of BNF.

Object Registration Macros

The Module.RegisterServer function registers individual object scripts during its activity. Now that you're armed with the knowledge that _Module.UpdateRegistryFromResource will interpret a Registry script, the DECLARE_REGISTRY_RESOURCEID macro in a default ATL Object Wizard-generated header file should make more sense.

 #define DECLARE_REGISTRY_RESOURCEID(x)\     static HRESULT WINAPI UpdateRegistry(BOOL bRegister)\     {\     return _Module.UpdateRegistryFromResource(x, bRegister);\     } 

Just as you saw in _tWinMain, the macro expands to a static function, UpdateRegistry, which delegates to _Module.UpdateRegistryFromResource. Table 7-1 lists other registration macros that you can also use.

Table 7-1. Registration Macros.

Macro Description
DECLARE_REGISTRY_RESOURCE(x) x is a string identifier for a script resource.
DECLARE_REGISTRY(class, pid, vpid, nid, flags) Inserts and removes the five basic keys for your object. No script is used.
DECLARE_NO_REGISTRY() Doesn't make any Registry entries.

If the mechanisms described above don't meet your needs, you can forgo the macros and write your own custom version of the static UpdateRegistry member function. Simply use the same signature as the macro expansion shown in the preceding code.

Chapter 9 examines the ATL Object Wizard in detail.

The ATL Registry Component (Registrar)

CComModule::RegisterServer and CComModule::UnregisterServer need a way to parse the Registry scripts in a server and add or remove the necessary entries. ATL provides the Registrar component for this purpose. The Registrar is available to your application as a prebuilt COM component in ATL.DLL or as statically linked code. Two preprocessors control the option: _ATL_STATIC_REGISTRY and _ATL_DLL. _ATL_STATIC_REGISTRY forces the inclusion of the Registrar code (statreg.h) into atlbase.h, which ends up in your stdafx.h. The absence of _ATL_STATIC_REGISTRY results in the use of the parser in ATL.DLL, which means you must distribute ATL.DLL with your application and register it during installation. ATL.DLL serves two purposes:

  • It contains the COM version of the parser CDLLRegObject.
  • It exports global ATL functions to optimize your server for size.

Linkage to ATL.DLL is enabled with the _ATL_DLL preprocessor. The ReleaseMinSize build uses this option to build the smallest server possible, removing the global implementations from the ATL source and instead using the DLL versions. Using _ATL_DLL and _ATL_STATIC_REGISTRY together results in a compiler error. The key to understanding this problem lies in statreg.h.

 #if defined(_ATL_DLL) | defined(_ATL_DLL_IMPL) class ATL_NO_VTABLE CRegObject     : public IRegistrar #else class CRegObject #endif 

When _ATL_STATIC_REGISTRY is defined, CComModule::UpdateRegistryFromResource attempts to instantiate a CRegObject. This will fail if _ATL_DLL is also defined because of the ATL_NO_VTABLE optimization shown in the preceding code. ReleaseMinDependency builds have the ATL_STATIC_REGISTRY preprocessor and therefore include the Registrar code directly in your server. To completely remove dependencies on ATL.DLL, you must remove the _ATL_DLL preprocessor and add _ATL_STATIC_REGISTRY. Table 7-2 summarizes the preprocessors and the resulting project dependencies.

Table 7-2. ATL Preprocessors.

No No No linkage to ATL.DLL but uses ATL.DLL for the COM version of the parser
No Yes Linkage to ATL.DLL and uses COM version of the parser
Yes No No ATL.DLL dependencies whatsoever
Yes Yes Not allowed; build failure

To make this information concrete, let's look at how CComObject uses the parser in the default debug build configuration, which has neither _ATL _DLL nor _ATL_STATIC_REGISTRY. Recall that CComModule::UpdateRegistryFromResource is where ATL does the real registration work, either when called by itself or as the result of the DECLARE_REGISTRY_RESOURCEID(x) macro and CComModule::RegisterServer. Atlbase.h defines UpdateRegistryFromResource differently depending on the _ATL_STATIC_REGISTRY preprocessor. Here are the macro definitions for static and DLL linkage:

 #ifdef _ATL_STATIC_REGISTRY #define UpdateRegistryFromResource UpdateRegistryFromResourceS #else #define UpdateRegistryFromResource UpdateRegistryFromResourceD #endif 

Because we don't have _ATL_STATIC_REGISTRY in the debug build, we'll use UpdateRegistryFromResourceD. This function delegates to AtlModuleUpdateRegistryFromResourceD, which uses good old CoCreateInstanceEx to instantiate the Registrar component from ATL.DLL.

 ATLINLINE ATLAPI AtlModuleUpdateRegistryFromResourceD(     _ATL_MODULE* pM, LPCOLESTR lpszRes, BOOL bRegister,     struct _ATL_REGMAP_ENTRY* pMapEntries, IRegistrar* pReg) {     USES_CONVERSION;     ATLASSERT(pM != NULL);     HRESULT hRes = S_OK;     CComPtr<IRegistrar> p;     if(pReg != NULL)         p = pReg;     else     {         hRes = CoCreateInstanceEx(CLSID_Registrar, NULL,             CLSCTX_INPROC_SERVER, IID_IRegistrar, (void**)&p);     }      if(bRegister)         hRes = p->ResourceRegister(pszModule,             ((UINT)LOWORD((DWORD)lpszRes)), szType);  } 

ResourceRegister handles parsing the entire script and making all of the requested Registry entries. You can look at statreg.h in the ATL include directory for all of the gory parser details. As you can see, ATL has a significant amount of code to support self-registration.

Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127 © 2008-2017.
If you may any questions please contact us: