Implementando uma coleção baseada na biblioteca padrão C++
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á:
Editar o arquivo IDL para a interface gerada.
Criar cinco typedefs que descrevem como os itens de coleção são armazenados e como eles serão expostos aos clientes por meio de interfaces COM.
Criar typedefs para as implementações de enumerador e coleção.
Editar o código C++ gerado pelo assistente para usar o typedef de coleção.
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:
As interfaces de coleção geralmente são duplas porque os clientes de Automação acessam a propriedade
_NewEnum
por meio deIDispatch::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.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.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.)
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.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.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