Compartilhar via


Como implementar uma coleção baseada na Biblioteca C++ Standard

A ATL fornece a interface ICollectionOnSTLImpl para permitir que você implemente rapidamente interfaces de coleção baseadas na Biblioteca C++ Standard em seus objetos. Para entender como essa classe funciona, você trabalhará em um exemplo simples (abaixo) que usa essa classe para implementar uma coleção somente leitura voltada para clientes de Automação.

O código de exemplo é do Exemplo de ATLCollections.

Para concluir este procedimento, você irá:

Gerando um Novo Objeto Simples

Crie um novo projeto, garantindo que a caixa Atributos em Configurações de Aplicativo esteja desmarcada. Use a caixa de diálogo Adicionar Classe ATL e Adicionar Assistente de Objeto Simples para gerar um Objeto Simples chamado Words. Verifique se uma interface dupla chamada IWords é gerada. Objetos da classe gerada serão usados para representar uma coleção de palavras (ou seja, cadeias de caracteres).

Editando o arquivo IDL

Agora, abra o arquivo IDL e adicione as três propriedades necessárias para se transformar IWords em uma interface de coleção somente leitura, conforme mostrado abaixo:

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

};

Esse é o formulário padrão para uma interface de coleção somente leitura projetada para clientes de Automação. Os comentários numerados nesta definição de interface correspondem aos comentários abaixo:

  1. As interfaces de coleção geralmente são duplas porque os clientes de Automação acessam a propriedade _NewEnum por meio de IDispatch::Invoke. No entanto, os clientes de Automação podem acessar os métodos restantes por meio da vtable, portanto, interfaces duplas são preferíveis a dispinterfaces.

  2. Se uma interface dupla ou dispinterface não for estendida no tempo de execução (ou seja, você não fornecerá métodos nem propriedades adicionais por meio de IDispatch::Invoke), você deverá aplicar o atributo nonextensible à sua definição. Esse atributo permite que os clientes de Automação executem a verificação completa do código no tempo de compilação. Nesse caso, a interface não deve ser estendida.

  3. O DISPID correto é importante se você quiser que os clientes de Automação possam usar essa propriedade. (Observe que há apenas um sublinhado em DISPID_NEWENUM.)

  4. Você pode fornecer qualquer valor como DISPID da propriedade Item. No entanto, Item normalmente usa DISPID_VALUE para torná-la a propriedade padrão da coleção. Isso permite que os clientes de Automação se refiram à propriedade sem nomeá-la explicitamente.

  5. O tipo de dados usado para o valor retornado da propriedade Item é o tipo do item armazenado na coleção no que diz respeito aos clientes COM. A interface retorna cadeias de caracteres, portanto, você deve usar o tipo de cadeia de caracteres COM padrão, BSTR. Você pode armazenar os dados em um formato diferente internamente, como você verá em breve.

  6. O valor usado para o DISPID da propriedade Count é completamente arbitrário. Não há DISPID padrão para essa propriedade.

Criando typedefs para armazenamento e exposição

Depois que a interface de coleção for definida, você precisará decidir como os dados serão armazenados e como os dados serão expostos por meio do enumerador.

As respostas a essas perguntas podem ser fornecidas na forma de um número de typedefs, que você pode adicionar perto da parte superior do arquivo de cabeçalho para sua classe recém-criada:

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

Nesse caso, você armazenará os dados como um std::vector de std::string. std::vector é uma classe de contêiner da Biblioteca C++ Standard que se comporta como uma matriz gerenciada. std::string é a classe de cadeia de caracteres da Biblioteca C++ Standard. Essas classes facilitam o trabalho com uma coleção de cadeias de caracteres.

Como o suporte do Visual Basic é fundamental para o sucesso dessa interface, o enumerador retornado pela propriedade _NewEnum deve dar suporte à interface IEnumVARIANT. Essa é a única interface de enumerador compreendida pelo Visual Basic.

Criando typeDefs para classes de política de cópia

Os typedefs criados até agora fornecem todas as informações necessárias para criar mais typedefs para as classes de cópia que serão usadas pelo enumerador e pela coleção:

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

Neste exemplo, você pode usar a classe personalizada GenericCopy definida em VCUE_Copy.h e VCUE_CopyString.h do exemplo ATLCollections. É possível usar essa classe em outro código, mas talvez seja necessário definir outras especializações de GenericCopy para dar suporte a tipos de dados usados em suas próprias coleções. Para obter mais informações, consulte Classes de política de cópia da ATL.

Criando typedefs para enumeração e coleção

Agora, todos os parâmetros de modelo necessários para especializar as classes CComEnumOnSTL e ICollectionOnSTLImpl para esta situação foram fornecidos na forma de typedefs. Para simplificar o uso das especializações, crie mais dois typedefs, conforme mostrado abaixo:

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

Agora CollectionType é um sinônimo para uma especialização de ICollectionOnSTLImpl que implementa a interface IWords definida anteriormente e que fornece um enumerador que dá suporte a IEnumVARIANT.

Editando o código gerado pelo assistente

Agora você deve derivar CWords da implementação da interface representada pelo typedef CollectionType em vez de IWords, conforme mostrado abaixo:

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.

Adicionando código para popular a coleção

A única coisa que resta é preencher o vetor com dados. Neste exemplo simples, você pode adicionar algumas palavras à coleção no construtor da classe:

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

Agora, você pode testar o código com o cliente de sua escolha.

Confira também

Coleções e Recenseadores
Exemplo de ATLCollections
Classes de política de cópia da ATL