Attribute Programming FAQ

 

The latest version of this topic can be found at Attribute Programming FAQ.

This topic answers the following frequently asked questions:

  • What is an HRESULT?

  • When do I have to specify the parameter name for an attribute?

  • Can I use comments in an attribute block?

  • How do attributes interact with inheritance?

  • How can I use attributes in a nonattributed ATL project?

  • How can I use an .idl file in an attributed project?

  • Can I modify code that is injected by an attribute?

  • How can I forward declare an attributed interface?

  • Can I use attributes on a class derived from a class that also uses attributes?

What is an HRESULT?

An HRESULT is a simple data type that is often used as a return value by attributes and ATL in general. The following table describes the various values. More values are contained in the header file winerror.h.

Name Description Value
S_OK Operation successful 0x00000000
E_UNEXPECTED Unexpected failure 0x8000FFFF
E_NOTIMPL Not implemented 0x80004001
E_OUTOFMEMORY Failed to allocate necessary memory 0x8007000E
E_INVALIDARG One or more arguments are invalid 0x80070057
E_NOINTERFACE No such interface supported 0x80004002
E_POINTER Invalid pointer 0x80004003
E_HANDLE Invalid handle 0x80070006
E_ABORT Operation aborted 0x80004004
E_FAIL Unspecified failure 0x80004005
E_ACCESSDENIED General access denied error 0x80070005

When do I have to specify the parameter name for an attribute?

In most cases, if the attribute has a single parameter, that parameter is named. This name is not required when inserting the attribute in your code. For example, the following usage of the aggregatable attribute:

[coclass, aggregatable(value=allowed)]  
class CMyClass  
{  
// The class declaration  
};  

is exactly the same as:

[coclass, aggregatable(allowed)]  
class CMyClass  
{  
// The class declaration  
};  

However, the following attributes have single, unnamed parameters:

call_as case cpp_quote
default defaultvalue defaultvtable
emitidl entry first_is
helpcontext helpfile helpstring
helpstringcontext helpstringdll id
iid_is import importlib
include includelib last_is
length_is max_is no_injected_text
pointer_default pragma restricted
size_is source switch_is
switch_type transmit_as wire_marshal

Can I use comments in an attribute block?

You can use both single-line and multiple-line comments within an attribute block. However, you cannot use either style of comment within the parentheses holding the parameters to an attribute.

The following is allowed:

[ coclass,  
   progid
("MyClass.CMyClass.1"), /* Multiple-line  
                                       comment */  
   threading
("both") // Single-line comment  
]  

The following is disallowed:

[ coclass,  
   progid
("MyClass.CMyClass.1" /* Multiple-line comment */ ),  
   threading
("both" // Single-line comment)  
]  

How do attributes interact with inheritance?

You can inherit both attributed and unattributed classes from other classes, which may themselves be attributed or not. The result of deriving from an attributed class is the same as deriving from that class after the attribute provider has transformed its code. Attributes are not transmitted to derived classes through C++ inheritance. An attribute provider only transforms code in the vicinity of its attributes.

How can I use attributes in a nonattributed ATL project?

You may have a nonattributed ATL project, which has an .idl file, and you may want to start adding attributed objects. In this case, use the Add Class Wizard to provide the code.

How can I use an .idl file in an attributed project?

You may have a .idl file that you want to use in your ATL attributed project. In this case, you would use the importidl attribute, compile the .idl file to a .h file (see the MIDL Property Pages in the project's Property Pages dialog box), and then include the .h file in your project.

Can I modify code that is injected by an attribute?

Some attributes inject code into your project. You can see the injected code by using the /Fx compiler option. It is also possible to copy code from the injected file and paste it into your source code. This allows you to modify the behavior of the attribute. However, you may have to modify other parts of your code as well.

The following sample is the result of copying injected code into a source code file:

// attr_injected.cpp  
// compile with: comsupp.lib  
#define _ATL_ATTRIBUTES 1  
#include <atlbase.h>  
#include <atlcom.h>  
  
[ module(name="MyLibrary") ];  
  
// ITestTest  
[   
   object,  
   uuid("DADECE00-0FD2-46F1-BFD3-6A0579CA1BC4"),  
   dual,  
   helpstring("ITestTest Interface"),  
   pointer_default(unique)  
]  
  
__interface ITestTest : IDispatch {  
   [id(1), helpstring("method DoTest")]   
   HRESULT DoTest([in] BSTR str);  
};  
  
// _ITestTestEvents  
[  
   uuid("12753B9F-DEF4-49b0-9D52-A79C371F2909"),  
   dispinterface,  
   helpstring("_ITestTestEvents Interface")  
]  
  
__interface _ITestTestEvents {  
   [id(1), helpstring("method BeforeChange")] HRESULT BeforeChange([in] BSTR str, [in,out] VARIANT_BOOL* bCancel);  
};  
  
// CTestTest  
[  
   coclass,  
   threading(apartment),  
   vi_progid("TestATL1.TestTest"),  
   progid("TestATL1.TestTest.1"),  
   version(1.0),  
   uuid("D9632007-14FA-4679-9E1C-28C9A949E784"),  
   // this line would be commented out from original file  
   // event_source("com"),  
   // this line would be added to support injected code  
   source(_ITestTestEvents),  
   helpstring("TestTest Class")  
]  
  
class ATL_NO_VTABLE CTestTest : public ITestTest,  
// the following base classes support added injected code  
public IConnectionPointContainerImpl<CTestTest>,  
public IConnectionPointImpl<CTestTest, &__uuidof(::_ITestTestEvents), CComDynamicUnkArray>  
{  
public:  
   CTestTest() {  
   }  
   // this line would be commented out from original file  
   // __event __interface _ITestTestEvents;  
   DECLARE_PROTECT_FINAL_CONSTRUCT()  
   HRESULT FinalConstruct() {  
      return S_OK;  
   }  
  
void FinalRelease() {}  
  
public:  
   CComBSTR m_value;  
   STDMETHOD(DoTest)(BSTR str) {  
      VARIANT_BOOL bCancel = FALSE;  
      BeforeChange(str,&bCancel);  
      if (bCancel) {  
          return Error("Error : Someone don't want us to change the value");  
      }  
  
     m_value =str;  
     return S_OK;  
    }  
// the following was copied in from the injected code.  
HRESULT BeforeChange(::BSTR i1,::VARIANT_BOOL* i2) {  
   HRESULT hr = S_OK;  
   IConnectionPointImpl<CTestTest, &__uuidof(_ITestTestEvents), CComDynamicUnkArray>* p = this;  
   VARIANT rgvars[2];  
   Lock();  
   IUnknown** pp = p->m_vec.begin();  
   Unlock();  
   while (pp < p->m_vec.end()) {  
      if (*pp != NULL) {  
         IDispatch* pDispatch = (IDispatch*) *pp;  
         ::VariantInit(&rgvars[1]);  
         rgvars[1].vt = VT_BSTR;  
         V_BSTR(&rgvars[1])= (BSTR) i1;  
         ::VariantInit(&rgvars[0]);  
         rgvars[0].vt = (VT_BOOL | VT_BYREF);  
         V_BOOLREF(&rgvars[0])= (VARIANT_BOOL*) i2;  
         DISPPARAMS disp = { rgvars, NULL, 2, 0 };  
         VARIANT ret_val;  
         hr = __ComInvokeEventHandler(pDispatch, 1, 1, &disp, &ret_val);  
         if (FAILED(hr))  
            break;  
      }  
      pp++;  
   }  
   return hr;  
}  
  
BEGIN_CONNECTION_POINT_MAP(CTestTest)  
CONNECTION_POINT_ENTRY(__uuidof(::_ITestTestEvents))  
END_CONNECTION_POINT_MAP()  
// end added code section  
  
// _ITestCtrlEvents Methods  
public:  
};  
  
int main() {}  

How can I forward declare an attributed interface?

If you are going to make a forward declaration of an attributed interface, you must apply the same attributes to the forward declaration that you apply to the actual interface declaration. You must also apply the export attribute to your forward declaration.

Can I use attributes on a class derived from a class that also uses attributes?

No, using attributes on a class derived from a class that also uses attributes is not supported.

See Also

Concepts