Udostępnij za pośrednictwem


Implementowanie kolekcji opartej na standardowej bibliotece C++

AtL udostępnia ICollectionOnSTLImpl interfejs umożliwiający szybkie implementowanie standardowych interfejsów kolekcji opartych na bibliotece C++ na obiektach. Aby zrozumieć, jak działa ta klasa, użyjesz prostego przykładu (poniżej), który używa tej klasy do implementowania kolekcji tylko do odczytu skierowanej do klientów usługi Automation.

Przykładowy kod pochodzi z przykładu ATLCollections.

Aby wykonać tę procedurę, wykonaj następujące czynności:

Generowanie nowego prostego obiektu

Utwórz nowy projekt, upewniając się, że pole Atrybuty w obszarze Ustawienia aplikacji zostało wyczyszczone. Użyj okna dialogowego Dodawanie klasy ATL i Kreatora dodawania prostego obiektu, aby wygenerować prosty obiekt o nazwie Words. Upewnij się, że jest generowany podwójny interfejs o nazwie IWords . Obiekty wygenerowanej klasy będą używane do reprezentowania kolekcji wyrazów (czyli ciągów).

Edytowanie pliku IDL

Teraz otwórz plik IDL i dodaj trzy właściwości niezbędne do przekształcenia IWords w interfejs kolekcji tylko do odczytu, jak pokazano poniżej:

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

};

Jest to standardowy formularz interfejsu kolekcji tylko do odczytu zaprojektowanego z myślą o klientach usługi Automation. Ponumerowane komentarze w tej definicji interfejsu odpowiadają poniższym komentarzom:

  1. Interfejsy kolekcji są zwykle podwójne, ponieważ klienci usługi Automation uzyskują _NewEnum dostęp do właściwości za pośrednictwem metody IDispatch::Invoke. Jednak klienci usługi Automation mogą uzyskiwać dostęp do pozostałych metod za pośrednictwem tabeli vtable, dlatego preferowane są dwa interfejsy.

  2. Jeśli podwójny interfejs lub dispinterface nie zostaną rozszerzone w czasie wykonywania (tj. nie udostępnisz dodatkowych metod lub właściwości za pośrednictwem IDispatch::Invokemetody ), należy zastosować atrybut niespodziewalny do definicji. Ten atrybut umożliwia klientom usługi Automation wykonywanie pełnej weryfikacji kodu w czasie kompilacji. W takim przypadku interfejs nie powinien być rozszerzony.

  3. Prawidłowy identyfikator DISPID jest ważny, jeśli chcesz, aby klienci usługi Automation mogli używać tej właściwości. (Należy pamiętać, że w DISPID_NEWENUM istnieje tylko jeden podkreślenie).

  4. Możesz podać dowolną wartość jako DISPID Item właściwości . Item Jednak zazwyczaj używa DISPID_VALUE, aby ustawić ją jako domyślną właściwość kolekcji. Dzięki temu klienci usługi Automation mogą odwoływać się do właściwości bez jawnego nazewnictwa.

  5. Typ danych używany dla wartości Item zwracanej właściwości jest typem elementu przechowywanego w kolekcji, jeśli chodzi o klientów COM. Interfejs zwraca ciągi, więc należy użyć standardowego typu ciągu COM, BSTR. Dane można przechowywać w innym formacie wewnętrznie, ponieważ wkrótce zobaczysz.

  6. Wartość używana dla identyfikatora DISPID Count właściwości jest całkowicie dowolna. Dla tej właściwości nie ma standardowego identyfikatora DISPID.

Tworzenie definicji typów dla magazynu i ekspozycji

Po zdefiniowaniu interfejsu kolekcji należy zdecydować, jak będą przechowywane dane i jak dane będą widoczne za pośrednictwem modułu wyliczającego.

Odpowiedzi na te pytania można podać w postaci wielu definicji typów, które można dodać w górnej części pliku nagłówka dla nowo utworzonej klasy:

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

W takim przypadku dane będą przechowywane jako std::vector std ::strings. std::vector to klasa kontenera biblioteki standardowej języka C++, która zachowuje się jak tablica zarządzana. std::string jest klasą ciągów biblioteki standardowej języka C++. Te klasy ułatwiają pracę z kolekcją ciągów.

Ponieważ obsługa języka Visual Basic jest niezbędna do sukcesu tego interfejsu, moduł wyliczający zwrócony przez _NewEnum właściwość musi obsługiwać IEnumVARIANT interfejs. Jest to jedyny interfejs wyliczający zrozumiały dla języka Visual Basic.

Tworzenie definicji typów dla klas zasad kopiowania

Utworzone definicje typów do tej pory zawierają wszystkie informacje potrzebne do utworzenia dalszych definicji typów dla klas kopiowania, które będą używane przez moduł wyliczający i kolekcję:

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

W tym przykładzie można użyć niestandardowej klasy zdefiniowanej GenericCopy w pliku VCUE_Copy.h i VCUE_CopyString.h z przykładu ATLCollections . Możesz użyć tej klasy w innym kodzie, ale może być konieczne zdefiniowanie dalszych specjalizacji GenericCopy w celu obsługi typów danych używanych we własnych kolekcjach. Aby uzyskać więcej informacji, zobacz ATL Copy Policy Classes (Klasy zasad kopiowania ATL).

Tworzenie definicji typów dla wyliczenia i kolekcji

Teraz wszystkie parametry szablonu niezbędne do specjalizacji CComEnumOnSTL klas i ICollectionOnSTLImpl dla tej sytuacji zostały podane w postaci definicji typów. Aby uprościć korzystanie z specjalizacji, utwórz jeszcze dwie definicje typów, jak pokazano poniżej:

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

Teraz CollectionType jest synonimem specjalizacji ICollectionOnSTLImpl , która implementuje IWords interfejs zdefiniowany wcześniej i udostępnia moduł wyliczający, który obsługuje IEnumVARIANT.

Edytowanie kodu wygenerowanego przez kreatora

Teraz musisz pochodzić CWords z implementacji interfejsu reprezentowanej przez definicję CollectionType typu, a nie IWords, jak pokazano poniżej:

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.

Dodawanie kodu w celu wypełnienia kolekcji

Jedyną rzeczą, która pozostaje, jest wypełnienie wektora danymi. W tym prostym przykładzie można dodać kilka słów do kolekcji w konstruktorze dla klasy:

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

Teraz możesz przetestować kod przy użyciu wybranego klienta.

Zobacz też

Kolekcje i moduły wyliczania
Przykład ATLCollections
Klasy zasad kopii ATL