许多COM库都是一组对象的形式暴露给外界使用,这组对象通常被称作对象模型。这组对象模型中通常有父对象或者说是根对象,这个对象是允许被创建的,这个根对象有子对象或子对象的集合,这些个子对象不能自主创建他必须被他的根对象创建或者是被他的父对象创建。举个例子来说可能会更加清晰:比如microsoft outlook对象模型。这个对象模型有个一个根对象是Application,这个对象允许我们用CoCreateInstance来创建,这个COM对象下有一个称作Namespace的对象,Namespace对象下又有Store这样的对象,store对象下又有文件夹的集合,这些文件夹中又含有邮件或者是联系人等资料的集合,这个集合中的元素下又含有附件集合,如此便构成一个象金字塔式的分层体系。这些子对象常是在父对象中创建,不能象根对象一样呼叫CoCreateInstance等来创建。还有一个更加普通的例子便是MSXML的DOM模型,这个更为大家所熟知。在这些个模型中集合和枚举器在其中起到了粘合剂的作用。
下面我就对工作中写的枚举器过程进行一个简单回顾,我要实现一个指定范围内的偶数枚举器。这个简单的逻辑能让我很快地完成回顾,我可以花很少时间关注别的问题,花更多时间来关注实现过程。
用向导生成ATL项目就叫MyCollection。在这个项目中加入一个Simple Atl Object类,名称就叫EvenNumbers。这样就在我们项目中MyCollection.idl文件中产生了相应的接口声明。为接口添加如下的方法或者属性
1// MyCollection.idl : MyCollection 的 IDL 源
2//
3
4// 此文件将由 MIDL 工具处理以
5// 产生类型库(MyCollection.tlb)和封送处理代码。
6
7import "oaidl.idl";
8import "ocidl.idl";
9
10[
11 object,
12 uuid(D4C7CD02-CD9F-48A6-BD6C-F1B02E66DA1C),
13 dual,
14 nonextensible,
15 helpstring("IEvenNumbers 接口"),
16 pointer_default(unique)
17]
18interface IEvenNumbers : IDispatch
19{
20 [id(1)] HRESULT Calc([in] LONG lMin, [in] LONG lMax);
21 [propget, id(2)] HRESULT Count([out, retval] LONG* pVal);
22 [propget, id(DISPID_VALUE)] HRESULT Item(LONG nIndex, [out, retval] LONG* pVal);
23 [propget, id(DISPID_NEWENUM)] HRESULT _NewEnum([out, retval] IUnknown** pVal);
24};
25[
26 uuid(FFFC1807-CFBA-47A0-9036-04AC92E02F8D),
27 version(1.0),
28 helpstring("MyCollection 1.0 类型库")
29]
30library MyCollectionLib
31{
32 importlib("stdole2.tlb");
33 [
34 uuid(503BB40F-86C8-4D1A-8E5C-5C81460C3EA5),
35 helpstring("EvenNumbers Class")
36 ]
37 coclass EvenNumbers
38 {
39 [default] interface IEvenNumbers;
40 };
41};
42
现在我们完成了大部分的工作,就留下较难的get__NewEnum实现。在理解实现机理后这部分也不算难,这里只是回顾一个写作过程,并不会太多地说原理,跟着做就可以实现一般应用要求的集合枚举器。不说原理的原因是ATL很复杂,自己水平又有限。有人说MFC库是个魔鬼,我认为ATL库的复杂度也不会比MFC库容易。ATL库对C++中的模板应用可以说发挥到了极致。
EvenNumbers.h文件。
#include <vector>
#include "Reuse/VCUE_CopyLong.h"
#include "MyCollection_i.h"
namespace LongColl
{
typedef std::vector<LONG> ContainerType;
typedef VARIANT EnumeratorExposedType;
typedef IEnumVARIANT EnumeratorInterface;
typedef LONG CollectionExposedType;
typedef IEvenNumbers CollectionInterface;
typedef VCUE::GenericCopy<EnumeratorExposedType, ContainerType::value_type> EnumeratorCopyType;
typedef _Copy<CollectionExposedType> CollectionCopyType;
typedef CComEnumOnSTL<EnumeratorInterface,&__uuidof(EnumeratorInterface), EnumeratorExposedType, EnumeratorCopyType, ContainerType> EnumeratorType;
typedef ICollectionOnSTLImpl<CollectionInterface, ContainerType, CollectionExposedType, CollectionCopyType, EnumeratorType> CollectionType;
}
// CEvenNumbers
class ATL_NO_VTABLE CEvenNumbers :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CEvenNumbers, &CLSID_EvenNumbers>,
public IDispatchImpl<LongColl::CollectionType, &IID_IEvenNumbers, &LIBID_MyCollectionLib, /**//*wMajor =*/ 1, /**//*wMinor =*/ 0>
{
由于我们的类继承了ICollectionImpl,所以Item, Count这两个属性我们可以不必自己再去实现。我们只要实现Calc方法 及_NewEnum属性了。这里使用ATL开发小组提供的文件VCUE_Collection。所以只需一行代码就解决这个问题。如下:
STDMETHODIMP CEvenNumbers::Calc(LONG lMin, LONG lMax)
{
// TODO: 在此添加实现代码
if ((lMin % 2) == 1)
{
++lMin;
}
for (LONG i = lMin; i < lMax; i += 2)
{
m_vec.push_back(i);
}
return S_OK;
}
STDMETHODIMP CEvenNumbers::get__NewEnum(IUnknown** pVal)
{
// TODO: 在此添加实现代码
return VCUE::CreateSTLEnumerator<LongColl::EnumeratorType>(pVal, this, m_vec);
}
到此就做好一个集合枚举器的例子。可以在C#中使用。如下。
foreach(object obj in evenNumbers)
{ Console.WriteLine(obj.ToString()); }
Sample: http://www.cppblog.com/Files/Robertxiao/MyCollection.rar