在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