ATL Attributes: module

This is a listen-to attribute for ATLPROV. It is handled first by the compiler as a built-in attribute and then by such other attribute providers that “listen to” it. The following notes assume familiarity with the syntax and behaviour of the module attribute as built in to the compiler. The arguments of interest to ATLPROV are type, name, uuid and resource_name.


The module attribute may cause ATLPROV to inject code that would define variables and functions at global scope. It is an error (ATL2070) if any of these objects that seem likely to get defined are already defined, meaning specifically if any of the following are true:

Module Class

The module attribute may be applied anonymously, else to a target class or struct (hereafter referred to just as a class). If the attribute has a target, then ATLPROV adds to the target class whatever is needed for representing the module. If the attribute is anonymous, ATLPROV defines a module class from scratch.

Either way, ATLPROV defines an instance of this module class, and names this instance _AtlModule.

Injected Module Class

When ATLPROV is left to define the module class, the name it constructs is CnameModule, using the name argument of the module attribute. The definition varies with the type argument.

If the type is dll or exe, the module class is defined as

class moduleclass : public CAtlDllModuleT <moduleclass>
    DECLARE_LIBID (__uuidof (name))
    DECLARE_REGISTRY_APPID_RESOURCEID (resource_name, "uuid")


class moduleclass : public CAtlExeModuleT <moduleclass>
    DECLARE_LIBID (__uuidof (name))
    DECLARE_REGISTRY_APPID_RESOURCEID (resource_name, "uuid")

respectively. The line with the DECLARE_REGISTRY_APPID_RESOURCEID macro is omitted unless the resource_name argument is given and non-empty.

The module class is defined similarly when the type is service, but a non-empty resource_name is simply assumed:

class moduleclass : public CAtlServiceModuleT <moduleclass, resource_name>
    DECLARE_LIBID (__uuidof (name))
    DECLARE_REGISTRY_APPID_RESOURCEID (resource_name, "uuid")

Failure to supply a non-empty resource_name brings no complaint from ATLPROV but the injected code is defective (not that the hapless user can obtain it for inspection) and does not compile without error. Indeed, compiling the simple fragment

#define     _WIN32_WINNT    0x0400
#define     _ATL_ATTRIBUTES
#include    <atlbase.h>
#include    <atlisapi.h>

    module (type = "service", name = "Test")    // various errors, plus fatal error C1004

produces not just 17 errors and then a fatal error, but also

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Better, one might think, would be that the “application’s support team” at Microsoft be given the time and discipline to check such very nearly trivial examples before the product is released and promoted (and contributing to Microsoft’s earnings). The point to injecting code at the direction of attributes is surely to reduce not just the work for programmers but also the inevitability of silly coding oversights by programmers hurrying through their cutting and pasting of boilerplate code. It’s not obviously any help to anyone if a programmer’s syntactically correct use of attributes instead picks up silly coding oversights from Microsoft (with cryptic error messages as a bonus).

Modified Target Class

When the module attribute has a target, ATLPROV tries to make the target into the module class by adding a base class and members corresponding to those that would otherwise have been defined for the new class CnameModule. Bases and members already defined for the target are respected. Bases and members shown above for CnameModule are added, but with CnameModule now replaced by the name of the target class.

Were the correspondence exact, there would no difference between an anonymous application and a targeted one in which the target class happens to be named CnameModule and to have an empty definition. Instead, there are slight differences, and it is not known whether these exist by design.

In all cases, the added base class derives from CAtlModule. If the target already derives from CAtlModule, then all additions to the target are skipped, with a warning (ATL4071).


Members are added without setting an access specifier. When the type is dll, the DECLARE_REGISTRY_APPID_RESOURCEID macro is omitted.

in the opposite order, i.e., with DECLARE_REGISTRY_APPID_RESOURCEID before DECLARE_LIBID. The additions accept whatever is the current access specifier.

no DECLARE_REGISTRY_APPID_RESOURCEID macro is added when the type is dll.

type argument added members
exe if resource_name is not empty DECLARE_REGISTRY_APPID_RESOURCEID (resource_name, "uuid")
all type values DECLARE_LIBID (__uuidof (name))

Were the correspondence exact, then there would be no difference between an anonymous application and a targeted one in which the target class happens to be named CnameModule. Instead, there are differences and it is not known whether they exist by design or oversight.

Exported Functions

A DLL written with the ATL is expected to export The most entry points are needed when the type is dll.

extern "C" STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, void **ppv)
    return _AtlModule.DllGetClassObject (rclsid, riid, ppv);
extern "C" STDAPI DllRegisterServer (void)
    return _AtlModule.DllRegisterServer ();
extern "C" STDAPI DllUnregisterServer (void)
    return _AtlModule.DllUnregisterServer ();
extern "C" STDAPI DllCanUnloadNow (void)
    return _AtlModule.DllCanUnloadNow ();
BOOL WINAPI DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
return _AtlModule.DllMain (dwReason, lpReserved);

Moreover, the linker has to be told of them:

#if defined (_M_IX86)
#pragma comment (linker, "/EXPORT:DllMain=_DllMain@12,PRIVATE")
#pragma comment (linker, "/EXPORT:DllRegisterServer=_DllRegisterServer@0,PRIVATE")
#pragma comment (linker, "/EXPORT:DllUnregisterServer=_DllUnregisterServer@0,PRIVATE")
#pragma comment (linker, "/EXPORT:DllGetClassObject=_DllGetClassObject@12,PRIVATE")
#pragma comment (linker, "/EXPORT:DllCanUnloadNow=_DllCanUnloadNow@0,PRIVATE")
#elif defined (_M_IA64)
#pragma comment (linker, "/EXPORT:DllMain,PRIVATE")
#pragma comment (linker, "/EXPORT:DllRegisterServer,PRIVATE")
#pragma comment (linker, "/EXPORT:DllUnregisterServer,PRIVATE")
#pragma comment (linker, "/EXPORT:DllGetClassObject,PRIVATE")
#pragma comment (linker, "/EXPORT:DllCanUnloadNow,PRIVATE")

If the type is either exe or service, there is one global function to define:

extern "C" int WINAPI _tWinMain (
    HINSTANCE hInstance,
    HINSTANCE /* hPrevInstance */,
    LPTSTR lpCmdLine,
    int nShowCmd)
    return _AtlModule.WinMain (nShowCmd);