Condividi tramite


Implementazione di una raccolta basata su libreria standard C++

ATL fornisce l'interfaccia ICollectionOnSTLImpl per consentire di implementare rapidamente interfacce di raccolta basate su librerie standard C++ sugli oggetti. Per comprendere il funzionamento di questa classe, si userà un semplice esempio (di seguito) che usa questa classe per implementare una raccolta di sola lettura destinata ai client di automazione.

Il codice di esempio proviene dall'esempio ATLCollections.

Per completare questa procedura, è necessario:

Generazione di un nuovo oggetto semplice

Creare un nuovo progetto, assicurandosi che la casella Attributi in Impostazioni applicazione sia deselezionata. Utilizzare la finestra di dialogo Aggiungi classe ATL e Aggiunta guidata oggetto semplice per generare un oggetto semplice denominato Words. Assicurarsi che venga generata un'interfaccia doppia denominata IWords . Gli oggetti della classe generata verranno usati per rappresentare una raccolta di parole, ovvero stringhe.

Modifica del file IDL

Aprire ora il file IDL e aggiungere le tre proprietà necessarie per trasformare IWords in un'interfaccia di raccolta di sola lettura, come illustrato di seguito:

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

};

Questo è il formato standard per un'interfaccia di raccolta di sola lettura progettata con i client di Automazione. I commenti numerati in questa definizione di interfaccia corrispondono ai commenti seguenti:

  1. Le interfacce di raccolta sono in genere duali perché i client di Automazione accedono alla _NewEnum proprietà tramite IDispatch::Invoke. Tuttavia, i client di automazione possono accedere ai metodi rimanenti tramite la tabella virtuale, quindi le interfacce doppie sono preferibili alle differenze.

  2. Se in fase di esecuzione non verrà estesa un'interfaccia doppia o un'interfaccia doppia, ovvero non verranno forniti metodi o proprietà aggiuntivi tramite IDispatch::Invoke, è necessario applicare l'attributo nonextensible alla definizione. Questo attributo consente ai client di Automazione di eseguire la verifica completa del codice in fase di compilazione. In questo caso, l'interfaccia non deve essere estesa.

  3. Il diSPID corretto è importante se si vuole che i client di automazione possano usare questa proprietà. Si noti che in DISPID_NEWENUM è presente un solo carattere di sottolineatura.

  4. È possibile specificare qualsiasi valore come DISPID della Item proprietà . Tuttavia, Item usa in genere DISPID_VALUE per impostarlo come proprietà predefinita dell'insieme. In questo modo, i client di automazione possono fare riferimento alla proprietà senza denominarla in modo esplicito.

  5. Il tipo di dati utilizzato per il valore restituito della Item proprietà è il tipo dell'elemento archiviato nella raccolta per quanto riguarda i client COM. L'interfaccia restituisce stringhe, quindi è consigliabile usare il tipo stringa COM standard, BSTR. È possibile archiviare i dati in un formato diverso internamente, come si vedrà a breve.

  6. Il valore utilizzato per il DISPID della Count proprietà è completamente arbitrario. Non esiste alcun DISPID standard per questa proprietà.

Creazione di typedef per l'archiviazione e l'esposizione

Dopo aver definito l'interfaccia di raccolta, è necessario decidere come verranno archiviati i dati e come verranno esposti i dati tramite l'enumeratore.

Le risposte a queste domande possono essere fornite sotto forma di typedef, che è possibile aggiungere nella parte superiore del file di intestazione per la classe appena creata:

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

In questo caso, i dati verranno archiviati come std::vector di std::strings. std::vector è una classe contenitore della libreria standard C++ che si comporta come una matrice gestita. std::string è la classe stringa della libreria standard C++. Queste classi semplificano l'uso di una raccolta di stringhe.

Poiché il supporto di Visual Basic è fondamentale per il successo di questa interfaccia, l'enumeratore restituito dalla _NewEnum proprietà deve supportare l'interfaccia IEnumVARIANT . Si tratta dell'unica interfaccia enumeratore riconosciuta da Visual Basic.

Creazione di typedef per le classi di criteri di copia

I typedef creati finora forniscono tutte le informazioni necessarie per creare ulteriori typedef per le classi di copia che verranno usate dall'enumeratore e dalla raccolta:

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

In questo esempio è possibile usare la classe personalizzata GenericCopy definita in VCUE_Copy.h e VCUE_CopyString.h dall'esempio ATLCollections . È possibile usare questa classe in altro codice, ma potrebbe essere necessario definire altre specializzazioni di GenericCopy per supportare i tipi di dati usati nelle raccolte. Per altre informazioni, vedere Classi di criteri di copia ATL.

Creazione di typedef per enumerazione e raccolta

Ora tutti i parametri del modello necessari per specializzare le CComEnumOnSTL classi e ICollectionOnSTLImpl per questa situazione sono stati forniti sotto forma di typedef. Per semplificare l'uso delle specializzazioni, creare altri due typedef, come illustrato di seguito:

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

A CollectionType questo punto è un sinonimo di una specializzazione di ICollectionOnSTLImpl che implementa l'interfaccia IWords definita in precedenza e fornisce un enumeratore che supporta IEnumVARIANT.

Modifica del codice generato dalla procedura guidata

È ora necessario derivare CWords dall'implementazione dell'interfaccia rappresentata dal CollectionType typedef anziché IWordsda , come illustrato di seguito:

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.

Aggiunta di codice per popolare la raccolta

L'unica cosa che rimane è popolare il vettore con i dati. In questo semplice esempio è possibile aggiungere alcune parole alla raccolta nel costruttore per la classe :

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

È ora possibile testare il codice con il client preferito.

Vedi anche

Raccolte ed enumeratori
Esempio di ATLCollections
Classi di criteri di copia ATL