COM中多个数据值返回

在COM中,实现多个数据返回,可以使用SAFEARRAY和ICollection两种方法,其他方法,我还没有考虑到。

使用SAFEARRAY方式返回的多个数据,并不是特别灵活,而且SAFEARRAY在其他语言中,使用也不是特别方便,尤其是JavaScript中,想要处理SAFEARRAY,还是挺麻烦的一件事。同时,SAFEARRAY需要对处理的数据结构进行特别处理,应该算得上是一个麻烦吧。

使用ICollection返回多个数据,相对比较方便,当然,也有很多细节需要注意,但是实现了这样一个ICollection对象之后,便可以重用这个对象,相比较SAFEARRAY,重用效果更好,也更容易做更多的数据扩展与数据处理。ICollection是一个枚举容器,需要枚举器来辅助进行操作元素

ICollection是标准的COM接口:

interface ICollection : IDispatch
{
[propget, id(DISPID_LISTITEM)] HRESULT Item([in] 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);
};
ICollection需要枚举器辅助进行数据处理,非常符合OOP的实现,这些枚举器都用IEnum开头,类似接口为:
interface IEnumXXX : IUnknown
{
HRESULT Next( unsigned long celt, VARIANT * rgvar, unsigned long * pceltFetched);
HRESULT Skip(unsigned long celt);
HRESULT Reset();
HRESULT Clone(IEnumVARIANT ** ppenum);
};
目前微软已经定义好的IEnumXXX类型有:

下面是一个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

原文地址:https://www.cnblogs.com/ubunoon/p/1847754.html