Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Bob Swart (aka Dr.Bob) - Medical Officer Delphi in a Nutshell
 Under The Hood #1 - RTTI
See Also: Delphi Papers and Dr.Bob's Examines Columns

RTTI, or Run-Time Type Information, is a powerful but poorly documented feature of Delphi. Browsing the on-line help, you'll get only a few lines of examples using the IS and AS keywords. There's much more to RTTI than that. For example, did you know that the Object Inspector uses the RTTI to get a list of published properties and events to display? And if the Object Inspector can, then so can we, right?

The object Inspector of Delphi is able to "walk" the run-time type information to get a list of all published properties (and types) and events of a given component (in this case Form1 of type TForm).

In order to get our hands on the run-time type information, we first need to know the internals, i.e. the internal datastructures or algorithms we need to use to access that information. For this, we need to check out the file TYPINFO.INT (in your DELPHI\DOC directory). However, the first few lines give us a fair warning (note: this is the Delphi 1.0 version, so don't expect anything from this article to work with Delphi 2.0):

  {*******************************************************}
  {                                                       }
  {       Delphi Visual Component Library                 }
  {                                                       }
  {       Copyright (c) 1995 Borland International        }
  {                                                       }
  {  Warning:                                             }
  {    The interface section of this file will change     }
  {    in future versions of Delphi.                      }
  {                                                       }
  {*******************************************************}
After we've seen (and disregarded) the warning, let's see if we can at least get to know the internals of the run-time type information for Delphi 1.0 (we'll get back to RTTI for Delphi 2.0 when we try to get an enum's name).

Basically, we have an instance of type TClass, and want a list of published properties. So, let's write a routine that does just this.

  procedure GetPropertyNames(AClass: TClass; var PropertyNames: TStringList);
  { Given a TClass, put all its published properties in the PropertyNames
    TStringList, with the name of the parent where the property is first
    being published.
  }
Every instance of type TClass has a method called ClassInfo that returns a pointer to a TTypeInfo record that contains the RTTI for that particular instance. And this TypeInfo record has a field called Kind to check the kind of the instance (unknown, integer, char, enumeration, float, string, set, class or method).

If we indeed have a class (i.e. if AClass.ClassInfo^.Kind = tkClass) then we can pursue further on the list. We can use the function GetTypeData on the TypeInfo record to get a pointer to a structure that holds the TypeData. This record holds the following information for an tkClass kind of instance:

  ClassType: TClass;
  ParentInfo: PTypeInfo;
  PropCount: SmallInt;
  UnitName: string;
So, now we know the parent as well. And if we need a complete list of our class instance's published properties, then we should need not only get the published properties of the class itself, but for all its parent classes as well. This smells like recursion (as you'll see in the final code), with end-condition being the name of the parent class which is nil for the final parent.

The PropCount gives us indication how many properties are defined for this particular instance at this level. We have to use the procedure GetPropInfos to get the information for all properties at this level. Then, we need to walk this list to get the name and type. For each element, the PropInfo holds the following information:

  TPropInfo = packed record
    PropType: PTypeInfo;
    GetProc: Pointer;
    SetProc: Pointer;
    StoredProc: Pointer;
    Index: SmallInt;
    Default: Longint;
    NameIndex: SmallInt;
    Name: String;
  end;
We're only interested in the name here, but we could of course also explore the type of the property, and walk the RTTI chain again. Soon, this leads to complex maces of run-time type interlinked information, and I fully understand why this information will probably change with each next release of Delphi.

Anyway, the complete source code for the unit Hood1 and function GetPropertyNames is as follows. See if you can fin out how it works (it helps if you have a printout of TYPINFO.INT on your desk), and as an exercise to the reader: try to write a similar function that lists the unit names that are needed for a particular class instance and all its properties (for this you need to explore the type of the property as well). Have fun!

  unit Hood1;
  interface
  uses Classes;

   procedure GetPropertyNames(AClass: TClass; var PropertyNames: TStringList);
   { Given a TClass, put all its published properties in the PropertyNames
     TStringList, with the name of the parent where the property is first
     being published.
     Note: the PropertyNames TStringList must be Created before this procedure
     is called (in order to fill it with the list of property names).
   }

  implementation
  uses TypInfo;

    procedure GetPropertyNames(AClass: TClass; var PropertyNames: TStringList);
    var
      TypeInfo: PTypeInfo;
      TypeData: PTypeData;
      PropList: PPropList;
      ParentName: PString;
      i: Integer;
    begin
      if Assigned(PropertyNames) then { check to see if the TStringList is valid }
      begin
        PropertyNames.Clear;
        TypeInfo := AClass.ClassInfo;
        if TypeInfo^.Kind = tkClass then
        begin
          TypeData := GetTypeData(TypeInfo);
          ParentName := @TypeData^.ParentInfo^.Name;
          if (ParentName^ <> TypeInfo^.Name) and
             (GetClass(ParentName^) <> nil) then { recursive! }
            GetPropertyNames(GetClass(ParentName^),PropertyNames);
          if TypeData^.PropCount > 0 then
          begin
            PropertyNames.Add(TypeInfo^.Name+':');
            new(PropList);
            GetPropInfos(TypeInfo, PropList);
            for i:=0 to Pred(TypeData^.PropCount) do
              if PropertyNames.IndexOf(PropList^[i]^.Name) < 0 then
                PropertyNames.Add(PropList^[i]^.Name);
            Dispose(PropList)
          end
        end
      end
    end {GetPropertyNames};
  end.


This webpage © 1996-2017 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.