在COM中,实现多个数据返回,可以使用SAFEARRAY和ICollection两种方法,其他方法,我还没有考虑到。
使用SAFEARRAY方式返回的多个数据,并不是特别灵活,而且SAFEARRAY在其他语言中,使用也不是特别方便,尤其是JavaScript中,想要处理SAFEARRAY,还是挺麻烦的一件事。同时,SAFEARRAY需要对处理的数据结构进行特别处理,应该算得上是一个麻烦吧。
使用ICollection返回多个数据,相对比较方便,当然,也有很多细节需要注意,但是实现了这样一个ICollection对象之后,便可以重用这个对象,相比较SAFEARRAY,重用效果更好,也更容易做更多的数据扩展与数据处理。ICollection是一个枚举容器,需要枚举器来辅助进行操作元素
ICollection是标准的COM接口:
下面是一个ATL中的ICollection实现:
// idl文件:
// DynamicArray.idl : IDL source for DynamicArray.dll // // This file will be processed by the MIDL tool to // produce the type library (DynamicArray.tlb) and marshalling code. import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(7942687C-3E0D-468B-80E7-7D0FC34B15EF), dual, helpstring("IVector Interface"), pointer_default(unique) ] interface IVector : IDispatch { [id(DISPID_NEWENUM), propget] HRESULT _NewEnum([out, retval] IUnknown** ppUnk); [id(DISPID_VALUE), propget] HRESULT Item( [in] long Index, [out, retval] VARIANT* pVal); [id(0x00000001), propget] HRESULT Count([out, retval] long * pVal); }; [ uuid(A08196C2-46C7-4BE2-824D-A26581990B43), version(1.0), helpstring("DynamicArray 1.0 Type Library") ] library DYNAMICARRAYLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(38080E8B-9884-49CA-932D-A298E6882598), helpstring("Vector Class") ] coclass Vector { [default] interface IVector; }; };
// vector.h文件
#ifndef __VECTOR_H_ #define __VECTOR_H_ #include "resource.h" // main symbols #include <vector> #include <atlcom.h> typedef std::vector<VARIANT> CollType; typedef CComEnumOnSTL<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT>, CollType > EnumType; typedef ICollectionOnSTLImpl<IVector, CollType, VARIANT, _Copy<VARIANT>, EnumType > CollectionType; ///////////////////////////////////////////////////////////////////////////// // CVector class ATL_NO_VTABLE CVector : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CVector, &CLSID_Vector>, //public IDispatchImpl<IVector, &IID_IVector, &LIBID_DYNAMICARRAYLib>, public IDispatchImpl<CollectionType, &IID_IVector, &LIBID_DYNAMICARRAYLib> //public CollectionType { public: CVector() { m_coll.push_back( CComVariant(100) ); m_coll.push_back( CComVariant(200) ); m_coll.push_back( CComVariant(300) ); } DECLARE_REGISTRY_RESOURCEID(IDR_VECTOR) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CVector) COM_INTERFACE_ENTRY(IVector) COM_INTERFACE_ENTRY(IDispatch) // COM_INTERFACE_ENTRY(ICollection) END_COM_MAP() // IVector public: };
编译后,注册到系统后。通过OLEView.exe工具查看,可以得到下面的接口信息:
[ uuid(7942687C-3E0D-468B-80E7-7D0FC34B15EF), helpstring("IVector Interface"), dual ] dispinterface IVector { properties: methods: [id(0xfffffffc), propget] IUnknown* _NewEnum(); [id(00000000), propget] VARIANT Item([in] long Index); [id(0x00000001), propget] long Count(); };
显然,我们的ICollection接口实现对象已经ok了。
前面已经说过,ICollection实现的枚举容器的接口,在JavaScript中,枚举器使用Enumerator来表示,使用方式如下:
var enumObj = new Enumerator([collections]) collections 可选,为任意集合对象
在JavaScript中枚举器对象具有下面的接口方法:
atEnd() 返回一个bool值,指明是否已经到达结尾.如果当前项是集合中的最后一个,或者集合为空,或者当前项没有定义,则返回true,否则返回false enumObj.atEnd() item() 返回集合中的当前项 如果没有定义,则返回undefined enumObj.item() moveFirst() 指针重新指向集合首位 如果集合集合中没有项,则当前项被设置为defined enumObj.moveFirst() moveNext() 将集合中的当前项向下移动一项 enumObj.moveNext()
这些接口方法,回自动去调用ICollection中实现的接口函数。因此,我们只需要将ICollection实现对象作为JavaScript中Enumerator对象的参数即可遍历容器中的所有数据。
如:
var value = "";
enumObj = new Enumerator(coll); for(; !enumObj.atEnd(); enumObj.moveNext() ) { var x = enumObj.item(); value += x + "\r\n"; }
在VB中,有直接对枚举类型进行操作的语句,使用FOR ... NEXT方式进行。
大部分语言中,对于ICollection中的操作都很方便,因此很容易实现对ICollection对象的实现与处理。
ICollection可以作为数组参数进行处理,也可以作为返回值进行返回,在跨语言进行不同的数据处理时,这将是一个很好的多个数据传递粘合剂,并且实现好的枚举器,可以重复使用。
myblog:
http://www.cnblogs.com/ubunoon
http://qtrstudio.com/blog