Domande frequenti sulla programmazione con attributi

Che cos'è un HRESULT?

HRESULT è un tipo di dati semplice che viene spesso usato come valore restituito dagli attributi e ATL in generale. Nella tabella seguente vengono descritti i vari valori. Altri valori sono contenuti nel file di intestazione winerror.h.

Nome Descrizione valore
S_OK Operazione riuscita 0x00000000
E_UNEXPECTED Errore imprevisto 0x8000FFFF
E_NOTIMPL Non implementato 0x80004001
E_OUTOFMEMORY Impossibile allocare la memoria necessaria 0x8007000E
E_INVALIDARG Uno o più argomenti non sono validi 0x80070057
E_NOINTERFACE Nessuna interfaccia di questo tipo supportata 0x80004002
E_POINTER Puntatore non valido 0x80004003
E_HANDLE Handle non valido 0x80070006
E_ABORT Operazione interrotta 0x80004004
E_FAIL Errore non specificato 0x80004005
E_ACCESSDENIED Errore generale di accesso negato 0x80070005

Quando è necessario specificare il nome del parametro per un attributo?

Nella maggior parte dei casi, se l'attributo ha un singolo parametro, tale parametro viene denominato. Questo nome non è obbligatorio quando si inserisce l'attributo nel codice. Ad esempio, l'utilizzo seguente dell'attributo aggregabile :

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

è esattamente uguale a:

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

Tuttavia, gli attributi seguenti dispongono di singoli parametri senza nome:

È possibile usare i commenti in un blocco di attributi?

È possibile usare commenti a riga singola e a più righe all'interno di un blocco di attributi. Tuttavia, non è possibile utilizzare uno stile di commento tra parentesi che contengono i parametri di un attributo.

È consentito quanto segue:

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

Non è consentito quanto segue:

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

Come interagiscono gli attributi con l'ereditarietà?

È possibile ereditare classi con attributi e non con attributi da altre classi, che possono essere attribuite o meno. Il risultato della derivazione da una classe con attributi equivale alla derivazione da tale classe dopo che il provider di attributi ha trasformato il codice. Gli attributi non vengono trasmessi alle classi derivate tramite l'ereditarietà C++. Un provider di attributi trasforma solo il codice nelle vicinanze degli attributi.

Come è possibile usare gli attributi in un progetto ATL non con attributi?

Potrebbe essere disponibile un progetto ATL non con attributi, che include un file con estensione idl e potrebbe essere necessario iniziare ad aggiungere oggetti con attributi. In questo caso, usare l'Aggiunta guidata classe per fornire il codice.

Come è possibile usare un file con estensione idl in un progetto con attributi?

Potrebbe essere presente un file con estensione idl che si vuole usare nel progetto con attributi ATL. In questo caso, usare l'attributo importidl, compilare il file con estensione idl in un file con estensione h (vedere le pagine delle proprietà MIDL nella finestra di dialogo Pagine delle proprietà del progetto) e quindi includere il file con estensione h nel progetto.

È possibile modificare il codice inserito da un attributo?

Alcuni attributi inseriscono il codice nel progetto. È possibile visualizzare il codice inserito usando l'opzione del compilatore /Fx . È anche possibile copiare il codice dal file inserito e incollarlo nel codice sorgente. In questo modo è possibile modificare il comportamento dell'attributo. Tuttavia, potrebbe essere necessario modificare anche altre parti del codice.

L'esempio seguente è il risultato della copia del codice inserito in un file di codice sorgente:

// 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() {}

Come è possibile inoltrare dichiarare un'interfaccia con attributi?

Se si intende creare una dichiarazione forward di un'interfaccia con attributi, è necessario applicare gli stessi attributi alla dichiarazione di inoltro applicata alla dichiarazione di interfaccia effettiva. È inoltre necessario applicare l'attributo export alla dichiarazione forward.

È possibile usare attributi in una classe derivata da una classe che usa anche attributi?

No, l'uso di attributi in una classe derivata da una classe che usa anche attributi non è supportato.