Поделиться через


Реализация коллекции на основе стандартной библиотеки C++

ATL предоставляет ICollectionOnSTLImpl интерфейс, позволяющий быстро реализовать интерфейсы коллекции на основе стандартной библиотеки C++ на объектах. Чтобы понять, как работает этот класс, вы будете работать с простым примером (ниже), который использует этот класс для реализации коллекции только для чтения, направленной на клиентов службы автоматизации.

Пример кода состоит из примера ATLCollections.

Чтобы выполнить эту процедуру, выполните следующие действия.

Создание нового простого объекта

Создайте проект, гарантируя очистку поля "Атрибуты" в разделе "Параметры приложения". Используйте диалоговое окно добавления класса ATL и мастер добавления простых объектов для создания простого объекта Words. Убедитесь, что вызывается IWords двойной интерфейс. Объекты созданного класса будут использоваться для представления коллекции слов (то есть строк).

Редактирование IDL-файла

Теперь откройте IDL-файл и добавьте три свойства, необходимые для включения IWords в интерфейс коллекции только для чтения, как показано ниже:

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

};

Это стандартная форма для интерфейса коллекции только для чтения, разработанного с учетом клиентов службы автоматизации. Нумерованные комментарии в этом определении интерфейса соответствуют приведенным ниже комментариям:

  1. Интерфейсы коллекции обычно двойны, так как клиенты службы автоматизации обращаются к свойству_NewEnum.IDispatch::Invoke Однако клиенты службы автоматизации могут получить доступ к оставшимся методам с помощью vtable, поэтому двойные интерфейсы предпочтительнее для отключения интерфейса.

  2. Если двойной интерфейс или dispinterface не будет расширен во время выполнения (т. е. вы не предоставите дополнительные методы или свойства через IDispatch::Invoke), следует применить атрибут nonextensible к определению. Этот атрибут позволяет клиентам службы автоматизации выполнять полную проверку кода во время компиляции. В этом случае интерфейс не должен быть расширен.

  3. Правильное значение DISPID важно, если требуется, чтобы клиенты службы автоматизации могли использовать это свойство. (Обратите внимание, что в DISPID_NEWENUM есть только одно подчеркивание.)

  4. Любое значение можно указать как DISPID Item свойства. Item Однако обычно использует DISPID_VALUE, чтобы сделать его свойством по умолчанию коллекции. Это позволяет клиентам службы автоматизации ссылаться на свойство без явного именования.

  5. Тип данных, используемый для возвращаемого значения Item свойства, является типом элемента, хранящегося в коллекции в отношении клиентов COM. Интерфейс возвращает строки, поэтому следует использовать стандартный тип строки COM, BSTR. Данные можно хранить внутри другого формата, как вы увидите в ближайшее время.

  6. Значение, используемое для DISPID Count свойства, является полностью произвольным. Для этого свойства нет стандартного DISPID.

Создание typedefs для хранилища и воздействия

После определения интерфейса сбора необходимо решить, как будут храниться данные и как данные будут предоставляться с помощью перечислителя.

Ответы на эти вопросы можно указать в виде ряда типов, которые можно добавить в верхней части файла заголовка для созданного класса:

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

В этом случае данные будут храниться в виде std::vector std ::strings. std::vector — это класс контейнера стандартной библиотеки C++, который ведет себя как управляемый массив. std::string — это строковый класс стандартной библиотеки C++. Эти классы упрощают работу с коллекцией строк.

Так как поддержка Visual Basic жизненно важна для успешного выполнения этого интерфейса, перечислитель, возвращаемый _NewEnum свойством, должен поддерживать IEnumVARIANT интерфейс. Это единственный интерфейс перечислителя, понятный Visual Basic.

Создание typedefs для классов политик копирования

Созданные на данный момент typedefs предоставляют все сведения, необходимые для создания дополнительных типов для классов копирования, которые будут использоваться перечислителем и коллекцией:

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

В этом примере можно использовать пользовательский GenericCopy класс, определенный в VCUE_Copy.h и VCUE_CopyString.h из примера ATLCollections . Этот класс можно использовать в другом коде, но может потребоваться определить дополнительные специализации для поддержки типов данных, используемых GenericCopy в собственных коллекциях. Дополнительные сведения см. в разделе "Классы политик копирования ATL".

Создание typedefs для перечисления и коллекции

Теперь все параметры шаблона, необходимые для специализации CComEnumOnSTL , и ICollectionOnSTLImpl классы для этой ситуации были предоставлены в виде typedefs. Чтобы упростить использование специализаций, создайте еще два типаdefs, как показано ниже:

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

Теперь CollectionType является синонимом специализации ICollectionOnSTLImpl , реализующей IWords интерфейс, определенный ранее, и предоставляет перечислитель, поддерживающий IEnumVARIANT.

Редактирование созданного мастером кода

Теперь необходимо наследовать CWords от реализации интерфейса, представленной типдифом CollectionType , а не IWordsследующим образом:

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.

Добавление кода для заполнения коллекции

Единственное, что остается, заключается в заполнении вектора данными. В этом простом примере можно добавить несколько слов в коллекцию в конструкторе для класса:

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

Теперь вы можете протестировать код с помощью выбранного клиента.

См. также

Коллекции и перечислители
Пример ATLCollections
Классы политики копирования ATL