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:
Generare un nuovo oggetto semplice.
Modificare il file IDL per l'interfaccia generata.
Creare cinque typedef che descrivono come vengono archiviati gli elementi della raccolta e come verranno esposti ai client tramite interfacce COM.
Creare due typedef per le classi di criteri di copia.
Creare typedef per le implementazioni dell'enumeratore e della raccolta.
Modificare il codice C++ generato dalla procedura guidata per usare il typedef della raccolta.
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:
Le interfacce di raccolta sono in genere duali perché i client di Automazione accedono alla
_NewEnum
proprietà tramiteIDispatch::Invoke
. Tuttavia, i client di automazione possono accedere ai metodi rimanenti tramite la tabella virtuale, quindi le interfacce doppie sono preferibili alle differenze.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.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.
È 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.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.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é IWords
da , 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