Forum aux questions sur la programmation par attributs

Qu'est-ce qu'un HRESULT ?

Un HRESULT est un type de données simple qui est souvent utilisé comme valeur de retour par les attributs et l'ATL en général. Le tableau suivant décrit les différentes valeurs. D'autres valeurs sont contenues dans le fichier d'en-tête winerror.h.

Nom Description active
S_OK L’opération a réussi 0x00000000
E_UNEXPECTED Échec inattendu 0x8000FFFF
E_NOTIMPL Non implémenté 0x80004001
E_OUTOFMEMORY Échec de l'allocation de la mémoire nécessaire 0x8007000E
E_INVALIDARG Un ou plusieurs arguments ne sont pas valides 0x80070057
E_NOINTERFACE Interface non prise en charge 0x80004002
E_POINTER Pointeur invalide 0x80004003
E_HANDLE Poignée non valide 0x80070006
E_ABORT L'opération a été interrompue 0x80004004
E_FAIL Échec non spécifié 0x80004005
E_ACCESSDENIED Erreur générale de refus d'accès 0x80070005

Quand dois-je spécifier le nom du paramètre pour un attribut ?

Dans la plupart des cas, si l'attribut a un seul paramètre, ce paramètre est nommé. Ce nom n'est pas nécessaire lorsque vous insérez l'attribut dans votre code. Par exemple, l'utilisation suivante de l'attribut aggregatable :

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

est exactement la même chose que :

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

Cependant, les attributs suivants ont des paramètres uniques, non nommés :

Puis-je utiliser des commentaires dans un bloc d'attributs ?

Vous pouvez utiliser des commentaires sur une ou plusieurs lignes à l'intérieur d'un bloc d'attributs. Cependant, vous ne pouvez pas utiliser l'un ou l'autre style de commentaire à l'intérieur des parenthèses contenant les paramètres d'un attribut.

Ce qui suit est autorisé :

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

Ce qui suit n'est pas autorisé :

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

Comment les attributs interagissent-ils avec l'héritage ?

Vous pouvez hériter de classes attribuées et non attribuées à partir d'autres classes, qui peuvent elles-mêmes être attribuées ou non. Le résultat de la dérivation d'une classe attribuée est le même que la dérivation de cette classe après que le fournisseur d'attributs a transformé son code. Les attributs ne sont pas transmis aux classes dérivées par l'héritage C++. Un fournisseur d'attributs ne transforme le code qu'à proximité de ses attributs.

Comment puis-je utiliser les attributs dans un projet ATL non attribué ?

Il se peut que vous ayez un projet ATL sans attributs, avec un fichier .idl, et que vous souhaitiez commencer à ajouter des objets attribués. Dans ce cas, utilisez l'assistant d'ajout de classe pour fournir le code.

Comment puis-je utiliser un fichier .idl dans un projet attribué ?

Vous disposez peut-être d'un fichier .idl que vous souhaitez utiliser dans votre projet ATL attribué. Dans ce cas, vous devez utiliser l'attribut importidl, compiler le fichier .idl en un fichier .h (voir les pages de propriété MIDL dans la boîte de dialogue Pages de propriété du projet), puis inclure le fichier .h dans votre projet.

Puis-je modifier le code injecté par un attribut ?

Certains attributs injectent du code dans votre projet. Vous pouvez voir le code injecté en utilisant l'option /Fx du compilateur. Il est également possible de copier le code du fichier injecté et de le coller dans votre code source. Cela vous permet de modifier le comportement de l'attribut. Cependant, il se peut que vous deviez également modifier d'autres parties de votre code.

L'exemple suivant est le résultat de la copie du code injecté dans un fichier de code source :

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

Comment puis-je faire une déclaration à terme d'une interface attribuée ?

Si vous allez faire une déclaration de transfert d'une interface attribuée, vous devez appliquer à la déclaration de transfert les mêmes attributs que ceux que vous appliquez à la déclaration de l'interface réelle. Vous devez également appliquer l'attribut export à votre déclaration de transfert.

Puis-je utiliser des attributs sur une classe dérivée d'une classe qui utilise également des attributs ?

Non, l'utilisation d'attributs sur une classe dérivée d'une classe qui utilise également des attributs n'est pas prise en charge.