【问题标题】:How to determine if an IInterface descendant have a property?如何确定 IInterface 后代是否具有属性?
【发布时间】:2011-09-12 01:46:41
【问题描述】:

由于导入类型库,我有很多接口。所以,接口是这样的:

  ISomeCollection = dispinterface
    ['{6592E851-3D65-4D04-B5F3-B137667B816A}']
    procedure Remove(Identifier: OleVariant); dispid 2;
    function Add(Name: OleVariant; DatabaseType_ID: OleVariant): ERSModel; dispid 3;
    property _NewEnum: IUnknown readonly dispid -4;
    property Item[Identifier: OleVariant]: ERSModel readonly dispid 4;
    property _Item[Identifier: OleVariant]: ERSModel readonly dispid 0; default;
    property Count: Integer readonly dispid 1;
  end;

_NewEnum 是 Visual Basic for-each 循环语句消耗的习语(它与 Delphi 的 for-in 完全一样)的 COM 对象集合 - 尽管声明为 IUnknown,但它实际上是一个 IEnumVARIANT 接口。由于这是枚举集合项目的唯一方法,因此我解决了它:

{This class have just this class function} 
class function TVariantUtils.GetAs<T>(pModeloOleVar: OleVariant): T;
begin
  Result := (T(IUnknown(pModeloOleVar)));
end;

用途:

var 
  EnumColecction: IEnumVariant;
  // TEnumeratorObjects: This is a generic class to implement an enumerator over
  // an IEnumVARIANT interface
  ListOfSubObjects: TEnumaretorObjects; 
begin
  ...
  EnumCollection := TVariantUtils.GetAs<IEnumVariant>(Object.SomeCollection._NewEnum);
  ListOfSubObects := TEnumeratorObjects<ItemofSomeCollection>.Create(EnumCollection);
  ...
End;

构造函数接收一个 IEnumVariant 参数。我想要的是创建一个构造函数 接收 IInterface 并确定 ISomeCollection 是否具有 _NewEnum 属性 IUnknown 类型 - 并执行上述代码一次。 我不知道编译时接口的名称或 GUID。

Obs:delphi-xe 标签是因为我想知道机制,即使仅适用于 Delphi XE (即使我需要为此购买入门版)。 我用的是 D2010。

编辑: 我尝试使用 RTTI(它可以编译但不起作用):

constructor TEnumeratorVariant<T>.Create(pEnumeraVariante: IInterface);
var
  EnumVar: IEnumVariant;
  Contexto: TRttiContext;
  InfoTipo: TRttiType ;
  PropInfo: TRttiProperty;
  pTipo: PTypeInfo;
begin
  Contexto.Create;
  pTipo := TypeInfo(pEnumeraVariante);
  InfoTipo := Contexto.GetType(TypInfo(pEnumeraVariante));
  PropInfo := InfoTipo.GetProperty('_NewEnum');
  if Assigned(PropInfo) then
  begin
    Supports(PropInfo.GetValue(pEnumeraVariante), IEnumVariant, EnumVar);
    Create(EnumVar);
  end;
  Contexto.Free;
  PropInfo.Free;
  InfoTipo.Free;
end;

【问题讨论】:

  • delphi有反射吗?我认为这是使用其他语言的方式。
  • 是的,Delphi 有一个类似的概念,叫做 RTTI。我的最后一次编辑尝试了它.....
  • 当你说用 RTTI 做它不起作用时,它不起作用是什么?
  • @Mason:我并不是说它不起作用。我是说以我编码的方式它不起作用......所以我想知道正确的路径是什么 - 或者即使不存在正确的路径......
  • @Rob:这不是重点,Rob。我知道我可以做到。我不知道该怎么做:给定作为参数传递的 IDispatch 后代,如何检测它是否具有 _NewEnum 属性。

标签: delphi generics interface delphi-2010 delphi-xe


【解决方案1】:

尝试标准的IDispatch 方法(未测试,您可能需要调整它):

function GetEnumerator(const Disp: IDispatch): IEnumVariant;
var
  DispParams: TDispParams;
  ExcepInfo: TExcepInfo;
  Status: Integer;
  VarResult: OleVariant;
begin
  Result := nil;
  FillChar(DispParams, SizeOf(DispParams), 0);
  FillChar(ExcepInfo, SizeOf(ExcepInfo), 0);
  Status := Disp.Invoke(DISPID_NEWENUM, GUID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, DispParams, @VarResult, @ExcepInfo, nil);
  if Succeeded(Status) then
    Result := IUnknown(VarResult) as IEnumVariant
  else
    DispatchInvokeError(Status, ExcepInfo);
end;

【讨论】:

  • 顺便说一句,这正是 Rob 的评论 EnumCollection := Object.SomeCollection._NewEnum as IEnumVariant 中的代码所做的。但是通过像这里这样写出来,您可以将其更改为不引发异常,而是返回 TrueFalse 以指示该属性是否存在以及是否成功获取其值。
  • 另外,如果您坚持通过名称 '_NewEnum' 而不是它的 dispid (-4) 访问属性,您可以先调用 GetIDsOfNames 以获取 dispid 然后继续类似以上。
  • 请将@Result 更改为@VarResult。非常感谢,已经很完美了。
【解决方案2】:

你在正确的轨道上。 Delphi 的 RTTI 可以找到接口的方法,但是接口必须为这些方法生成 RTTI。默认情况下它不会这样做。你必须启用它。将{$M+} 指令放在类型库导入单元的顶部,它应该可以工作。

【讨论】:

    猜你喜欢
    • 2023-03-17
    • 2011-03-04
    • 2013-02-20
    • 2018-12-12
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多