2005. augusztus 9., kedd

Get the published properties of an persistent object


Problem/Question/Abstract:

How to get the published properties of an persistent object / Using the pPropInfo-Pointer and the RTTI of Delphi

Answer:

The TypeInfo unit of Delphi declares several types and functions that gives you easy access to the puplished properties of an object and other informations.
You can obtain a list of the published properties of a class and get the name an type of each property.

The TypeInfo funtion returns a pointer to a type information record. The TypInfo unit declares a real type, that is, a pointer to a TTypeInfo record :

PTypeInfo = ^TTypeInfo;
TTypeInfo = record
  Kind: TTypeKind;
  Name: ShortString;
end;

The TTypeKind datatype describes the Datatype , returned by the GetTypeData function.

TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat,
  tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,
  tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray);
TTypeKinds = set of TTypeKind;

Well ... for our first step to access the objects published properties we need to use the PPropInfo-pointer.

PPropInfo = ^TPropInfo;
TPropInfo = packed record
  PropType: PPTypeInfo;
  GetProc: Pointer;
  SetProc: Pointer;
  StoredProc: Pointer;
  Index: Integer;
  Default: Longint;
  NameIndex: SmallInt;
  Name: ShortString;
end;

To clarify it, please take a look at this example :

function GetFontSize(Obj: TPersistent): Integer;
{
in this Procedure we want to get the pPropInfo-pointer - pointing
on the Font-Property from an arbitrary TPersistent-Class.
The return-value in this instance will be the font-size ( if the font
property exists , if not -> the return value will be -1 )
}
var
  PropInfo: PPropInfo;
begin
  RESULT := -1;
  // Get the PPropInfo-Pointer for Font of the TPersistent obj
  PropInfo := GetPropInfo(Obj, 'Font');
  // At first we will find out if the property FONT exists
  if PropInfo = nil then
    EXIT; // The Property doesn't exists
  {
    TFont is not an ordinal-Type - therefore will have to control if
    Typekind of the TypeInfo-Class is set to tkClass
  }
  if PropInfo.PropType^.Kind <> tkClass then
    EXIT; // property isn't a tkClass type
  {
    now, we now that the TypeKind of die PropInfo-pointer is a class .
    last but not least we will use the GetObjectProp, the return-value
    of this function is a TObject. Subsequently, we will use this object as
    a TFont to get the Size value.
  }
  RESULT := ((GetObjectProp(Obj, PropInfo)) as TFont).Size;
end;

But to get the complete list of all properties of a TPersistent-Class we will need the pPropList-Type . This type is a simple pointer-array and the magic key to all Property-Informations and their structures.

Take a look at this :

procedure TForm1.Button1Click(Sender: TObject);

const
  tkOrdinal = [tkEnumeration, tkInteger, tkChar, tkSet, tkWChar]; //Filter

begin
  {
    in this method of the mainform-class we are seeking for all ordinal-type
    properties of the edit1-component. The from the GetPropertyList method
    returned list of all properties will be written into the Listbox1. You can
    replace the obj parameter with an arbitrary TObject ( but usually TPersistent
    objects ).
    For another filter please take a look at the TTypeKinds-set.
  }
  GetPropertyList(Edit1, ListBox1.Items, tkOrdinal);
end;

procedure GetPropertyList(Obj: TObject; List: TStrings; Filter: TTypeKinds);
var
  PropList: pPropList;
  count, i: Integer;
begin
  List.Clear;
  // Here we'll get the count of the given properties, ...
  Count := GetPropList(Obj.ClassInfo, Filter, nil);
  // ...and create room for the PropList,...
  GetMem(PropList, Count * SizeOf(PPropInfo));
  // ...get the Proplist-Data,...
  GetPropList(Obj.ClassInfo, Filter, PropList);
  // ...and write the property-names into the StringList
  for i := 0 to Count - 1 do
    List.Add(Proplist[i].Name);
end;

Nincsenek megjegyzések:

Megjegyzés küldése