Use array parameters in COM -ICOLLECTION
Keywords: DCOM, array, custom type, Marshal, SafeArray, iCollection
1 use iCollection
Icollection is an interface inherited from iDispatch. Icollection also requires a IEnumvariant interface to fit implementation. IEnumvariant is inherited from iUnknown instead of inheriting from the IDispatch interface.
The iCollection interface provides the largest object-oriented design flexibility and reusability. In the array pointer and the SaFearRay method, each element of the array must be calculated in advance and saved in a specific data structure. Using the iCollection interface, you can design an array of proactive generated, that is, the elements of the array are required to reduce memory usage and speed up the processing speed.
1.1 Icollection and IEnumvariant
The ICollection interface is used to define an array object, while the IenumVariant interface is used to define an enumeration object. The role of enumeration object is to read array elements in order, sometimes, can achieve higher efficiency by enumerating objects.
The definition of Icollection and Ienumvariant is as follows:
Interface iCollection: IDispatch
{
[PropGet, ID (Dispid_ListItem)] HRESULT ITEM
Const Variant Varindex,
[OUT, RETVAL] VARIANT * PVAL);
[PropGet, ID (Dispid_ListCount)] HRESULT Count
[OUT, RETVAL] long * pval);
[Propget, ID (Dispid_collcount)] HRESULT Length (
[OUT, RETVAL] long * pval);
[Propget, ID (Dispid_Newenum), Restricted, Hidden]
HRESULT _NEWENUM ([OUT, RETVAL] IUNKNOWN * * PVAL);
... // Other methods or properties
}
Interface Ienumvariant: IUNKNOWN
{
HRESULT NEXT
Unsigned long Celt,
Variant * rgvar,
UNSIGNED long * pCELTFETCHED);
HRESULT SKIP (UNSIGNED Long Celt);
HRESULT RESET ();
HRESULT Clone (IEnumvariant ** Ppenum);
}
Sometimes, COM objects do not only implement array functions, but also other functions. Therefore, most of the time, the interface implemented by the COM object is inherited from Icollection.
There are two ways to manipulate an array through the Icollection manipulation. One is to obtain an element with an array subscript through the Item property. In this way, only one element can be obtained each time, and the subscript object is passed, so the efficiency is relatively low. Another approach is to pass enumerators. The enumerator of array objects is obtained by _newenum attribute. Elements can only be obtained in order through an enumerator, but each time you can get any elements, so the efficiency is high. The ICollection object can only implement one of the access methods, or both are implemented. There is also an important attribute in Icollection: count. Count Attributes Returns the length of the array, for arrays that cannot be determined, or do not implement Count properties.
The Ienumvariant interface is used to define an enumerator. The enumerator is used in sequential reading of array elements. Through Next method, you can read any more elements at a time. Since the enumerator can access array elements in order, the next method does not need to pass the subscript. The Skip method is used to skip several elements without reading. RESET sets the current element to a population head, so you can restart the enumeration. Clone is used to get a new enumerator. Two enumerators can do not interfere with each other. It is important to note that there may be some array objects to implement a different attribute name. In fact, the attribute name in ICollection is not important, and it is an DISPATCH ID. As long as you pass the Dispatch ID, you can get the correct properties.
1.2 array object
An array object is a COM object that implements the iCollection interface. The user of the array object acquires data in the array through the ICollection interface, and does not need to know the specific implementation of the array. The advantage of this design is that the code of the array can completely ignore the implementation method of the array, and when the implementation of the array changes, the code using the array can be kept compatible on the binary code, that is, the target code can be used without compiling. .
The simplest method of making an array object is a template using ATL. The CCOMENUMONSTL template is used to generate an enumeration object that implements the IenumVariant interface. Of course, if you want to implement all the advantages of an array object, it is best to write the code of an array object yourself.
1.3 iCollection parameter IDL declaration
In the IDL declaration. Array objects should be declared as idispatch *. If it is output or input to output parameters, a dual pointer should be used.
[ID (0)] getNumber ([out] idispatch ** ppobj);
[ID (1)] setNumber ([in] iDispatch * pobj);
Currently, what the Icollection array we have seen is read-only. In fact, iCollection can be designed to be readable, as long as the ITEM property of the iCollection is set to be readily writable. Please refer to the relevant information about the readable ICOLLECTION object.
1.4 Implement array objects via ATL
ATL is supported by two templates to ICollection. They are ccomenumonstl and iCollectionOnstlimpl. CCOMENUMONSTL is used to implement an enumerator based on STL object. ICollectionOnstlimpl is used to implement an ICollection interface. The functions and usage of these two templates will be described in detail below.
1.4.1 ccomenumonstl
The definition of CCOMENUMONSTL is as follows:
Template Const IID * PIID, Class T, Class Copy, Class Colltype, Class threadmodel = CComObjectthreadmodel> Class ATL_NO_VTABLE CCOMENUMONSTL: Public IEnumonstlimpl Public CComobjectrootex In the template parameters, Base is the interface implemented by the enumerator, usually IENUMVARIANT. PIID is the IID of an enumerator interface, usually IID_IENUMVARIANT. T is the type of enumerator output value, usually Variant. Copy is a copy class that converts values in the STL object into an enumerator output parameter. COLLTYPE is a STL type for storing data. ThreadModel is a thread mode parameter, which can be CCOMSINGLETHREADMODEL or CCOMMULTITHREADMODEL, the default value is the current default thread mode. Assume that the array element is saved using the VECTOR class. The Vector parameter is long data. Enumerators can be implemented by the following methods. Define COLLTYPE Typedef st :: Vector 2. Define the COPY class The COPY is used to perform parameter conversion between the element type and enumerator types of the STL class. Each COPY class must have three static functions: init, copy, destroy. INIT is used to initialize the enumerator class, and COPY is used to copy the STL element to the enumerator parameter, and Destroy is used to destroy the enumerator parameters. The following is a COPY class instance for conversion between long and variant. Class CopyVariantlong { PUBLIC: Static void init (Variant * P) { Variantinit (P); } Static HRESULT COPY (VARIANT * PTO, Const long * pfrom) { PTO-> VT = Vt_i4; PTO-> LVAL = * PFROM; Return S_OK; } Static void Destroy (Variant * P) { VariantClear (P); } } 3. Define enumeors The enumerator type can be conveniently defined by the class defined above. Typedef ccomenumonstl & IID_IENUMVARIANT, Variant, CopyVariantlong, COLLTYPE> ENUMTYPE; 1.4.2 icollectiononstlimpl ICollectionOnstlimpl is used to help implement the ICollection interface. Icollectiononstlimpl is defined as follows: Template Class Colltype, Class ItemType, Class CopyItem, Class EnumType> Class Icollectiononstlimum: Public T In the iCollectiononstlimpl template, T is the interface to be implemented, which generally uses interfaces from iCollection inherited. The COLLTYPE parameter is a STL type for saving data, which should be the same as the enumerator. ItemType is the type of Item attribute in iCollection, usually Variant. CopyItem is a COPY class for the Item property, and the COPY class in the enumerator is the same. EnumType is the type of enumerator. The iCollection interface can be implemented by the following steps. 1. Define the ICollection type TypedEf iCollectionOnstlimpl COLLTYPE, Variant, CopyVariantlong, EnumType> CollectionType; 2. Define array objects Define an array object and a COM object that defines a normal ATL. Just turn the interface parameters (first parameters) in idispatchImp to become the iCollectionOnstlimpl parameter you just complete. Class ATL_NO_VTABLE CNUMBERCOLLECTION: Public CComobjectrootex Public CCOCLASS Public IDispatchImpl & IID_INUMBERCOLLECTION, & Libid_collectionObjlib> { ... } 1.5 Using an array object For a universal iCollection object, you can only access it through IdisPatch. That is to say, the elements in the array are accessed by Idispatch :: Invoke method. On the other hand, Icollection objects typically refer to data through a Variant type. So, we must also understand how to access the variable of the Variant type. 1.5.1 call IDispatch IDispatch is an interface defined in Automation. With Idispatch, COM customers can obtain information such as types, parameters, and return values of each method and attribute in the interface. With Idispatch's Invoke method, COM customers can also directly call methods and properties in the interface. The content of IDispatch is very rich, it is impossible to introduce it here, so it means to make a simple description of how to invoke IDispatch via the Invoke method. 1. Definition of Invoke method HRESULT INVOKE Dispid DispidMember, Refiid RIID, LCID LCID, Word wflags, Dispparams Far * pdispparams, Variant Far * PvarResult, Excepinfo Far * Pexcepinfo, Unsigned int far * puargerrr ); The parameters of Invoke are as follows: l Dispidmember: Dispatch ID of the attribute or method called l RIID: Reserved, must be IID_NULL l LCID: Language environment. Generally use Locale_thread_DEFAULT l Wflags: You can be one of the following four parameters: Dispatch_method method call dispatch_propertyget () Read Properties Dispatch_PropertyPut () Write Properties Dispatch_PropertyPutRef () By reference write properties l pdispparams: argument array l PvarResult: return value l PEXCEPINFO: An abnormality inside the called method or attribute (if an abnormal occurs) l Puarger: When it returns DISP_E_PARAMNOTFOUND or DISP_E_TYPEMISMATCH, the error parameter number is returned. The following is an example using Invoke. The following example returns a Dispatch ID is a simple parameter of the dispid_listcount, actually the length of the array. VARIANT VARRESULT; Dispparams Dispparams; EXCEPINFO EXCEPINFO; Uint Errarg; Variantinit (& varResult); Dispparams.cargs = 0; Dispparams.cnamedargs = 0; Dispparams.rgdispidnamedargs = NULL; Dispparams.Rgvarg = NULL; HR = Pobj-> Invoke DISPID_LISTCOUNT, IID_NULL, Locale_user_default, Dispatch_propertyget, & Dispparams, & Varenum, & ExcePinfo, & Errarg); IF (Failed (HR)) { Goto cleanup; } The following example returns an attribute with parameters. Variant varindex; Variant Varresult; Disprams Disprams; EXCEPINFO EXCEPINFO; Uint Errarg; Variantinit (& varIndex); Variantinit (& varResult); Dispparams.cargs = 1; Dispparams.cnamedargs = 0; Dispparams.rgdispidnamedargs = NULL; Dispparams.Rgvarg = & varindex; VariantClear (& varIndex); VariantClear (& varResult); Varindex.vt = vt_i2; Varindex.ival = (short) Index; HR = Pobj-> Invoke DISPID_LISTITEM, IID_NULL, Locale_user_default, Dispatch_propertyget, & Dispparams, & varresult, & ExcePinfo, & Errarg); IF (Failed (HR)) { ... } 1.5.2 Use IEnumvariant enumeration data To use the IenumVariant enumeration data, you must first get the Ienumvariant pointer. The Ienumvariant pointer is to be the _newenum attribute through the iCollection. Specific operations can refer to the previous section for Invoke. After obtaining IenumVariant, you can read the array elements in the order of IenumVariant. Please refer to the following code enumeration data: This code is summarized in the array. Ulong result = 0; Ulong res; While (1) { HR = Penum-> Next (1, & var, & res); IF (Failed (HR)) { Goto cleanup; } IF (hr! = s_ok || res! = 1) { Break; } HR = VariantchangeType (& var, & var, 0, vt_i4); IF (Failed (HR)) { Goto cleanup; } Result = var.lval; } 1.5.3 Using Item and COUNT In addition to using an enumerator, you can also read the elements using the Item and Count attributes. Using Item and count compared to using an enumerator, you can get any element at any time, but the speed is slower than the use enumerator. An array element can be obtained with reference to the method of reading the Automation property via Invoke. 1.5.4 Variant Type In Icollection, a large number of Variant data is used. Here you summarize the use of Variant: 1. Use Variant variables directly a. Defining a variant variable You can directly define variables of the Variant type. Variant Val; b. Initialize Variant variables Be sure to initialize before using the Variant variable. Variantinit (& VAL); c. Set the variable value If you set a variable value, if you already have a value in the Variant variable, you must first clear the original data. VariantClear (& VAL); Val.vt = vt_i4; // Setting type VAL.LVAL = 10; // Setting the variable value d. Clear the Variant variable After using the Variant variable, you want to clear the variable, otherwise the memory leak will occur. VariantClear (& VAL); e. Dynamic allocation of Variant variables If you want to dynamically allocate a Variant variable, you should use the standard COM memory management function. Standard COM memory management functions include CotaskMallAlloc, CotaskMemFree, and CotaskMemRealloc. Variant * pval; PVAL = (variant *) cotaskmemalloc (size_of (variant)); Variantinit (PVAL); PVAL-> vt = vt_i4; PVAL-> LVAL = 10; ... VariantClear (PVAL); CotaskMemfree (PVAL); 2. Use Variant variables via ccomvariant CCOMVARIANT is a simple packaging of ATL for Variant. Variant can be easier to use Ccomvariant, without having to worry that there is no initialization or clear. If there is no special case, you should try to use Ccomvariant as much as possible without using Variant. The following is a code instance using CCOMVARIANT. Ccomvariant Val; VAL.VT = VT_I4; Val.lval = 10; // VAL does not have to be cleared The following is an example of using the CCOMVARIANT array. Ccomvariant * pval; Pval = new ccomvariant [10]; For (int i = 0; i <10; i) { PVAL [i] .vt = vt_i4; PVAL [I] .lval = i 1; } ... delete [] pval; 2 post Due to the time relationship, and the complexity of the COM specification itself. This article is impossible to face, it can only play the role of tiles. I am here on the instance code of this article, you can request it by email. My Email address is Nelsonc@online.sh.cn. If you have anything unclear, you can also explore Email. If you want to know other contents about COM or DOTNET, you can also tell me. I will publish more articles in the future, I hope to help everyone.