Compartir a través de


Implementar una colección basada en la biblioteca estándar de C++

ATL proporciona la interfaz ICollectionOnSTLImpl para permitirle implementar rápidamente interfaces de colección, basadas en la biblioteca estándar de C++, en los objetos. Para comprender cómo funciona esta clase, trabajará con un ejemplo sencillo (aparece abajo) que usa esta clase para implementar una colección de solo lectura destinada a clientes de Automation.

El código de ejemplo procede del ejemplo de ATLCollections.

Para completar este procedimiento, necesita:

Generación de un nuevo Objeto simple

Cree un nuevo proyecto y asegúrese de que el cuadro Atributos, en la configuración de la aplicación, esté desactivado. Use el cuadro de diálogo Agregar clase, en ATL, y el Asistente para agregar objetos simples para generar un Objeto simple denominado Words. Asegúrese de que se genera una interfaz dual denominada IWords. Los objetos de la clase generada se usarán para representar una colección de palabras (es decir, cadenas).

Edición del archivo IDL

Ahora, abra el archivo IDL y agregue las tres propiedades necesarias para convertir IWords en una interfaz de colección de solo lectura, como se muestra a continuación:

[
   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);

};

Este es el formulario estándar para una interfaz de colección de solo lectura diseñada para los clientes de Automation. Los comentarios numerados de esta definición de interfaz corresponden a los siguientes comentarios:

  1. Las interfaces de colección suelen ser duales porque los clientes de Automation acceden a la propiedad _NewEnum a través de IDispatch::Invoke. Sin embargo, los clientes de Automation pueden acceder a los métodos restantes a través de la tabla virtual, por lo que las interfaces duales son preferibles a las dispinterfaces.

  2. Si una interfaz dual o dispinterface no se extiende en tiempo de ejecución (es decir, no proporciona métodos o propiedades adicionales a través de IDispatch::Invoke), debe aplicar el atributo nonextensible a la definición. Este atributo permite que los clientes de Automation realicen la comprobación de código completa en tiempo de compilación. En este caso, la interfaz no debe extenderse.

  3. El DISPID correcto es importante si desea que los clientes de Automation puedan usar esta propiedad. (Tenga en cuenta que solo hay un carácter de subrayado en DISPID_NEWENUM).

  4. Puede proporcionar cualquier valor como DISPID de la propiedad Item. Sin embargo, Item normalmente usa DISPID_VALUE para convertirlo en la propiedad predeterminada de la colección. Esto permite que los clientes de Automation hagan referencia a la propiedad sin asignarle un nombre explícito.

  5. El tipo de datos utilizado para el valor devuelto de la propiedad Item es el tipo del elemento almacenado en la colección, en lo que respecta a los clientes COM. La interfaz devuelve cadenas, por lo que debe usar el tipo de cadena COM estándar, BSTR. Puede almacenar los datos en un formato diferente internamente, como verá en breve.

  6. El valor utilizado para el DISPID de la propiedad Count es completamente arbitrario. No hay ningún DISPID estándar para esta propiedad.

Creación de typedefs para almacenamiento y exposición

Una vez definida la interfaz de colección, debe decidir cómo se almacenarán los datos y cómo los expondrá el enumerador.

Las respuestas a estas preguntas se pueden proporcionar en forma de una serie de typedefs, que puede agregar cerca de la parte superior del archivo de encabezado para la clase recién creada:

// 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;

En este caso, almacenará los datos como std::vector de std::strings. std::vector es una clase contenedora de la biblioteca estándar de C++ que se comporta como una matriz administrada. std::string es la clase de cadena de la biblioteca estándar de C++. Estas clases facilitan el trabajo con una colección de cadenas.

Dado que la compatibilidad con Visual Basic es clave para el buen funcionamiento de esta interfaz, el enumerador devuelto por la propiedad _NewEnum debe admitir la interfaz IEnumVARIANT. Esta es la única interfaz del enumerador que Visual Basic entiende.

Creación de typedefs para las clases de directiva de copia

Las typedefs que ha creado hasta ahora proporcionan toda la información que necesita para crear typedefs adicionales para las clases de copia que el enumerador y la colección utilizarán:

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

En este ejemplo, puede usar la clase personalizada GenericCopy definida en VCUE_Copy.h y VCUE_CopyString.h del ejemplo de ATLCollections. Puede usar esta clase en otro código, pero es posible que tenga que definir más especializaciones de GenericCopy para admitir tipos de datos usados en sus propias colecciones. Para más información, consulte Clases de directiva de copia de ATL.

Creación de typedefs para enumeración y colección

Ahora todos los parámetros de plantilla necesarios para especializar las clases CComEnumOnSTL y ICollectionOnSTLImpl para esta situación se han proporcionado en forma de typedefs. Para simplificar el uso de las especializaciones, cree dos typedefs más, como se muestra a continuación:

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

Ahora CollectionType es un sinónimo de una especialización de ICollectionOnSTLImpl que implementa la interfaz IWords, definida anteriormente, y proporciona un enumerador que admite IEnumVARIANT.

Edición del código generado por el asistente

Ahora debe derivar CWords de la implementación de interfaz representada por la typedef CollectionType, en vez de por la IWords, como se muestra a continuación:

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.

Agregar código para rellenar la colección

Lo único que queda es rellenar el vector con datos. En este sencillo ejemplo, puede agregar algunas palabras a la colección en el constructor de la clase:

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

Ahora, puede probar el código con el cliente que prefiera.

Consulte también

Colecciones y enumeradores
Ejemplo de ATLCollections
Clases de directivas de copia ATL