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:
Editar el archivo IDL para la interfaz generada.
Crear cinco typedefs (definiciones de tipos) que describan cómo se almacenan los elementos de la colección y cómo se exponen a los clientes a través de interfaces COM.
Crear typedefs para las implementaciones de enumerador y colección.
Editar el código de C++ generado por el asistente para usar la typedef de la colección.
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:
Las interfaces de colección suelen ser duales porque los clientes de Automation acceden a la propiedad
_NewEnum
a través deIDispatch::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.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.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).
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.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.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