Реализация коллекции на основе стандартной библиотеки C++
ATL предоставляет ICollectionOnSTLImpl
интерфейс, позволяющий быстро реализовать интерфейсы коллекции на основе стандартной библиотеки C++ на объектах. Чтобы понять, как работает этот класс, вы будете работать с простым примером (ниже), который использует этот класс для реализации коллекции только для чтения, направленной на клиентов службы автоматизации.
Пример кода состоит из примера ATLCollections.
Чтобы выполнить эту процедуру, выполните следующие действия.
Измените IDL-файл для созданного интерфейса.
Создайте пять типов , описывающие хранение элементов коллекции и способ их предоставления клиентам через COM-интерфейсы.
Создайте два типа для классов политик копирования.
Создайте типдефы для реализаций перечислителя и коллекции.
Измените созданный мастером код C++ для использования типа коллекции.
Создание нового простого объекта
Создайте проект, гарантируя очистку поля "Атрибуты" в разделе "Параметры приложения". Используйте диалоговое окно добавления класса 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);
};
Это стандартная форма для интерфейса коллекции только для чтения, разработанного с учетом клиентов службы автоматизации. Нумерованные комментарии в этом определении интерфейса соответствуют приведенным ниже комментариям:
Интерфейсы коллекции обычно двойны, так как клиенты службы автоматизации обращаются к свойству
_NewEnum
.IDispatch::Invoke
Однако клиенты службы автоматизации могут получить доступ к оставшимся методам с помощью vtable, поэтому двойные интерфейсы предпочтительнее для отключения интерфейса.Если двойной интерфейс или dispinterface не будет расширен во время выполнения (т. е. вы не предоставите дополнительные методы или свойства через
IDispatch::Invoke
), следует применить атрибут nonextensible к определению. Этот атрибут позволяет клиентам службы автоматизации выполнять полную проверку кода во время компиляции. В этом случае интерфейс не должен быть расширен.Правильное значение DISPID важно, если требуется, чтобы клиенты службы автоматизации могли использовать это свойство. (Обратите внимание, что в DISPID_NEWENUM есть только одно подчеркивание.)
Любое значение можно указать как DISPID
Item
свойства.Item
Однако обычно использует DISPID_VALUE, чтобы сделать его свойством по умолчанию коллекции. Это позволяет клиентам службы автоматизации ссылаться на свойство без явного именования.Тип данных, используемый для возвращаемого значения
Item
свойства, является типом элемента, хранящегося в коллекции в отношении клиентов COM. Интерфейс возвращает строки, поэтому следует использовать стандартный тип строки COM, BSTR. Данные можно хранить внутри другого формата, как вы увидите в ближайшее время.Значение, используемое для 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