views:

277

answers:

4

I'm trying to fetch an interface using D2010 RTTI.

program rtti_sb_1;
{$APPTYPE CONSOLE}
{$M+}
uses
  SysUtils,
  Rtti,
  mynamespace in 'mynamespace.pas';
var
  ctx:      TRttiContext;
  RType:    TRttiType;
  MyClass:  TMyIntfClass;
begin
  ctx := TRttiContext.Create;
  MyClass := TMyIntfClass.Create;
  // This prints a list of all known types, including some interfaces.
  // Unfortunately, IMyPrettyLittleInterface doesn't seem to be one of them.
  for RType in ctx.GetTypes do
    WriteLn(RType.Name);
  // Finding the class implementing the interface is easy.
  RType := ctx.FindType('mynamespace.TMyIntfClass');
  // Finding the interface itself is not.
  RType := ctx.FindType('mynamespace.IMyPrettyLittleInterface');
  MyClass.Free;
  ReadLn;
end.

Both IMyPrettyLittleInterface and TMyIntfClass = class(TInterfacedObject, IMyPrettyLittleInterface) are declared in mynamespace.pas, in particular

unit mynamespace;
interface
type
  IMyPrettyLittleInterface = interface
    ['{6F89487E-5BB7-42FC-A760-38DA2329E0C5}']
  end;
  TMyIntfClass = class(TInterfacedObject, IMyPrettyLittleInterface)
  end;
  //...

Do anyone know why this doesn't work? Is there a way to solve my problem? Thanks in advance!

A: 

Does IMyPrettyLittleInterface have a GUID? I don't think the RTTI system will recognize it if it doesn't.

Mason Wheeler
Hi Mason! Yes, it does. I updated my post to reflect the actual implementation. A thought struck me: my D2010 was updated the other day, and I had to add the {$M+} myself, which I believe was done for me in the past. Perhaps RTTI got broken in the last update?
conciliator
Have you tried adding $M+ to the mynamespace unit as well? I assume you did, but your code doesn't show it.
Paul-Jan
A GUID is not required to have the Delphi 2010 RTTI work.
Robert Love
A: 

RTTI is generated for types declared while the $M switch is active. Like all compiler directives, that switch has per-unit scope. You set it in your DPR file, but that setting has no effect in the unit where you declared your types.

Set that switch prior to your interface and class declarations:

type
  {$M+}
  IMyPrettyLittleInterface = interface
    ['{6F89487E-5BB7-42FC-A760-38DA2329E0C5}']
  end;
  TMyIntfClass = class(TInterfacedObject, IMyPrettyLittleInterface)
  end;
Rob Kennedy
Thanks Rob (and Paul-Jan too), I'd forgot to include the $M switch. Unfortunately, it still doesn't work. Can someone reproduce my problem?
conciliator
$M+ does not apply to Delphi 2010 RTTI at all.
Robert Love
+6  A: 

This is a strange behavior you have found. You can find the type using:

RType := ctx.GetType(TypeInfo(IMyPrettyLittleInterface));

But after you have done this once you can access it by name, so if you need to access it by Name you can do the following to make it work.

Example Program:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Rtti,
  TypInfo,
  Unit1 in 'Unit1.pas';

var
 ctx : TRttiContext;
 IType : TRttiType;
begin;
  ctx := TRttiContext.Create;
  IType := ctx.FindType('Unit1.IExample');
  if Assigned(IType) then
  begin
    writeln('Found');
    Writeln(IType.QualifiedName);
  end
  else
    writeln('Not Found');
  ReadLn;
end.

Example Unit:

unit Unit1;

interface

type
  IExample = interface
    ['{D61F3245-13FB-44BF-A89D-BB358FAE7D19}']
  end;

implementation
uses Rtti;
var
 C : TRttiContext;

initialization
 C.GetType(TypeInfo(IExample));

end.
Robert Love
Hi Robert! Thank you for your answer - it works great! It seems strange, though. For the moment, I'll live with a workaround, but I sure hope it'll be fixed in the future. You think I should report to QC?
conciliator
Yes I think it should be QC'ed.
Robert Love
Done. It is now reported as QC #85277.
conciliator
{$STRONGLINKTYPES ON} will also work, see my answer for other details.
Barry Kelly
+3  A: 

The problem is that neither the VMT nor the typeinfo of classes which implement an interface contain any references to the typeinfo of those interfaces. The typeinfo for the interfaces is then eliminated by the linker if not otherwise referenced in the program. To fix this, there would need to be a typeinfo format change for classes to reference the implemented interfaces' typeinfo, or else all interfaces would need to be strongly linked into the executable. Other kinds of fixes, such as strong-linking interfaces that are implemented by linked-in classes without actually including references in the class typeinfo, are problematic owing to how the compiler's integrated smart linker works.

Another way around this issue is to use the directive {$STRONGLINKTYPES ON}. This will cause all types in the EXE, BPL or DLL's root type table (the index that lets the RTL map qualified names to types) to be linked in with strong fixups rather than weak fixups. Symbols that only have weak fixups referencing them are eliminated by the smart linker; if one or more strong fixups references the symbol, then it gets included in the final binary and the weak references don't get nil'ed (actually @PointerToNil) out.

As described in other answers, TypeInfo(ITheInterface) in non-dead code fixes the problem; this is because the TypeInfo() magic function creates a strong fixup to the interface.

Barry Kelly