Developers Workshop to COM and ATL 3.0
| < Free Open Study > |
|
CComModule: Your Server's Housekeeper
This single ATL class contains methods and data members centered on the creation and registration details of the coclasses in the component housing. Most of the methods of CComModule make use of the server-wide object map to register, unregister, and create your objects. The basic functionality supplied by CComModule can be broken down into the following categories:
-
Secondary creation and post destruction calls for the CComModule instance.
-
Maintaining the number of server locks and active objects in the server.
-
Performing registration and unregistration duties.
-
Retrieving class factories for clients.
Creation and Termination of the CComModule Object
Every ATL COM server contains a single global instance of CComModule named _Module. ATL's implementation of DllMain() calls CComModule::Init() or CComModule::Term() based on the reason for the method invocation:
// ATL in-proc servers initialize and terminate _Module via DllMain() BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) { if (dwReason == DLL_PROCESS_ATTACH) { _Module.Init(ObjectMap, hInstance, &LIBID_ATLSHAPESLib); DisableThreadLibraryCalls(hInstance); } else if (dwReason == DLL_PROCESS_DETACH) { _Module.Term(); } return TRUE; // ok }
ATL EXE servers call the same methods within the scope of _tWinMain(). Regardless of the type of component housing, these methods are used to set the internal state of the global level CComModule object (_Module) and deallocate any acquired resources.
CComModule::Init() takes three parameters: the object map (which is an array of _ATL_OBJMAP_ENTRY structures, hence _ATL_OBJMAP_ENTRY*), the instance of the DLL or EXE module, and the LIBID that defines the classes and interfaces defined in the server. If the LIBID is NULL, ATL assumes the *.tlb defined by your project contains the necessary information:
// Pass in object map, DLL/EXE instance, and LIBID to the CComModule object. HRESULT Init( _ATL_OBJMAP_ENTRY* p, // The object map. HINSTANCE h, // Module instance. const GUID* plibid = NULL) // LIBID.
CComModule::Term() takes no parameters, and is called when the server itself (DLL or EXE) is being removed from memory:
// Clean up CComModule instance when server is going down. void Term( );
Monitoring Active Locks and Active Objects
CComModule provides a number of methods used to monitor the number of server locks and currently active objects. When we were building in-proc servers by hand, we maintained two global counters for these purposes. When we were building our local server in Chapter 5, we combined these two counters into a single global variable. This is the approach of ATL. All of these locking methods are internally implemented to manipulate the public m_nLockCnt data member. When m_nLockCnt reaches zero, the server has no active objects and no server locks and can therefore be unloaded. Lock() and Unlock() calls are automatically called by the ATL framework; however, you may call them yourself if necessary:
// Active object/lock methods of CComModule. LONG Lock(); // Increments m_nLockCnt. LONG Unlock(); // Decrements m_nLockCnt. LONG GetLockCount(); // Returns current value of m_nLockCnt. LONG m_nLockCnt; // Active objects + server locks.
As we have seen, ATL DLLs make use of GetLockCount() from within the DllCanUnloadNow() export:
// The lock count is represented by m_nLockCnt and obtained via GetLockCount(). STDAPI DllCanUnloadNow(void) { return (_Module.GetLockCount()==0) ? S_OK : S_FALSE; }
Registration Duties
Registration of your COM server is also handled by methods of CComModule. Both CComModule::RegisterServer() and CComModule::UnregisterServer() define a defaulted NULL LPCLSID parameter, which informs ATL to enter (or remove) registry information for every coclass contained in the server-wide object map.
ATL allows you to tweak the behavior of the server's (un)registration. If you wish to insert or remove registry information on a more selective coclass-by-coclass basis, set the default NULL LPCLSID parameter to a specific CLSID. Furthermore, the BOOL parameter is used to determine if the type library itself is to be registered as part of the process:
// Enter/remove server information for each entry in the object map. HRESULT RegisterServer(BOOL bRegTypeLib = FALSE, CLSID* pCLSID = NULL ); HRESULT UnregisterServer(BOOL bUnRegTypeLib, const CLSID* pCLSID = NULL);
For in-proc servers, these methods are called from the DllRegisterServer() and DllUnregisterServer() exports:
// DllRegisterServer - Adds entries to the system registry STDAPI DllRegisterServer(void) { // Registers object, typelib, and all interfaces in typelib return _Module.RegisterServer(TRUE); } // DllUnregisterServer - Removes entries from the system registry STDAPI DllUnregisterServer(void) { return _Module.UnregisterServer(TRUE); }
Creating Class Factories
A handful of CComModule methods are used to create class factories per client request. Given what you already know about class factories created by DLL and EXE servers, I'd bet you could decipher the following CComModule methods:
// Return an IClassFactory pointer for a client (DLL servers only) HRESULT GetClassObject( REFCLSID rclsid, REFIID riid, LPVOID* ppv ); // Register a class object to the class table (EXE servers only) HRESULT RegisterClassObjects( DWORD dwClsContext, DWORD dwFlags ); // Remove a class object from the class table (EXE servers only) HRESULT RevokeClassObjects( );
The Default CComModule Instance
As mentioned, the ATL COM AppWizard will always generate a global instance of CComModule named _Module. Don't start to get clever and attempt to rename this instance, as ATL source code is looking for this object by name. DLL servers create an instance of CComModule like so:
// The single CComModule instance (DLL servers). CComModule _Module;
EXE servers do not create an instance of CComModule directly. Rather, the ATL COM AppWizard will create an instance of a class named CExeModule, which is derived from CComModule. CExeModule inherits the core functionality of CComModule, and extends it by adding EXE-specific functionality:
// The single CExeModule instance (EXE servers). CExeModule _Module;
The reason ATL subclasses a new class from CComModule is to keep the amount of boilerplate code to a minimum. If CComModule contained EXE-centric methods, DLL servers would lug around code they do not actually need! This wraps up our initial investigation of CComModule's public sector (see online help for the set of remaining CComModule methods). As mentioned earlier, a CComModule object cannot function by itself, but must have access to the server's object map.
| < Free Open Study > |
|