Implémenter une collection STL-Basée
ATL fournit l'interface d' ICollectionOnSTLImpl pour vous permettre d'implémenter rapidement les interfaces de collection basées sur de (STL) Standard Template Library) dans vos objets.Pour comprendre cette classe s'exécute, vous allez via un exemple simple (ci-dessous) qui utilise cette classe pour implémenter en lecture seule des clients Automation visés par collection.
L'exemple de code est d' ATLCollections.
Pour exécuter cette procédure, vous voulez bien :
générez un nouvel objet simple.
Modifiez le fichier IDL pour l'interface générée.
créez cinq typedefs décrivant comment les éléments de collection sont stockés et comment ils sont exposés aux clients via des interfaces COM.
Créez deux typedefs des classes de stratégie de copie.
Créez les typedefs pour les implémentations d'énumérateur et de collection.
Modifiez le code C++ généré par l'Assistant pour utiliser le typedef de collection.
Ajoutez le code pour remplir une collection.
Générer un nouvel objet simple
Créez un projet, garantissant que la zone d'attributs dans les paramètres d'application est supprimé.Utilisez ATL ajoute la boîte de dialogue de classe et ajoutez l'assistant simple d'objet pour générer un objet simple appelé Words.Assurez-vous qu'une interface double appelée IWords est générée.Les objets de la classe générée seront utilisés pour représenter une collection de mots (autrement dit, chaînes).
Modifier le fichier IDL
Maintenant, ouvrez le fichier IDL et ajoutez les trois propriétés nécessaires pour transformer IWords en interface de la 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 pour une interface de la collection en lecture seule conçue avec les clients Automation à l'esprit.Les commentaires comptabilisés dans cette définition d'interface correspondent aux commentaires ci-dessous :
Les interfaces de collection sont généralement doubles car les clients Automation accède à la propriété d' _NewEnum via IDispatch::Invoke.Toutefois, les clients Automation peuvent accéder aux méthodes restantes via le pointeur vtable, les interfaces doubles sont préférables aux dispinterfaces.
Si une interface double ou une dispinterface n'est pas étendue au moment de l'exécution (autrement dit, vous fournirez ne pas des méthodes supplémentaires ou des propriétés via IDispatch::Invoke), vous devez appliquer l'attribut de nonextensible à la définition.Cet attribut permet aux clients Automation d'exécuter la vérification complète de code au moment de la compilation.Dans ce cas, l'interface ne doit pas être étendue.
Le DISPID approprié est important si vous souhaitez que les clients Automation pour pouvoir utiliser cette propriété.(Notez qu'il n'existe qu'un trait de soulignement dans DISPID_NEWENUM.)
Vous pouvez fournir une valeur comme DISPID de la propriété de Élément .Toutefois, Élément utilise généralement DISPID_VALUE pour en faire la propriété par défaut de la collection.Cela permet aux clients Automation pour faire référence à la propriété sans la nommer explicitement.
Le type de données utilisé pour la valeur de retour de la propriété de Élément est le type de l'élément enregistré dans la collection en ce qui concerne les clients COM.L'interface retourne des chaînes, vous devez utiliser le type chaîne standard COM, BSTR.Vous pouvez stocker les données dans un format différent en interne comme vous le constaterez bientôt.
La valeur utilisée pour le DISPID de la propriété de Nombre est complètement arbitraire.Il n'existe aucun DISPID standard pour cette propriété.
Créer des typedefs pour le stockage et l'exposition
Une fois que l'interface de la collection est définie, vous devez décider comment les données sont stockées, et comment les données sont exposées via l'énumérateur.
Les réponses à ces questions peuvent être effectuées sous la forme d'un nombre quelconque de typedef, que vous pouvez ajouter vers le haut du fichier d'en-tête pour votre classe nouvellement créé :
// 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 stockerez les données sous forme std::vector de std::stringS.std::vector est une classe de conteneur STL qui se comporte comme un tableau managé.std::string est la classe de chaîne de la bibliothèque C++ standard.Ces classes facilitent à utiliser une collection de chaînes.
Étant donné que la prise en charge de Visual Basic est essentiel à la réussite de cette interface, l'énumérateur retourné par la propriété d' _NewEnum doit prendre en charge l'interface d' IEnumVARIANT .c'est la seule interface d'énumérateur comprise par Visual Basic.
Créer des typedefs des classes de stratégie de copie
Les typedefs que vous avez créés jusqu'à présent fournissent toutes les informations vous devez créer d'autres typedefs 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 d' GenericCopy définie dans VCUE_Copy.h et VCUE_CopyString.h de l'exemple d' ATLCollections .Vous pouvez utiliser cette classe dans un autre code, mais vous devrez peut-être définir d'autres spécialisations d' GenericCopy pour prendre en charge des types de données utilisés dans vos propres collections.Pour plus d'informations, consultez Classes de stratégie de copie ATL.
Créer des typedefs de l'énumération et la collection
Maintenant tous les paramètres de modèle nécessaires pour spécialiser les classes d' CComEnumOnSTL et d' ICollectionOnSTLImpl pour cette situation ont été fournis sous la forme de typedef.Pour simplifier l'utilisation des spécialisations, créez deux définitions de types supplémentaires comme indiqué ci-dessous :
typedef CComEnumOnSTL< EnumeratorInterface, &__uuidof(EnumeratorInterface), EnumeratorExposedType, EnumeratorCopyType, ContainerType > EnumeratorType;
typedef ICollectionOnSTLImpl< CollectionInterface, ContainerType, CollectionExposedType, CollectionCopyType, EnumeratorType > CollectionType;
Maintenant CollectionType est un synonyme d'une spécialisation d' ICollectionOnSTLImpl qui implémente l'interface d' IWords définie précédemment et fournit un énumérateur qui prend en charge IEnumVARIANT.
Modifier le code généré par l'Assistant
Maintenant vous devez dériver CWords de l'implémentation d'interface représentée par le typedef d' CollectionType plutôt qu' 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.
Ajouter le code pour remplir une collection
La seule opération qui reste est de remplir un vecteur à des données.Dans cet exemple simple, vous pouvez ajouter des 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");
}
Maintenant, vous pouvez tester le code avec le client de votre choix.