Partager via


Implémentation d’une collection basée sur la bibliothèque standard C++

ATL fournit l’interface ICollectionOnSTLImpl pour vous permettre d’implémenter rapidement des interfaces de collection basées sur la bibliothèque standard C++ sur vos objets. Pour comprendre le fonctionnement de cette classe, vous allez utiliser un exemple simple (ci-dessous) qui utilise cette classe pour implémenter une collection en lecture seule destinée aux clients Automation.

L’exemple de code provient de l’exemple ATLCollections.

Pour effectuer cette procédure, vous allez :

Génération d’un nouvel objet simple

Créez un projet, en veillant à ce que la zone Attributs sous Application Paramètres soit effacée. Utilisez la boîte de dialogue Ajouter une classe ATL et l’Assistant Ajouter un objet simple pour générer un objet simple appelé Words. Vérifiez qu’une interface double appelée IWords est générée. Les objets de la classe générée sont utilisés pour représenter une collection de mots (c’est-à-dire des chaînes).

Modification du fichier IDL

Ouvrez maintenant le fichier IDL et ajoutez les trois propriétés nécessaires pour se transformer IWords en interface de collection en lecture seule, comme indiqué ci-dessous :

[
   object,
   uuid(7B3AC376-509F-4068-87BA-03B73ADC359B),
   dual,                                                    // (1)
   nonextensible,                                           // (2)
   pointer_default(unique)
]
interface IWords : IDispatch
{
   [id(DISPID_NEWENUM), propget]                            // (3)
   HRESULT _NewEnum([out, retval] IUnknown** ppUnk);

   [id(DISPID_VALUE), propget]                              // (4)
   HRESULT Item([in] long Index, [out, retval] BSTR* pVal); // (5)

   [id(0x00000001), propget]                                // (6)
   HRESULT Count([out, retval] long* pVal);

};

Il s’agit du formulaire standard d’une interface de collection en lecture seule conçue avec les clients Automation à l’esprit. Les commentaires numérotés dans cette définition d’interface correspondent aux commentaires ci-dessous :

  1. Les interfaces de collection sont généralement doubles, car les clients Automation accèdent à la _NewEnum propriété via IDispatch::Invoke. Toutefois, les clients Automation peuvent accéder aux méthodes restantes via la table virtuelle, de sorte que les interfaces doubles sont préférables aux dispinterfaces.

  2. Si une double interface ou dispinterface ne sera pas étendue au moment de l’exécution (autrement dit, vous ne fournirez pas de méthodes ou de propriétés supplémentaires via IDispatch::Invoke), vous devez appliquer l’attribut nonextensible à votre définition. Cet attribut permet aux clients Automation d’effectuer une vérification complète du code au moment de la compilation. Dans ce cas, l’interface ne doit pas être étendue.

  3. Le DISPID correct est important si vous souhaitez que les clients Automation puissent utiliser cette propriété. (Notez qu’il n’existe qu’un trait de soulignement dans DISPID_NEWENUM.)

  4. Vous pouvez fournir n’importe quelle valeur comme DISPID de la Item propriété. Toutefois, Item utilise généralement DISPID_VALUE pour le rendre la propriété par défaut de la collection. Cela permet aux clients Automation de faire référence à la propriété sans le nommer explicitement.

  5. Le type de données utilisé pour la valeur de retour de la Item propriété est le type de l’élément stocké dans la collection en ce qui concerne les clients COM. L’interface retourne des chaînes. Vous devez donc utiliser le type de chaîne COM standard, BSTR. Vous pouvez stocker les données dans un autre format en interne, car vous verrez bientôt.

  6. La valeur utilisée pour le DISPID de la Count propriété est complètement arbitraire. Il n’existe aucun DISPID standard pour cette propriété.

Création de typesdefs pour Stockage et exposition

Une fois l’interface de collecte définie, vous devez décider de la façon dont les données seront stockées et de la façon dont les données seront exposées via l’énumérateur.

Les réponses à ces questions peuvent être fournies sous la forme d’un certain nombre de typedefs, que vous pouvez ajouter en haut du fichier d’en-tête pour votre classe nouvellement créée :

// Store the data in a vector of std::strings
typedef std::vector< std::string >         ContainerType;

// The collection interface exposes the data as BSTRs
typedef BSTR                               CollectionExposedType;
typedef IWords                             CollectionInterface;

// Use IEnumVARIANT as the enumerator for VB compatibility
typedef VARIANT                            EnumeratorExposedType;
typedef IEnumVARIANT                       EnumeratorInterface;

Dans ce cas, vous allez stocker les données sous la forme d’un std ::vector de std ::strings. std ::vector est une classe de conteneur de bibliothèque standard C++ qui se comporte comme un tableau managé. std ::string est la classe de chaîne de la bibliothèque standard C++. Ces classes facilitent l’utilisation d’une collection de chaînes.

Étant donné que la prise en charge de Visual Basic est essentielle à la réussite de cette interface, l’énumérateur retourné par la _NewEnum propriété doit prendre en charge l’interface IEnumVARIANT . Il s’agit de la seule interface d’énumérateur comprise par Visual Basic.

Création de typedefs pour les classes de stratégie de copie

Les typesdefs que vous avez créés jusqu’à présent fournissent toutes les informations dont vous avez besoin pour créer d’autres typesdefs pour les classes de copie qui seront utilisées par l’énumérateur et la collection :

// Typedef the copy classes using existing typedefs
typedef VCUE::GenericCopy<EnumeratorExposedType, ContainerType::value_type> EnumeratorCopyType;
typedef VCUE::GenericCopy<CollectionExposedType, ContainerType::value_type> CollectionCopyType;

Dans cet exemple, vous pouvez utiliser la classe personnalisée GenericCopy définie dans VCUE_Copy.h et VCUE_CopyString.h à partir de l’exemple ATLCollections . Vous pouvez utiliser cette classe dans d’autres codes, mais vous devrez peut-être définir d’autres spécialisations pour prendre en charge les types de GenericCopy données utilisés dans vos propres collections. Pour plus d’informations, consultez les classes de stratégie de copie ATL.

Création de typedefs pour l’énumération et la collection

À présent, tous les paramètres de modèle nécessaires pour spécialiser les classes et ICollectionOnSTLImpl les CComEnumOnSTL classes de cette situation ont été fournis sous la forme de typesdefs. Pour simplifier l’utilisation des spécialisations, créez deux typesdefs supplémentaires, comme indiqué ci-dessous :

typedef CComEnumOnSTL< EnumeratorInterface, &__uuidof(EnumeratorInterface), EnumeratorExposedType, EnumeratorCopyType, ContainerType > EnumeratorType;
typedef ICollectionOnSTLImpl< CollectionInterface, ContainerType, CollectionExposedType, CollectionCopyType, EnumeratorType > CollectionType;

Il s’agit maintenant CollectionType d’un synonyme d’une spécialisation qui ICollectionOnSTLImpl implémente l’interface IWords définie précédemment et fournit un énumérateur qui prend en charge IEnumVARIANT.

Modification du code généré par l’Assistant

Vous devez maintenant dériver CWords de l’implémentation de l’interface représentée par le CollectionType typedef plutôt que IWords, comme indiqué ci-dessous :

class ATL_NO_VTABLE CWords :
   public CComObjectRootEx<CComSingleThreadModel>,
   public CComCoClass<CWords, &CLSID_Words>,
   // 'CollectionType' replaces 'IWords' in next line
   public IDispatchImpl<CollectionType, &IID_IWords, &LIBID_NVC_ATL_COMLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
DECLARE_REGISTRY_RESOURCEID(IDR_WORDS)


BEGIN_COM_MAP(CWords)
   COM_INTERFACE_ENTRY(IWords)
   COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// Remainder of class declaration omitted.

Ajout de code pour remplir la collection

La seule chose qui reste est de remplir le vecteur avec des données. Dans cet exemple simple, vous pouvez ajouter quelques mots à la collection dans le constructeur pour la classe :

CWords()
{
    m_coll.push_back("this");
    m_coll.push_back("is");
    m_coll.push_back("a");
    m_coll.push_back("test");
}

Vous pouvez maintenant tester le code avec le client de votre choix.

Voir aussi

Collections et énumérateurs
Exemple ATLCollections
Classes de stratégies de copie ATL