我需要实现什么接口以允许以delphi编写的COM对象在VBA中使用ForEach? [英] What interface to I need to implement to allow ForEach in VBA on a COM object written in delphi?

查看:107
本文介绍了我需要实现什么接口以允许以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 object
set MyObject= CreateObject("MyObjectClass")

for each Item as integer in MyObject.IDList
  Debug.Write(Cstr(Item) & ";")
Next

What 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

解决方案

Summary

I 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 classes

Here 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 Reset I 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 class The 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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆