Appendix D. Attributed ATL
ATL has always been the most powerful way to build COM objects in C++. Unfortunately, with that power comes complexity. ATL requires an advanced understanding of C++ to use it effectively. That advanced understanding took more effort than many people were willing to invest. Instead, many of those users used Visual Basic to do COM instead of C++. Unfortunately, there are some COM things VB 6 just doesn’t do (including marshal-by-value, ActiveX controls with sophisticated drawing, and multithreading); when people hit those limits, they were forced back into the C++ world.
Even for those who are experienced C++ developers, building ATL projects can be a headache. Some of the most important information about your project isn’t even in C++; it’s in IDL and RGS files and resources. It can be a real effort to keep all these different parts in sync.
When Visual Studio .NET was released in 2002, the ATL team took an unusual approach in attempting to address these issues: They extended the C++ language. That extension is called attributed ATL.
Fundamentals of ATL Attributes
Introducing Attributes
C++ classes require a lot of help to become
full-fledged COM objects. A coclass must be specified to expose the
CLSID to clients so that instances of the class can be created.
Interfaces must be defined in IDL and associated with a COM
coclass. Registry entries must be created to specify the location
of the server, the threading model of the class, and the ProgId, as
well as other information. Of course, none of these is an inherent
feature of standard C++ projects, so Visual Studio enlists the help
of additional tools such as the MIDL compiler and RGS registration
scripts. Consequently, much information that is logically part of
the class definition is spread out across multiple different files.
For instance, the CLSID for a component is specified in the coclass
statement in the IDL file and also appears multiple times in the
RGS script file used for component registration. Interfaces exposed
from a COM coclass are listed in the IDL file as part of the
coclass statement; they appear in the inheritance list in the C++
class definition and must be included in a special macro map used to implement
QueryInterface
. Having information about the class
distributed in multiple places is inconvenient from a maintenance
point of view and can even lead to obscure runtime errors if
everything is not kept consistent.
ATL attributes are designed to consolidate the information that COM requires with the actual C++ class used to implement the COM component. After all, many of the various aspects of COM behavior are logically part of the C++ class definition, so it makes perfect sense to specify these aspects alongside the C++ class. Attributes can be applied to C++ classes, to files as a whole, or to special interface definitions that appear in the class header file. They appear in square brackets preceding the item to which they are applied (or anywhere in the file for attributes that apply to the entire file). The following code snippet shows how attributes appear in a class header file:
1// CalcPi.h
2...
3[ coclass,
4 threading("apartment"),
5 uuid("86759049-8B8E-47F4-81F1-AE07D3F876C7"),
6]
7class ATL_NO_VTABLE CCalcPi :
8 public ICalcPi
9{ ... }
Applying the [coclass]
attribute is all
that is required to make the CCalcPi
C++ class available
as a COM coclass. The [threading]
attribute indicates that
the class supports the Apartment threading model and should be
registered in the Windows Registry as such. The [uuid]
attribute enables you to specify the CLSID to associate with the
COM coclass, although the compiler generates one for you if it
isn’t provided. In previous versions of ATL, the information
represented by these attributes was spread among IDL files, header
files, and RGS script files. ATL attributes provide for a more
concise specification of the exact same information.
Yet, the previous code certainly isn’t suitable
for consumption by any C++ compiler. How does this syntactic
shorthand achieve the same result as many lines of IDL code and
registration script? The answer is a fairly sophisticated code
generator that works behind the scenes. When the C++ compiler sees
an attribute in a source file, it passes the attribute and any
parameters associated with it (such as the “apartment” parameter to
the earlier [threading]
attribute) to a special COM
component called an attribute
provider. The provider injects code into the compiler’s
internal parse tree to accomplish what the attribute and its
parameters specified.
Code injected by the attribute provider affects
your ATL projects in various ways. Some attributes produce IDL code
to be consumed by the MIDL compiler, other attributes insert ATL
base classes in your class definition, and still others insert
macro maps in class header files. For example, the
[threading]
attribute applied to the CCalcPi
class previously inserts the ATL base class
CComObjectRoot-Ex<CComSingleThreadModel>
to provide
the level of thread safety that is appropriate for the “apartment”
threading model value supplied to the attribute. It is important to
note, however, that this code is not injected into the original source files.
If this were the case, it would revive the same sort of maintenance
hassles that ATL attributes were designed to alleviate in the first
place. Instead, the code that the attribute provider injects is
available only at compile time. Debug builds do include information
about the injected code, enabling you to step into generated code
while debugging.
Building an Attributed ATL COM Server
Creating the Project
As an example, I rebuilt the PiSvr project from Chapter 1, “Hello, ATL,” as an attributed ATL project. To create the project, you simply run the ATL Project Wizard as normal. The only difference is to check the Attributed check box on the Application Settings page of the wizard. In Visual Studio 2005, this check box is not checked by default.
In a nonattributed project, a DLL server has a starter file with the definition of the ATL module class and the appropriate exports. With an attributed project, you instead get this:
1// PiSvr.cpp : Implementation of DLL Exports.
2
3#include "stdafx.h"
4#include "resource.h"
5
6// The module attribute causes DllMain,
7// DllRegisterServer and DllUnregisterServer
8// to be automatically implemented for you
9[ module(dll,
10 uuid = "{5247B726-8CB9-450C-9636-9C5781B69729}",
11 name = "PiSvr",
12 helpstring = "PiSvr 1.0 Type Library",
13 resource_name = "IDR_PISVR") ]
14class CPiSvrModule {
15public:
16// Override CAtlDllModuleT members
17};
The attributed project is most notable for what it does not contain. There’s no declaration of an ATL module class. There’s no IDL file. Even the RGS file is extremely minimal:
1HKCR
2{
3 NoRemove AppID
4 {
5 '%APPID%' = s 'ATL8Project'
6 'ATL8Project.EXE'
7 {
8 val AppID = s '%APPID%'
9 }
10 }
11}
In the attributed ATL project, the information
that is scattered around the project is generated by the
module
attribute on the CPiSvrModule
class. Did
you notice the comment that says “Override CAtlDllModuleT
members”? That’s rather odd, considering that CPiSvrModule
doesn’t inherit from CAtlDllModuleT
. Or does it? One of
the things the module attribute does is add the
CAtlDllModuleT
class to CPiSvrModule
’s
inheritance list. This is a common theme with ATL attributes.
Instead of the gargantuan list of base classes that most
unattributed ATL classes end up with, a single attribute can
introduce one or more base classes.
Adding a Simple Object
My next step, of course, was to add an ATL Simple Object to the project, just like any other. There’s no difference in running the Simple Object Wizard, with the exception that, in an attributed project, the Attributed check box on the Names page of the wizard is checked and disabled.
Running the wizard with an attributed project
gives a significantly different output. First, we have two
interface definitions in the CalcPi.h
header file:
1// CalcPi.h
2...
3 // ICalcPi
4[ object,
5 uuid("8E3ABD67-5075-4C38-BA00-8289E336E7F9"),
6 dual,
7 helpstring("ICalcPi Interface"),
8 pointer_default(unique) ]
9 __interface ICalcPi : IDispatch {
10};
11// _ICalcPiEvents
12[ dispinterface,
13 uuid("9822AB1A-8031-4914-BD73-3459A91B98A9"),
14 helpstring("_ICalcPiEvents Interface") ]
15 __interface _ICalcPiEvents {
16};
17
18...
This looks a lot like IDL, but it’s not; the
__interface
keyword is another compiler extension that
lets you define IDL interfaces directly in your C++ code. The
attributes on the interface provide the rest of the information
(IID, helpstring, and so on) that ends up in the generated IDL
file.
Next up is the class definition for our COM object:
1// CalcPi.h
2...
3// CCalcPi
4
5[ coclass,
6 default(ICalcPi, _ICalcPiEvents),
7 threading(apartment),
8 support_error_info("ICalcPi"),
9 event_source(com),
10 vi_progid("PiSvr.CalcPi"),
11 progid("PiSvr.CalcPi.1"),
12 version(1.0),
13 uuid("A892A09D-98C9-4AD4-98C5-769F7743F204"),
14 helpstring("CalcPi Class") ]
15class ATL_NO_VTABLE CCalcPi :
16 public ICalcPi {
17public:
18 CCalcPi() { }
19
20 __event __interface _ICalcPiEvents;
21
22 DECLARE_PROTECT_FINAL_CONSTRUCT()
23
24 HRESULT FinalConstruct() { return S_OK; }
25 void FinalRelease() { }
26
27};
Notice the shortness of the base class list. The
only thing explicitly mentioned is the interface. Even though
ICalcPi
is a dual interface, there’s no
IDispatchImpl
in sight.
I also cooked up a simple console application as a test client. This client works just like any other COM client application:
1#include <iostream>
2
3#import "../PiSvr/Debug/PiSvr.dll" no_namespace
4
5using namespace std;
6
7class CoInit {
8public:
9 CoInit( ) { CoInitialize( 0 ); }
10 ~CoInit( ) { CoUninitialize( ); }
11};
12
13void _tmain(int argc, _TCHAR* argv[]) {
14 CoInit coInit;
15
16 ICalcPiPtr spPiCalc( __uuidof( CCalcPi ) );
17
18 spPiCalc->Digits = 20;
19 _bstr_t bstrPi = spPiCalc->CalcPi();
20
21 wcout << L"Pi to " << spPiCalc->Digits << " digits is " <<
22 bstrPi << endl;
23}
The output of this application, shown in Figure D.1, indicates that the COM object is properly registered and working.
Figure D.1. Calling an attributed COM object

Visual Studio 2005 supplies a large number of attributes, and they’re all fairly well documented in MSDN. [1] Instead of paraphrasing the documentation, let’s take a look at how attributes work.
To do their magic, ATL attributes generate a ton of code within the compiler. It sure would be nice to see what code they actually are building. Unfortunately, that’s where the trouble starts.
Expanding Attributed Code
The C++ compiler has a compiler switch
(/Fx
) that causes the compiler to output merge files that
contain the original source code merged with the injected code.
This is a useful mechanism for exploring and understanding exactly
what the attribute providers are doing under the covers in response
to attributes that we apply to our ATL code. IDL code that the
attribute providers generate is output to a
`<projectname>.idl`. Merge files for objects appear
as``_<classname>.mrg.h`` and
_<classname>.mrg.cpp
. Merged code for the server as
a whole is generated in _<projectname>.mrg.cpp
. The
/Fx
switch is activated from the Expand Attributed Source
property, located in the project property pages, shown in Figure D.2.
Figure D.2. Expand Attributed Source

Flipping this switch and compiling the project
results in this code in the _PiSvr.mrg.h
file:
1// CCalcPi
2
3[ coclass,
4 default(ICalcPi, _ICalcPiEvents),
5 threading(apartment),
6 support_error_info("ICalcPi"),
7 event_source(com),
8 vi_progid("PiSvr.CalcPi"),
9 progid("PiSvr.CalcPi.1"),
10 version(1.0),
11 uuid("A892A09D-98C9-4AD4-98C5-769F7743F204"),
12 helpstring("CalcPi Class") ]
13class ATL_NO_VTABLE CCalcPi :
14 public ICalcPi
15,
16 /*+++ Added Baseclass */ public ISupportErrorInfo
17,
18 /*+++ Added Baseclass */ public
19IProvideClassInfo2Impl<&__uuidof(CCalcPi),
20 &__uuidof(::_ICalcPiEvents)>
21{
22public:
23 CCalcPi() { }
24
25 __event __interface _ICalcPiEvents;
26
27 DECLARE_PROTECT_FINAL_CONSTRUCT()
28
29 HRESULT FinalConstruct() { return S_OK; }
30 void FinalRelease() { }
31
32public:
33
34 // IDispatch methods
35 virtual HRESULT STDMETHODCALLTYPE ICalcPi::Invoke(
36 /* [in] */ DISPID dispIdMember,
37 /* [in] */ REFIID riid,
38 /* [in] */ LCID lcid,
39 /* [in] */ WORD wFlags,
40 /* [out][in] */ DISPPARAMS *pDispParams,
41 /* [out] */ VARIANT *pVarResult,
42 /* [out] */ EXCEPINFO *pExcepInfo,
43 /* [out] */ UINT *puArgErr) {
44// Implementation removed for clarity
45}
46
47virtual HRESULT STDMETHODCALLTYPE ICalcPi::GetIDsOfNames(
48 /* [in] */ REFIID riid,
49 /* [size_is][in] */ LPOLESTR *rgszNames,
50 /* [in] */ UINT cNames,
51 /* [in] */ LCID lcid,
52 /* [size_is][out] */ DISPID *rgDispId) {
53// Implementation removed for clarity
54}
55
56HRESULT TypeInfoHelper(REFIID iidDisp, LCID /*lcid*/,
57 ITypeInfo** ppTypeInfo) {
58...
59}
60
61virtual HRESULT STDMETHODCALLTYPE ICalcPi::GetTypeInfoCount(
62 unsigned int* pctinfo) {
63...
64}
65
66virtual HRESULT STDMETHODCALLTYPE ICalcPi::GetTypeInfo(
67 unsigned int iTInfo, LCID lcid, ITypeInfo** ppTInfo) {
68...
69}
70
71BEGIN_CONNECTION_POINT_MAP(CCalcPi)
72 CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink)
73 CONNECTION_POINT_ENTRY(__uuidof(::_ICalcPiEvents))
74END_CONNECTION_POINT_MAP()
75
76// Registration implementation
77
78BEGIN_COM_MAP(CCalcPi)
79 COM_INTERFACE_ENTRY(ICalcPi)
80 COM_INTERFACE_ENTRY(IDispatch)
81 COM_INTERFACE_ENTRY(IConnectionPointContainer)
82 COM_INTERFACE_ENTRY(ISupportErrorInfo)
83 COM_INTERFACE_ENTRY(IProvideClassInfo2)
84 COM_INTERFACE_ENTRY(IProvideClassInfo)
85END_COM_MAP()
86 STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid) {
87 ...
88 }
89
90};
91
92OBJECT_ENTRY_AUTO(__uuidof(CCalcPi), CCalcPi)
The attribute providers
generate code to implement Registry updates for this object. They
also injected the COM_MAP
, CONNECTION_MAP
,
InterfaceSupportsErrorInfo
method, and all the other
boilerplates that would normally have shown up in our header
file.
There’s just one problem: This output is wrong.
Look at the base class list. Where’s
CComObjectRootEx
? CComCoClass
? Also, the
attributes are still in the file. You cannot feed the generated
.mrg
files into a C++ compiler and get code that works.
Unfortunately, this limitation [2] makes it hard to trust the
code that attributes generate because there’s no way to actually
see all of it.
/Fx
did generate correct output in Visual Studio.NET 2002
and 2003; it was broken in the 2005 release. Microsoft’s response
was that the switch was supposed to be only a quick check, and that
it will not be fixed. See `
http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=a09357fe-32fa-43a1-9223-95bc2c38765e <http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=a09357fe-32fa-43a1-9223-95bc2c38765e>`__
(http://tinysells.com/59).
The Future of Attributed ATL
When ATL 7 was released, Attributed ATL was a big new feature. All the wizards defaulted to generating attributed code, all the new help was about attributes, and everyone swore that this was the wave of the future. There was even talk of someday opening up the compiler so that people could create their own attribute providers.
Things didn’t work out that way. Attributed code suffers from several problems:
Difficulty debugging. If there’s a problem in the code generated by the attribute or its interaction with your code, it’s very difficult to debug.
Lack of control. Attributes do a lot of work for you behind the scenes, but if you don’t like the code the attribute is generating, you’re out of luck. Because C++ is all about powerful tools that give you fine control over your code, this doesn’t work so well for most C++ developers.
Bugs. Some areas in attributed code (particularly around connection points) are just plain buggy.
It’s very telling that in Visual Studio 2005, the ATL wizards now default to nonattributed code. The only part of ATL that requires attributed code is the Web Services portions of ATL Server (see Chapter 13, “Hello, ATL Server,” for a discussion). Microsoft seems to be moving away from the attributed ATL experiment. It looks like the ATL community is stuck with templates, macros, IDL files, and RGS scripts.
Summary
Attributed ATL was an attempt to simplify the use of ATL through compiler extensions. Instead of using base classes, and scattering information through many different types of files (C++, IDL, RGS, and so on), attributed ATL used IDL-like attributes to provide extra information attached to the various type declarations in your ATL code.
At compile time, the attributes manipulate the compiler parse tree to add base classes, ATL maps, and sometimes entire interface implementations. Attributes also provide information that gets passed through to the MIDL compiler. This enables you to centralize all the information about a COM class with the C++ class declaration instead of having some in the class header, some in the IDL file, and some in RGS scripts.
Attributes were an interesting experiment, but it appears that Microsoft has abandoned the idea because the ATL wizards no longer default to attributed code.