Collection and enumeration in ATL
The relationship between the collection and enumerators in COM is very similar to the relationship between the container and iterator in STL.
Enumerator
If a COM object can be called a "collection", it is clear that inside the object is definitely a collection of the same type of data. When we want to expose internal data to the client, if you use the STL directly The mode provides an Iterator type, then the server-side data package is too weak, and the data is easily destroyed by the client. In view of this, COM provides an enumerator for customers to use - the client adopts the interface of the set, acquires the interface of the enumerator, using the enumerator to access the data; except for the envelope interface, the general set object also provides GET__ITEM method Exposure of your own data.
Suppose the ICollection interface supports the enumerator IEnumsth, then get the enumerator interface by the following code, enumerate internal elements:
HRESULT HRES;
Ienumsth PSth;
HRES = ICOLLECTION-> GET__NEWENUM ((iUnknown **) & psth);
There is nothing in our program, and COM specifies that all Ienumxxx types requires the following four methods: Next, Skip, Reset, Clone. Obviously, the role of the GET__NEWENUM () method is to construct an object (hereinafter referred to as an enumerator object), which implements the four methods listed above, and then returns the interface pointer of the object to the client. .
ATL provides a general implementation template for an enumerator object, which greatly facilitates our work. The design idea of the envelope object template is "policy", let's take a look at what strategies to implement the template:
1. The name of the enumerator interface - that is, Ienumsth in the above example
2, the IID of the enumerator interface is __UUIDOF (IEnumsth)
3, the type of data being enumerated - is also the Sth in the above example (can be Variant, BSTR, etc.)
4, replication strategy - this policy is the most complex, its function is the function of the data COPY inside the "Collection" object to the buffer provided by the customer
5. Data storage form - data structure (array, STL container, etc.) in "Collection" objects (arrays, STL containers, etc.)
6, thread model
The template class provided by ATL is as follows:
Template
CLASS ATL_NO_VTABLE CCOMENUMONSTL
This class is exactly 6 template parameters, each of which corresponds to the corresponding policy listed above, so before using the class, first carefully consider the value of these 6 template parameters, then combine it, you can get it The enumerator object you need -, like other objects in ATL, the object also needs to create its examples in the form of CComObject <> :: CreateInstance, rather than direct NEW CCOMOBJECT <>.
NB: For 6 template parameters, IID = __UUIDOF (Name) this equation is established, so the truly orthogonal strategy category is only 5. However, since the __UUIDOF operator is an extension of the Visual C compiler to standard C , ATL does not omit the IID's policy class here. However, in many other places in ATL, there are uses for the __UUIDOF operator - in the world of COM, the thrush of MS is impossible ............ The steps to use CCOMENUMONSTL are as follows:
1. Determine which STL container needs enumeration
2. When the client needs an enumerator interface, select the appropriate template parameter, TypeDef out a specific enumerator object.
3, ccomobject <> :: createInstance () Generate an enumerator instance
4. Call the init () method for the generated enumerator instance
5, return to the pointer and results information to customers
The whole process is simple or simple, especially some steps, and the ATL development team provides a project called: ATLCOLLECTIONS in the ATL Sample, which provides: ATLCOLLECTIONS, with a file called Reuse The clip, the three files there are very strong reuse, in the vcue_collection.h file, the ATL development team provides us with the createstLenumerator () function, encapsulating the creation process of the entire enumerator object, This function is like this:
Template
HRESULT CREATESTLENUMERATOR (IUNKNOWN * PPUNK, IUNKNOWN * PUNKFORRELEASE, COLLTYPE & Collection)
{
IF (PPUnk == Null)
Return E_POINTER;
* PPUNK = NULL;
CCOMOBJECT
HRESULT HR = CComobject
IF (Failed (HR))
Return HR;
HR = Penum-> Init (PunkForRelease, Collection);
En (ac))
HR = Penum-> QueryInterface (PPUNK);
IF (Failed (HR))
Delete Penum;
Return HR;
}
The CreatestLenumerator () function encapsulates the initialization and error handling code necessary to create an enumerator of the CCOMENUMONSTL style. We can use this function to complete the get__newenum () method:
Typedef ccomenumonstl _Copy Std :: vector StdMethod (get__newenum) (iUnknown ** PPUNK) { Return CreatestLenumerator } The whole GET__NEWENUM is a statement: Return Createstlenumerator Let's take a look at an example: The function of this example is simple, there is a std :: Vector 1. Create a new solution called Atlenum1, add a new type of item called Atlenum1, in the project properties setting, cancel its "property" selection, hook "Allow merge Proxy / stub code check box, click the project. 2. Switch to "Class View", add a template as shown below to add a template for the ATL simple object name atlenum: Figure atlenum-01 Figure atlenum-02 The default property setting of the VC for the added class is to support dual interface, we change it to a custom interface, exempt from the number of troubles achieved by the iDispatch interface. 3, we assume that vector is global and is initialized in the constructor of the AtleNum class. Open the Atlenum.h file, add the header file to include: #include Add a Vector Std :: Vector Rewriting the Catlenum constructor is as follows: Catlenum () { IF (Test_Vec.empty ()) { Variant var; Variantinit (& var); Var.vt = vt_i4; Var.llval = 1; Test_vec.push_back (var); Var.llval = 2; Test_vec.push_back (var); } } Make Catlenum to initialize the Vector during construction. 4. Follow the figure below to add a method called getNum () to CATLENUM to provide an enumerator to the client: Figure atlenum-03 Figure atlenum-04 5. Find the ATLCOLLECTIONS project from the ATL Sample included with MSDN, and the Reuse folder in our Atlenum1 project is in the folder of our AtleNum1 project. Open atleNum.cpp, add an included file: #include "../reuse/vcue_collection.h" List the strategic classes listed in the article: Typedef std :: Vector TypedEf Variant ExposedType; Typedef Ienumvariant EnumeratorInterface; Typedef _copy Define an enumerator object according to the above strategies: Typedef Ccomenumonstl ENUMERATORTYPE; The enumeratortype of Typedef here is the enumerator we need. Change the code of the genum () method as follows: STDMETHODIMP CATLENUM :: GetNum (iUnknown ** PPUNK) { Return Vcue :: CreatestLenumerator } 6, the coding work of the entire server has been completed! We just have five TypeDef statements, then call a function provided by the ATL team to complete the function of the enumerator we need. Client test Server end coding completes, below we write a client-side program to test the write SERVER. For the sake of simplicity, the Client end code does not implement excessive features, just achieve the enumerator interface, and test the next () method of the enumerator. The entire Client end code is as follows: #include "stdafx.h" #include #import "../atalenum1/debug/atlenum1.dll" no_namespace Static const Ulong nbatchsize = 5; int main () { Coinitialize (NULL); Iatlenum * patlenum; HRESULT HRES = / :: CoCreateInstance (__ uuidof (atlenum), null, clsctx_all, __UUIDOF (Iatlenum), (LPVOID *) & PATINUM); Ienumvariant * Penumvar; HRES = PALENUM-> GetENUM ((iUnknown **) & penumvar); Ulong nreturned = 0; Variant arrvar [nbatchsize] = {0}; Penumvar-> Next (NbatchSize, & Arrvar [0], & nreturned); Penumvar-> release (); PATINUM-> Release (); Couninitialize (); Return 0; } Perform Penumvar-> Next (NbatchSize, & Arrvar [0], & nreturned); this statement can be seen from the debug window as follows: Figure Atlenum - 05 Variable NRETURNED = 2 Description This enumerator retrieves two data, expands the display of the Arrvar array, you can see that the enumerator interface correctly implements the next () method, passes the two Variant data to the client code.