本文介绍了我需要实现什么接口以允许以delphi编写的COM对象在VBA中使用ForEach?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 想象一下我想在VBA中执行类似的操作(伪代码),并假设我有一个可枚举的属性IDList: 昏暗的MyObject对象 set MyObject = CreateObject( MyObjectClass) 每个项目都作为MyObject.IDList中的整数$ ID Debug.Write(Cstr(Item)&; )接下来的 我的财产 IDList 必须在Delphi中看起来像吗? 仅仅从 IEnumerable< integer> 或 IEnumerable 派生它似乎并不起作用。 基本代码 为了避免默认 IENum 和 IEnum< T> 接口我创建了自己的一组接口用于Delphi端的枚举,用于对象pascal 循环中的code>。 ISGEnumeratorBase = interface(IInterface) ['{DA91A203-3B39-4287-9A6F-6E9E4B184BAD}'] 函数MoveNext:布尔值; 结尾; ISGEnumeratorReset =接口(ISGEnumeratorBase) [’{FBD2EFBD-D391-4BE2-A3AB-9C9D09197F78}’] 过程Reset; 结尾; ISGEnumeratorClone =接口(ISGEnumeratorBase) [’{E3A128FD-7495-464D-BD5E-3EBA3AEFE94F}’] 函数Clone:ISGEnumeratorBase; 结尾; ///< summary> ///< para> ///实现for..in循环所必需 ///< / para> /// IEnumerator< T& gt;的替代通用接口在系统单位中定义了 ///。允许为 ///接口类等枚举器的实现更容易。 ///< / summary> ISGEnumerator< T> = interface(ISGEnumeratorBase)函数GetCurrent:T; 属性Current:读取ReadCurrent; 结尾; ///< summary> ///< para> ///实现for..in循环所必需 ///< / para> ///< para> /// IEnumerator< T& gt;的替代通用接口 ///在系统单位中定义。允许更轻松地实现 ///用于接口类等的枚举器。< br /> ///< / para> ///< / summary> ISGEnumerable< T> =接口(IInterface)函数GetEnumerator:ISGEnumerator< T> ;; 结尾; 因此我在应用程序中使用的枚举器使用这些接口来发布自己。 我想要的是有一个适配器类,该适配器类允许在5月 ISGEnumerator< T> IEnumVariant 接口c>和 ISGEnumerable< T> 接口解决方案 摘要 我创建了一个通用接口适配器,该接口适配器可以使 IEnumVariant 或多或少地易于实现。接口。我还发现, IEnumVariant 接口是在Delphi提供的 ActiveX 单元中定义的,并且使用了 stdole32.tpl 作为类型库。 OLE枚举器基类 这是枚举器基类和通用枚举器基类: type TSGOLEVariantEnumeratorAdapterBase = class(TAutoIntfObject,IEnumVariant)私有类var vOLETypeLib:ITypeLib; 私有类函数GetOLETypeLib:ITypeLib;静态的; class Destructor ClassDestroy; //用于IOLEEnumVariant 函数Next(celt:LongWord; var rgvar:OleVariant; out pceltFetched:Longword):HResult; stdcall; 函数Skip(celt:LongWord):HResult; stdcall; 函数复位:HResult; stdcall; 函数Clone(Enum:IEnumVariant):HResult; stdcall; 受保护的类属性OLETypeLib:ITypeLib读取GetOLETypeLib; 函数DoNext(aFetchRequestCount:LongWord; var rgvar:OleVariant; out aActuallyFetchedCount:Longword):布尔值;虚拟;抽象; 函数DoSkip(aSkipCOunt:LongWord):布尔值;虚拟;抽象; 函数DoReset:布尔值;虚拟; 函数DoClone(out Enum:IEnumVariant):布尔值;虚拟; public 构造函数Create; 结尾; TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> = class(TSGOLEVariantEnumeratorAdapterBase,ISGEnumerator< TEnumeratedType>)私人 FSourceEnumerator:ISGEnumerator< TEnumeratedType> 受保护的函数MapCurrentToVariant(aCurrent:TEnumeratedType):olevariant;虚拟; 函数DoReset:布尔值;覆盖函数DoClone(out Enum:IEnumVariant):布尔值;覆盖函数DoNext(aFetchRequestCount:LongWord; var rgvar:OleVariant; out aActuallyFetchedCount:Longword):布尔值;覆盖函数DoSkip(aSkipCOunt:LongWord):布尔值;覆盖属性SourceEnumerator:ISGEnumerator< TEnumeratedType>读取FSourceEnumerator实现ISGEnumerator< TEnumeratedType> ;; public 构造函数Create(const aSourceEnumerator:ISGEnumerator< TEnumeratedType>); 结尾; 我在实例化TAutoIntfObject基类和正确的类型库上苦苦挣扎,但最终我设法使用了它像下面一样我为类型库使用了一个var类,以避免一遍又一遍地加载它。 构造函数TSGOLEVariantEnumeratorAdapterBase.Create; 开始继承了Create(OLETypeLib,IEnumVariant); 结尾; 类析构函数TSGOLEVariantEnumeratorAdapterBase.ClassDestroy; 开始 vOLETypeLib:= nil; 结尾; 类函数TSGOLEVariantEnumeratorAdapterBase.GetOLETypeLib:ITypeLib; 开始 // HH我们不能在软件包中丢失Win.ComServ //这就是为什么我在此处克隆呼叫或LoadTypeLibrary的原因如果未分配(vOLETypeLib)则在然后 OleCheck(LoadTypeLibEx('stdole32.tlb',REGKIND_NONE,vOLETypeLib)); 结果:= vOLETypeLib; 结尾; 此后,我实现了接口的方法,还允许对 dispintf 。循环实现的实际内容放在从接口方法调用的虚拟方法中。接口方法如下所示: function TSGOLEVariantEnumeratorAdapterBase.Next(celt:LongWord; var rgvar:OleVariant; out pceltFetched:Longword):HResult; VAR lActuallyFetched:longword; begin lActuallyFetched:= 0; 如果DoNext(celt,rgvar,lActuallyFetched)尝试然后结果:= S_OK 否则结果:= S_FALSE; 如果已分配(@pceltFetched),则 pceltFetched:= lActuallyFetched; ,但除外结果:= SafeCallException(ExceptObject,ExceptAddr); 结尾; 结尾; 函数TSGOLEVariantEnumeratorAdapterBase.Skip(celt:LongWord):HResult; 开始如果DoSkip(celt)则尝试然后结果:= S_OK 否则结果:= S_FALSE; ,但除外结果:= SafeCallException(ExceptObject,ExceptAddr); 结尾; 结尾; 函数TSGOLEVariantEnumeratorAdapterBase.Reset:HResult; 开始如果DoReset然后尝试结果:= S_OK 否则结果:= S_FALSE; ,但除外结果:= SafeCallException(ExceptObject,ExceptAddr); 结尾; 结尾; 函数TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> .DoClone(Enum:IEnumVariant):布尔值; VAR lCloneIntf:ISGEnumeratorClone; lCLonedEnumerator:ISGEnumerator< TEnumeratedType> ;; 开始如果Supports(FSourceEnumerator,ISGEnumeratorClone,lCloneIntf)然后开始 lCLonedEnumerator:= ISGEnumerator< TEnumeratedType>(lCloneIntf.Clone); Enum:= TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType>(self.ClassType).Create(lCLonedEnumerator); 结果:= True; end else结果:=继承; 结尾; 函数TSGOLEVariantEnumeratorAdapterBase.Clone(Enum:IEnumVariant):HResult; 开始如果DoClone(Enum)然后尝试结果:= S_OK 否则结果:= S_FALSE; ,但除外结果:= SafeCallException(ExceptObject,ExceptAddr); 结尾; 结尾; 克隆并重置 我已经为 Clone 和 Reset 方法,但是在我的示例中,实际上不是从Excel VBA中调用这些方法, 通用IEnumVariant适配器类 接下来的事情是创建覆盖Doxxx方法的通用适配器,并添加 MapCurrentToVariant 例程,以将当前值从源枚举器获取到输出变量。该例程是虚拟的,因此可以为特殊或更有效的转换而覆盖。 因此泛型类如下: TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> = class(TSGOLEVariantEnumeratorAdapterBase,ISGEnumerator< TEnumeratedType>)私人 FSourceEnumerator:ISGEnumerator< TEnumeratedType> 受保护的函数MapCurrentToVariant(aCurrent:TEnumeratedType):olevariant;虚拟; 函数DoReset:布尔值;覆盖函数DoClone(out Enum:IEnumVariant):布尔值;覆盖函数DoNext(aFetchRequestCount:LongWord; var rgvar:OleVariant; out aActuallyFetchedCount:Longword):布尔值;覆盖函数DoSkip(aSkipCOunt:LongWord):布尔值;覆盖属性SourceEnumerator:ISGEnumerator< TEnumeratedType>读取FSourceEnumerator实现ISGEnumerator< TEnumeratedType> ;; public 构造函数Create(const aSourceEnumerator:ISGEnumerator< TEnumeratedType>); 结尾; 实施被覆盖的例程非常简单。 构造函数TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> .Create( const aSourceEnumerator:ISGEnumerator< TEnumeratedType>); 开始 FSourceEnumerator:= aSourceEnumerator; 继承了Create; 结尾; 函数TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> .MapCurrentToVariant(aCurrent:TEnumeratedType):olevariant; 开始结果:= TValue.From< TEnumeratedType>(aCurrent).AsVariant; 结尾; 函数TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> .DoNext(aFetchRequestCount:LongWord; var rgvar:OleVariant; out aActuallyFetchedCount:Longword):布尔值; 类型 Olevariant的TVariantList = array [0..0]; 开始 aActuallyFetchedCount:= 0; 而(aFetchRequestCount> 0)和SourceEnumerator.MoveNext做开始 dec(aFetchRequestCount); TVariantList(rgvar)[aActuallyFetchedCount]:= MapCurrentToVariant(SourceEnumerator.Current); inc(aActuallyFetchedCount); 结尾; 结果:=(aFetchRequestCount = 0); 结尾; 函数TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> .DoSkip(aSkipCOunt:LongWord):布尔值; 开始而(aSkipCount> 0)和SourceEnumerator.MoveNext做 dec(aSkipCount); 结果:=(aSkipCOunt = 0); 结尾; 我添加了 Clone 和 Reset 选项稍后,因为我的应用程序并未实际使用它们,所以也许将来可以使用。实现看起来像这样: function TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> .DoClone(out Enum:IEnumVariant):boolean; VAR lCloneIntf:ISGEnumeratorClone; lCLonedEnumerator:ISGEnumerator< TEnumeratedType> ;; 开始如果Supports(FSourceEnumerator,ISGEnumeratorClone,lCloneIntf)然后开始 lCLonedEnumerator:= ISGEnumerator< TEnumeratedType>(lCloneIntf.Clone); Enum:= TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType>(self.ClassType).Create(lCLonedEnumerator); 结果:= True; end else结果:=继承; 结尾; 函数TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> .DoReset:布尔值; VAR lResetIntf:ISGEnumeratorReset; 开始如果Supports(FSourceEnumerator,ISGEnumeratorReset,lResetIntf)然后开始 lResetIntf.Reset; 结果:= True; end else结果:=继承; 结尾; 最后,我决定创建一个可枚举的适配器类,在某些情况下可能会派上用场: TSGGenericOLEVariantEnumerableAdapter< TEnumeratedType> = class(TAutoIntfObject,ISGEnumerable< TEnumeratedType>)私人 FSourceEnumerable:ISGEnumer ; TEnumeratedType> ;; 受保护的函数Get__NewEnum:IUnknown;安全电话排队; 属性SourceEnumerable:ISGEnumerable< TEnumeratedType>读取FSourceEnumerable实现ISGEnumerable< TEnumeratedType> ;; public 构造函数Create(const aTypeLib:ITypeLib; const aDispIntf:TGUID; const aSourceEnumerable:ISGEnumerable< TEnumeratedType>); 结尾; 该类的实现: 构造函数TSGGenericOLEVariantEnumerableAdapter< TEnumeratedType> .Create(const aTypeLib:ITypeLib; const aDispIntf:TGUID; const aSourceEnumerable:ISGEnumerable< TEnumeratedType>); 开始 FSourceEnumerable:= aSourceEnumerable; 继承了Create(aTypeLib,aDispIntf); 结尾; 函数TSGGenericOLEVariantEnumerableAdapter< TEnumeratedType> .Get__NewEnum:IUnknown; 开始结果:= TSGGenericOLEVariantEnumeratorAdapter< TEnumeratedType> .Create(SourceEnumerable.GetEnumerator); 结尾; 在我计划使用我的代码的地方,一切看起来都很干净,几乎不需要被实施。以下是从我的实际应用程序模型中获得一堆对象ID的枚举器示例: TAMDBObjIDEnumeratorAdapter = class(TSGGenericOLEVariantEnumeratorAdapter<整数>); TAMDBObjIDEnumerableAdapter = class(TSGGenericOLEVariantEnumerableAdapter ,IAMObjectIDs,ISGEnumerable ) public 构造函数Create(const aSourceEnumerable:ISGEnumerable< integer) 结尾; .... 构造函数TAMDBObjIDEnumerableAdapter.Create(const aSourceEnumerable:ISGEnumerable< integer>); 开始继承了Create(comserver.TypeLib,IAMObjectIDs,aSOurceEnumerable); 结尾; 该代码实际上已经使用Excel和Delphi进行了测试,但是为我提供了所有内部解决方案的代码Delphi枚举器超出了本期的主题范围,这就是为什么我没有为此创建演示项目的原因。谁知道,如果我发现时间和足够的赞成/要求,我可能会为此投入更多精力。 我希望我在Delphi中找到一个可行且干净的解决方案的旅程对其他人有帮助。 Imagine I want to do something like this in VBA (pseudocode), and assuming my has an enumerable property IDList:Dim MyObject objectset MyObject= CreateObject("MyObjectClass")for each Item as integer in MyObject.IDList Debug.Write(Cstr(Item) & ";")NextWhat would my property IDList have to look like in Delphi? Simply deriving it from IEnumerable<integer> or IEnumerable does not seem to do the job.Base code In order to avoid trouble with the default IENum and IEnum<T> interfaces I have created my own set of interfaces for enumeration on the Delphi side, to be used in object pascal for .. in .. loops. ISGEnumeratorBase= interface(IInterface) ['{DA91A203-3B39-4287-9A6F-6E9E4B184BAD}'] function MoveNext: Boolean; end; ISGEnumeratorReset = interface (ISGEnumeratorBase) ['{FBD2EFBD-D391-4BE2-A3AB-9C9D09197F78}'] procedure Reset; end; ISGEnumeratorClone = interface (ISGEnumeratorBase) ['{E3A128FD-7495-464D-BD5E-3EBA3AEFE94F}'] function Clone:ISGEnumeratorBase; end; /// <summary> /// <para> /// Required for implementing for..in loops /// </para> /// An alternative generic interface for the IEnumerator&lt;T&gt; defined /// in the system unit. Allows for easier implementation of enumerators for /// interfaced classes etc. /// </summary> ISGEnumerator<T> = interface(ISGEnumeratorBase) function GetCurrent:T; property Current: T read GetCurrent; end; /// <summary> /// <para> /// Required for implementing for..in loops /// </para> /// <para> /// An alternative generic interface for the IEnumerator&lt;T&gt; /// defined in the system unit. Allows for easier implementation of /// enumerators for interfaced classes etc. <br /> /// </para> /// </summary> ISGEnumerable<T>=interface(IInterface) function GetEnumerator:ISGEnumerator<T>; end;So the enumerators I use in my application use these interfaces to "publish" themselves.What I want is to have an adapter class that allows for creating the IEnumVariant interface on may ISGEnumerator<T> and ISGEnumerable<T> interfaces 解决方案 SummaryI have created a generic interface adapter that allows for more or less easy implementation of the IEnumVariant interface. I also discovered that the IEnumVariant interface is defined in the ActiveX unit provided with Delphi, and that it uses stdole32.tpl as a type library.OLE enumerator base classesHere are the enumerator base and the generic enumerator base classes:type TSGOLEVariantEnumeratorAdapterBase=class (TAutoIntfObject,IEnumVariant) private class var vOLETypeLib:ITypeLib; private class function GetOLETypeLib: ITypeLib; static; class Destructor ClassDestroy; // for IOLEEnumVariant function Next(celt: LongWord; var rgvar: OleVariant; out pceltFetched: Longword): HResult; stdcall; function Skip(celt: LongWord): HResult; stdcall; function Reset: HResult; stdcall; function Clone(out Enum: IEnumVariant): HResult; stdcall; protected class property OLETypeLib:ITypeLib read GetOLETypeLib; function DoNext(aFetchRequestCount: LongWord; var rgvar: OleVariant; out aActuallyFetchedCount: Longword): boolean; virtual; abstract; function DoSkip(aSkipCOunt: LongWord): boolean; virtual; abstract; function DoReset: boolean; virtual; function DoClone(out Enum: IEnumVariant): boolean; virtual; public constructor Create; end; TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>=class (TSGOLEVariantEnumeratorAdapterBase,ISGEnumerator<TEnumeratedType>) private FSourceEnumerator:ISGEnumerator<TEnumeratedType>; protected function MapCurrentToVariant(aCurrent:TEnumeratedType):olevariant; virtual; function DoReset: boolean; override; function DoClone(out Enum: IEnumVariant): boolean; override; function DoNext(aFetchRequestCount: LongWord; var rgvar: OleVariant; out aActuallyFetchedCount: Longword): boolean; override; function DoSkip(aSkipCOunt: LongWord): boolean; override; property SourceEnumerator:ISGEnumerator<TEnumeratedType> read FSourceEnumerator implements ISGEnumerator<TEnumeratedType>; public constructor Create(const aSourceEnumerator:ISGEnumerator<TEnumeratedType>); end;I struggled with the instantiation TAutoIntfObject base class and the correct type libraries, but I finally managed to work it out like below. I use a class var for the type library to avoid loading it over and over again.constructor TSGOLEVariantEnumeratorAdapterBase.Create;begin inherited Create(OLETypeLib,IEnumVariant);end;class destructor TSGOLEVariantEnumeratorAdapterBase.ClassDestroy;begin vOLETypeLib:=nil;end;class function TSGOLEVariantEnumeratorAdapterBase.GetOLETypeLib: ITypeLib;begin // HH we cannot lose Win.ComServ in a package // thats why I cloned the call or LoadTypeLibrary here if not Assigned(vOLETypeLib) then OleCheck(LoadTypeLibEx('stdole32.tlb', REGKIND_NONE, vOLETypeLib)); Result:=vOLETypeLib;end;After that I implemented the interface's methods, also allowing for exceptions to be handled correctly for the dispintf. The actual "meat" of the loop implementation are put in virtual methods called from the interface methods. The interface methods look like this:function TSGOLEVariantEnumeratorAdapterBase.Next(celt: LongWord; var rgvar: OleVariant; out pceltFetched: Longword): HResult;VAR lActuallyFetched:longword;begin lActuallyFetched:=0; try if DoNext(celt,rgvar,lActuallyFetched) then Result:=S_OK else Result:=S_FALSE; if Assigned(@pceltFetched) then pceltFetched:=lActuallyFetched; except Result:=SafeCallException(ExceptObject,ExceptAddr); end;end;function TSGOLEVariantEnumeratorAdapterBase.Skip(celt: LongWord): HResult;begin try if DoSkip(celt) then Result:=S_OK else Result:=S_FALSE; except Result:=SafeCallException(ExceptObject,ExceptAddr); end;end;function TSGOLEVariantEnumeratorAdapterBase.Reset: HResult;begin try if DoReset then Result:=S_OK else Result:=S_FALSE; except Result:=SafeCallException(ExceptObject,ExceptAddr); end;end;function TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>.DoClone(out Enum: IEnumVariant): boolean;VAR lCloneIntf:ISGEnumeratorClone; lCLonedEnumerator:ISGEnumerator<TEnumeratedType>;begin if Supports(FSourceEnumerator,ISGEnumeratorClone,lCloneIntf) then begin lCLonedEnumerator:=ISGEnumerator<TEnumeratedType>(lCloneIntf.Clone); Enum:=TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>(self.ClassType).Create(lCLonedEnumerator); Result:=True; end else Result :=inherited;end;function TSGOLEVariantEnumeratorAdapterBase.Clone(out Enum: IEnumVariant): HResult;begin try if DoClone(Enum) then Result:=S_OK else Result:=S_FALSE; except Result:=SafeCallException(ExceptObject,ExceptAddr); end;end;Clone and ResetI have added virtual methods for the Clone and Reset methods, but these are actually not called from within Excel VBA in my example,Generic IEnumVariant adapter classThe next thing was to create the Generic adapter which overrides the Doxxx methods and adds a MapCurrentToVariant routine to get the 'Current' value from the source enumerator to the output variant. This routine is virtual so it can be overridden for special or more efficient transformations.Thus the generic class looks like this:TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>=class (TSGOLEVariantEnumeratorAdapterBase,ISGEnumerator<TEnumeratedType>) private FSourceEnumerator:ISGEnumerator<TEnumeratedType>; protected function MapCurrentToVariant(aCurrent:TEnumeratedType):olevariant; virtual; function DoReset: boolean; override; function DoClone(out Enum: IEnumVariant): boolean; override; function DoNext(aFetchRequestCount: LongWord; var rgvar: OleVariant; out aActuallyFetchedCount: Longword): boolean; override; function DoSkip(aSkipCOunt: LongWord): boolean; override; property SourceEnumerator:ISGEnumerator<TEnumeratedType> read FSourceEnumerator implements ISGEnumerator<TEnumeratedType>; public constructor Create(const aSourceEnumerator:ISGEnumerator<TEnumeratedType>); end;Implementing the overridden routines was pretty straightforward.constructor TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>.Create( const aSourceEnumerator: ISGEnumerator<TEnumeratedType>);begin FSourceEnumerator:=aSourceEnumerator; inherited Create;end;function TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>.MapCurrentToVariant(aCurrent: TEnumeratedType): olevariant;begin Result:=TValue.From<TEnumeratedType>(aCurrent).AsVariant;end;function TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>.DoNext(aFetchRequestCount: LongWord; var rgvar: OleVariant; out aActuallyFetchedCount: Longword): boolean;type TVariantList=array[0..0] of Olevariant;begin aActuallyFetchedCount:=0; while (aFetchRequestCount>0) and SourceEnumerator.MoveNext do begin dec(aFetchRequestCount); TVariantList(rgvar)[aActuallyFetchedCount]:=MapCurrentToVariant(SourceEnumerator.Current); inc(aActuallyFetchedCount); end; Result:=(aFetchRequestCount=0);end;function TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>.DoSkip(aSkipCOunt: LongWord): boolean;begin while (aSkipCount>0) and SourceEnumerator.MoveNext do dec(aSkipCount); Result:=(aSkipCOunt=0);end;I have added the Clone and Reset options later on, as they are not actually used by my application, so maybe for future usage. The implementations look like this:function TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>.DoClone(out Enum: IEnumVariant): boolean;VAR lCloneIntf:ISGEnumeratorClone; lCLonedEnumerator:ISGEnumerator<TEnumeratedType>;begin if Supports(FSourceEnumerator,ISGEnumeratorClone,lCloneIntf) then begin lCLonedEnumerator:=ISGEnumerator<TEnumeratedType>(lCloneIntf.Clone); Enum:=TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>(self.ClassType).Create(lCLonedEnumerator); Result:=True; end else Result :=inherited;end;function TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>.DoReset: boolean;VAR lResetIntf:ISGEnumeratorReset;begin if Supports(FSourceEnumerator,ISGEnumeratorReset,lResetIntf) then begin lResetIntf.Reset; Result:=True; end else Result := inherited;end;Finally, I decided to create an enumerable adapter class as well which may come in handy in some cases: TSGGenericOLEVariantEnumerableAdapter<TEnumeratedType>=class (TAutoIntfObject,ISGEnumerable<TEnumeratedType>) private FSourceEnumerable:ISGEnumerable<TEnumeratedType>; protected function Get__NewEnum: IUnknown; safecall; inline; property SourceEnumerable:ISGEnumerable<TEnumeratedType> read FSourceEnumerable implements ISGEnumerable<TEnumeratedType>; public constructor Create(const aTypeLib:ITypeLib;const aDispIntf:TGUID;const aSourceEnumerable:ISGEnumerable<TEnumeratedType>); end;The implementation of the class:constructor TSGGenericOLEVariantEnumerableAdapter<TEnumeratedType>.Create(const aTypeLib:ITypeLib;const aDispIntf:TGUID;const aSourceEnumerable:ISGEnumerable<TEnumeratedType>);begin FSourceEnumerable:=aSourceEnumerable; inherited Create(aTypeLib,aDispIntf);end;function TSGGenericOLEVariantEnumerableAdapter<TEnumeratedType>.Get__NewEnum: IUnknown;begin Result:=TSGGenericOLEVariantEnumeratorAdapter<TEnumeratedType>.Create(SourceEnumerable.GetEnumerator);end;At the spots where I plan to use my code, everything looks rather clean, and only little has to be implemented. Below is an example of the enumerator to get a bunch of object IDs from my actual application model: TAMDBObjIDEnumeratorAdapter=class (TSGGenericOLEVariantEnumeratorAdapter<integer>); TAMDBObjIDEnumerableAdapter=class (TSGGenericOLEVariantEnumerableAdapter<integer>,IAMObjectIDs,ISGEnumerable<integer>) public constructor Create(const aSourceEnumerable:ISGEnumerable<integer>); end;....constructor TAMDBObjIDEnumerableAdapter.Create(const aSourceEnumerable: ISGEnumerable<integer>);begin inherited Create(comserver.TypeLib,IAMObjectIDs,aSOurceEnumerable);end;The code has actually been tested using Excel and Delphi, but providing all the code with my internal solutions for the Delphi enumerators is way beyond the topic of this issue, that;s why I did not create a demo project for this. Who knows, if I find the time and enough upvotes/requests I may put some more energy in this.I hope my journey into finding a "working and clean" solution for this in Delphi will help others. 这篇关于我需要实现什么接口以允许以delphi编写的COM对象在VBA中使用ForEach?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-16 04:36