Tuesday, August 07, 2007

Dif between "attributed" and "unattributed" ATL projects (VS 2005)

I'm trying to figure out how to make COM objects (note that the Microsoft-approved way to make them is with ATL). First obstacle: what's this "attributed" nonsense?
  • Only the attributed version defines _ATL_ATTRIBUTES in the project settings.
  • Attributed version doesn't include a .tlb file from its .rc resource file.
  • In the main .cpp file (ProjectName.cpp), the attributed version has just one empty class (CProjectNameModule) with a [module] attribute attached. The nonattributed version has the same class without the attribute, and this time the class is derived from CAtlDllModuleT<CProjectNameModule> (instead of nothing) and uses DECLARE_LIBID and DECLARE_REGISTRY_APPID_RESOURCEID inside it. In addition, the nonattributed version has DllMain, DllCanUnloadNow, DllGetClassObject, DllRegisterServer, DllUnregisterServer. All these functions make calls into an object called _AtlModule of type CProjectNameModule; oddly there is no such object in the attributed project.
  • Only the nonattributed project has ProjectName.def and ProjectName.idl. The .def mentions DllCanUnloadNow, DllGetClassObject, DllRegisterServer and DllUnregisterServer; the .idl has a single construct, "[...] library ProjectNameLib {...}".
  • Only the nonattributed project has the generated files ProjectName.h and ProjectName_i.c which define a few mysterious things.
  • When you make an ATL project you get a sibling project with a "PS" suffix. This extra or "bonus" project is not compiled by default and starts with only one source file, ProjectNameps.def. 'PS' evidently stands for Proxy/Stub.
  • Other files are generated that I'm not mentioning (see readme.txt in the generated project)
As they build, both projects spend time processing a bunch of mysterious ".idl" and ".acf" files that are not in the project. Then it looks like the generated DLL's GUID(s?) are automatically registered in the registry.

The docs say "An attributed project does not allow support for MFC or merging of proxy/stub code." but it doesn't give any reason, so I'm baffled.

This attributed/nonattributed stuff is in effect permanently:
  • When you go to create a new COM class on the attributed project (right click project, choose Add => Class, then ATL Simple Object), you get an "__interface IMyComClass" and a "class CMyComClass" derived from it in your C++ header file (MyComClass.h), with attributes attached that cause code to be generated in "_ATLProjectAttributed.idl" and in other files (files that are not in the project's list of files).
  • When you create the same COM class on the unattributed project, you get something quite different. In the header (MyComClass.h) you only get a definition for CMyComClass, not IMyComClass; and this time CMyComClass has three base classes, and uses the DECLARE_REGISTRY_RESOURCEID and BEGIN_COM_MAP(...) ... END_COM_MAP() macros inside the class. IMyComClass is defined in ProjectName.idl (the attributed project has no .idl listed). The .def file is unaffected.
In both cases, the MyComClass.cpp file starts empty and gets filled as you add functions/properties (by right-clicking COM interfaces in Class View). Interestingly, Class View lists the interfaces just as well for the unattributed project, even though the interfaces are declared in an .idl file, not a C++ file. Here's something I find very intriguing: just after you generate a "MyComClass" class with a wizard, IMyComClass immediately shows up in the Class View. If you refer to IMyComClass in code and press F12 you are taken to the definition in the .idl file. However, after you compile your project, put the cursor on IMyComClass and press F12 again. This time you're taken to the definition of IMyComClass in the generated C++ header file. In Class View you'll see IMyComClass listed twice; one refers to the .idl and one refers to the .h file. An intellisense member list for the interface won't appear until after the project has been compiled.

Links: