previous page
next page

The CAtlModule Class

All COM servers need to support registration, class objects, and lifetime management. Each type of server provides its registration, class objects, and lifetime management slightly differently, but the basic required functionality is the same for all types of servers.

ATL defines the CAtlModule base class and three derived classes, along with numerous helper methods that support server registration, class objects, and lifetime management. Each of the three derived classes provides support for a specific COM server type. CAtlDllModuleT provides inproc server functionality; CAtlExeModuleT and CAtlServiceModuleT support local servers and Windows service applications, respectively. Many of the methods in these classes simply iterate over the entries in the object map and ask each class in the map to perform the real work.

When you create an ATL COM server project using the Visual C++ ATL project wizard, it generates relatively boilerplate code that uses functionality in one of the CAtlModule-derived classes to implement the server.

An ATL server contains one global instance of the CAtlModule class, which the ATL project wizard names _AtlModule. The _AtlModule instance also holds state global to the entire server. You can access this via the global pointer _pAtlModule.

The _AtlModule Global Variable

An ATL inproc server uses the CAtlDllModuleT class directly by declaring a global instance of a project-specific module class derived from the CAtlDllModuleT server's project.cpp file. Here's what the module class definition and global variable declaration look like in the wizard-generated code for an inproc server called Math:

class CMathModule : public CAtlDllModuleT< CMathModule > {
public:
    DECLARE_LIBID(LIBID_Math)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MATH,
        "{9CB95B71-536A-476a-9244-61363F5C60CA}")
};

CMathModule _AtlModule;

The DECLARE_LIBID macro simply sets the m_libid member variable of the CAtlModule base class to the GUID value supplied as the macro parameter. The DECLARE_REGISTRY_APPID_RESOURCEID macro declares an UpdateRegistryAppId method and helper functions for registering the AppId for the server.

The custom module class that the wizard generates provides a place for you to customize the functionality of ATL's module classes. In many places, CAtlDllModuleT (as well as CExeModuleT and CServiceModuleT) downcasts to your custom module class so that a custom method is invoked if one is supplied. For instance, look at the implementation of DllRegisterServer that CAtlDllModuleT provides:

HRESULT DllRegisterServer(BOOL bRegTypeLib = TRUE) {
    T* pT = static_cast<T*>(this);                  
    HRESULT hr = pT->RegisterAppId();               
    if (SUCCEEDED(hr))                              
        hr = pT->RegisterServer(bRegTypeLib);       
    return hr;                                      
}                                                   

Here, if your CMathModule had defined a custom version of RegisterAppId or RegisterServer, those custom versions would be invoked in lieu of the default implementation provided in CAtlDllModuleT.

A local server defines a specialized version of CAtlModule, called CAtlExeModuleT, in the server's project.cpp file:

class CMathModule : public CAtlExeModuleT< CMathModule > {
public:
   DECLARE_LIBID(LIBID_Math)
   DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MATH,
       "{9CB95B71-536A-476a-9244-61363F5C60CA}")
};

CMathModule _AtlModule;

A service-based server also defines a specialized version of CAtlModule, called CAtlServiceModuleT, in the server's project.cpp file:

class CMathModule :
    public CAtlServiceModuleT< CMathModule, IDS_SERVICENAME >
{
public:
    DECLARE_LIBID(LIBID_Math)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MATH,
    "{9CB95B71-536A-476a-9244-61363F5C60CA}")
};

CMathModule _AtlModule;

The CAtlModule Registration Support

The CAtlModuleT class has extensive support for registration and unregistration of COM objects and servers.

The RegisterServer and UnregisterServer Methods

The RegisterServer and UnregisterServer methods add or remove standard class registration information (ProgID, version-independent ProgID, class description, and threading model) into or from the system Registry. These methods call upon a global instance of CAtlComModule, which, in turn, uses the AtlComModuleRegisterServer and AtlComModuleUnregisterServer helper functions to perform the actual task.

HRESULT RegisterServer(BOOL bRegTypeLib = FALSE,    
    const CLSID* pCLSID = NULL);                    
HRESULT UnregisterServer(BOOL bUnRegTypeLib = FALSE,
    const CLSID* pCLSID= NULL);                     

The RegisterServer method updates the system Registry for a single class object when the pCLSID parameter is non-NULL. When the parameter is NULL, the method calls the UpdateRegistry method for all classes listed in the object map. When the bRegTypeLib parameter is trUE, the method also registers the type library.

The UnregisterServer method removes the Registry entries for a single class when the pCLSID parameter is non-NULL. When the parameter is NULL, the method calls the UpdateRegistry method for all classes listed in the object map. The overloaded method that accepts a bUnRegTypeLib parameter also unregisters the type library when the parameter is trUE.

Inproc servers call upon this support from their DllRegisterServer and DllUnregisterServer functions:

STDAPI DllRegisterServer(void) {
    return _AtlModule.DllRegisterServer();
}

STDAPI DllUnregisterServer(void) {
    return _AtlModule.DllUnregisterServer();
}

The calls to CAtlDllModuleT's DllRegisterServer and DllUnregisterServer functions invoke RegisterServer and UnregisterServer on the CAtlModule base class. The calls to RegisterAppId and UnRegisterAppId execute the server's registration script. The DECLARE_REGISTRY_APPID_AND_RESOURCEID macro we supplied in our CAtlModule-derived class defined an UpdateRegistryAppId function that runs the server's registration script.

template <class T>                                          
class ATL_NO_VTABLE CAtlDllModuleT : public CAtlModuleT<T> {
public :                                                    
     ...                                                    
    HRESULT DllRegisterServer(BOOL bRegTypeLib = TRUE) {    
       T* pT = static_cast<T*>(this);                       
       // server script                                     
       HRESULT hr = pT->RegisterAppId();                    
         if (SUCCEEDED(hr))                                 
           // all class scripts                              
         hr = pT->RegisterServer(bRegTypeLib);              
       return hr;                                           
    }                                                       
    HRESULT DllUnregisterServer(BOOL bUnRegTypeLib = TRUE) {
       T* pT = static_cast<T*>(this);                       
       // all class scripts                                 
       HRESULT hr = pT->UnregisterServer(bUnRegTypeLib);    
         if (SUCCEEDED(hr))                                 
           // server script                                 
           hr = pT->UnregisterAppId();                      
         return hr;                                         
    }                                                       
    ...                                                     
};                                                          

With a local server, the ATL wizard generates a WinMain that simply delegates to CAtlExeModuleT::WinMain. This code parses the command line to determine which action (register or unregister) to take and then delegates the work to CAtlModule.

bool CAtlExeModuleT< T >::ParseCommandLine(LPCTSTR lpCmdLine,
   HRESULT* pnRetCode) {                                     
   ...                                                       
                                                             
   if (WordCmpI(lpszToken, _T("UnregServer"))==0) {          
     *pnRetCode = pT->UnregisterServer(TRUE);                
     if (SUCCEEDED(*pnRetCode))                              
       *pnRetCode = pT->UnregisterAppId();                   
     return false;                                           
   }                                                         
                                                             
   // Register as Local Server                               
   if (WordCmpI(lpszToken, _T("RegServer"))==0) {            
     *pnRetCode = pT->RegisterAppId();                       
     if (SUCCEEDED(*pnRetCode))                              
       *pnRetCode = pT->RegisterServer(TRUE);                
     return false;                                           
   }                                                         
                                                             
   ...                                                       
                                                             
   return true;                                              
}                                                            

The UpdateRegistryFromResource Methods

Notice that a COM server first calls the RegisterAppId to register the server-specific entries before registering the class-specific entries. This method calls the UpdateRegistryAppId method that our DECLARE_REGISTRY_APPID_RESOURCEID macro generates. Ultimately, the real work is delegated to the UpdateRegistryFromResource member function.

When you define the preprocessor symbol _ATL_STATIC_REGISTRY, the UpdateRegistryFromResource method maps to UpdateRegistryFromResourceS. When you do not define this preprocessor symbol, the method maps to UpdateRegistryFromResourceD. (The S or D stands for Static or Dynamic.) The difference between the two functions is simple: UpdateRegistryFromResourceS eventually calls code in atlbase.h that updates the Registry. The implementation of UpdateRegistryFromResourceD, on the other hand, calls out to the ATL Registrar component in atl80.dll[8] to do the registration. As a result, if you don't define _ATL_STATIC_REGISTRY, you need to distribute atl80.dll along with your COM server.[9]

[8] Actually, this can be atl7.dll, atl71.dll, or atl80.dll, depending on the version of ATL you're compiling against.

[9] atl80.dll is about 93KB in size. Separate versions of the registrar DLL used to exist for Win9x and WinNT, but they appear to have merged in this version of the library. Personally, I recommend using static linking just to avoid the need to distribute another DLL because the size cost is fairly minor.

#ifdef _ATL_STATIC_REGISTRY                                      
#define UpdateRegistryFromResource UpdateRegistryFromResourceS   
#else                                                            
#define UpdateRegistryFromResource UpdateRegistryFromResourceD   
#endif                                                           
                                                                 
     // Resource-based Registration                              
#ifdef _ATL_STATIC_REGISTRY                                      
    // Statically linking to Registry Component                  
    HRESULT WINAPI UpdateRegistryFromResourceS(LPCTSTR lpszRes,  
        BOOL bRegister,                                          
        struct _ATL_REGMAP_ENTRY* pMapEntries = NULL);           
    HRESULT WINAPI UpdateRegistryFromResourceS(UINT nResID,      
        BOOL bRegister,                                          
        struct _ATL_REGMAP_ENTRY* pMapEntries = NULL);           
#else                                                            
    HRESULT WINAPI UpdateRegistryFromResourceD(LPCTSTR lpszRes,  
    BOOL bRegister,                                              
    struct _ATL_REGMAP_ENTRY* pMapEntries = NULL) {              
        USES_CONVERSION;                                         
        return UpdateRegistryFromResourceDHelper(T2COLE(lpszRes),
                     bRegister, pMapEntries);                    
}                                                                
    HRESULT WINAPI UpdateRegistryFromResourceD(UINT nResID,      
        BOOL bRegister,                                          
        struct _ATL_REGMAP_ENTRY* pMapEntries = NULL) {          
        return UpdateRegistryFromResourceDHelper(                
                     (LPCOLESTR)MAKEINTRESOURCE(nResID),         
                     bRegister, pMapEntries);                    
    }                                                            
#endif                                                           

The Type Library Registration Methods

ATL provides a few other registration helper methods:

// Registry support (helpers)                
HRESULT RegisterTypeLib();                   
HRESULT RegisterTypeLib(LPCTSTR lpszIndex);  
HRESULT UnRegisterTypeLib();                 
HRESULT UnRegisterTypeLib(LPCTSTR lpszIndex);

As you might expect, RegisterTypeLib registers the type library contained in your server's resources. UnRegisterTypeLib unregisters it, of course. These two methods expect the type library to be present in your server's resources as a custom resource of type TYPELIB with integer resource identifier 1. You create such a resource by adding the following line to your server's .rc file.

1 TYPELIB "ATLInternals.tlb"

You can embed multiple type libraries in a server, although it's an unusual thing to do. You simply need to give them unique integer resource identifiers.

1 TYPELIB "ATLInternals.tlb"
2 TYPELIB "ATLInternalsEx.tlb"

To register and unregister the second type library, you must call the RegisterTypeLib or UnRegisterTypeLib methods, respectively, and specify a string in the form "\\N", where N is the integer index of the TYPELIB resource. The following lines register both type libraries from the previous resource script example:

_AtlModule.RegisterTypeLib ();
_AtlModule.RegisterTypeLib (_T ("\\2"));


previous page
next page
Converted from CHM to HTML with chm2web Pro 2.75 (unicode)