Delphi How to check if an instance of IInterface is an IInvokable

Viewed 200

I want to do a wrapper on my interface instances, and I intend to use the TVirtualInterface class to do this, but it only accepts IInvokable instances, is there any way I can implement the IsInterfaceIInvokable function so that it returns to me if the past instance is or not an IInvokable instance?

Code:

program InterfaceWrapper;

{$APPTYPE CONSOLE}

{$R *.res}


uses
   System.SysUtils,
   System.TypInfo,
   System.Rtti;

type
   IMinhaInterface = interface(IInvokable)
      ['{1DFA8F7B-57BF-44E5-BBBF-76492EE36CEA}']
      procedure Fazer;
   end;

   TMinhaInterfaceImp = class(TInterfacedObject, IMinhaInterface)
   public
      procedure Fazer;
   end;

   TWrapper = class(TVirtualInterface)
   private
      intF: IInterface;
   public
      constructor Create(const PIID: PTypeInfo; const intF: IInterface);
   end;

   { TMinhaInterfaceImp }

procedure TMinhaInterfaceImp.Fazer;
begin
   Writeln('procedure TMinhaInterfaceImp.Fazer');
end;

{ TWrapper }

constructor TWrapper.Create(const PIID: PTypeInfo; const intF: IInterface);
begin
   inherited Create(PIID);
   Self.intF := intF;
   Self.OnInvoke := procedure(Method: TRttiMethod; const Args: TArray<TValue>; out Result: TValue)
      begin
         try
            Writeln('Antes');
            Result := Method.Invoke(TValue.From(Self.intF), Copy(Args, 1, Length(Args) - 1));
            Writeln('Depois');
         except
            on E: Exception do
            begin
               Writeln(E.ClassName, ': ', E.Message);
            end;
         end;
      end;
end;

function IsInterfaceIInvokable(const intF: IInterface; const PIID: PTypeInfo): Boolean;
begin
   Result := intF is IInvokable;
end;

begin
   try
      var intF := TMinhaInterfaceImp.Create as IMinhaInterface;

      if IsInterfaceIInvokable(intF, TypeInfo(IMinhaInterface)) then
      begin
         intF := TWrapper.Create(TypeInfo(IMinhaInterface), intF) as IMinhaInterface;
      end;

      intF.Fazer;

      var str: string;
      Readln(str);
   except
      on E: Exception do
         Writeln(E.ClassName, ': ', E.Message);
   end;

end.
1 Answers

There are undocumented enum values in System.TypInfo.TIntfFlag (see RSP-24631)

So you can use this code - not only interface inheriting from IInvokable but if it has {$M+}:

function HasMethodInfo(typeInfo: PTypeInfo): Boolean;
type
  TIntfFlagEx = (ifHasGuid, ifDispInterface, ifDispatch, ifMethodInfo);
  TIntfFlagsEx = set of TIntfFlagEx;
begin
  Result := Assigned(typeInfo) and (typeInfo.Kind = tkInterface)
    and (ifMethodInfo in TIntfFlagsEx(typeInfo.TypeData.IntfFlags));
end;
Related