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 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.
#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"));
|