Wie PPropInfo verwenden???

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
thosch
Beiträge: 307
Registriert: Mo 10. Jul 2017, 20:32

Wie PPropInfo verwenden???

Beitrag von thosch »

Hallo,

ich habe folgende Definition für TPropInfo für Delphi gefunden:

Code: Alles auswählen

 TPropInfo = packed record
    PropType: PPTypeInfo;
    GetProc: Pointer;
    SetProc: Pointer;
    StoredProc: Pointer;
    Index: Integer;
    Default: Integer;
    NameIndex: SmallInt;
    Name: TSymbolName;
    function NameFld: TTypeInfoFieldAccessor; inline;
    function Tail: PPropInfo; inline;
  end;
In derjenigen für Freepascal fehlt die function NameFld und Name

Ich sehe auch kein Feld, das die Daten eines Feldes einer Klasse aufnimmt.

Wie kann ich da den Inhalt des Datenfeldes (property oder Feldvariable) verändern?

TPropList ist ja ein Array of TPropInfo. Damit sollte ich das jeweilige Feld per Index erreichen. Wie kann ich es aber dann in seinem Inhalt verändern?

Eine Unit, die mir Typinformation anzeigt, habe ich hier, aus Delphi 4 Developer Handbook:

Code: Alles auswählen

unit MainFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, DBClient, MidasCon, MConnect;

type

  TMainForm = class(TForm)
    pnlTop: TPanel;
    pnlLeft: TPanel;
    lbBaseClassInfo: TListBox;
    spSplit: TSplitter;
    lblBaseClassInfo: TLabel;
    pnlRight: TPanel;
    lblClassProperties: TLabel;
    lbPropList: TListBox;
    lbSampClasses: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure lbSampClassesClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation
uses TypInfo;

{$R *.DFM}

function CreateAClass(const AClassName: string): TObject;
{ This method illustrates how you can create a class from the class name. Note
  that this requires that you register the class using RegisterClasses() as
  show in the initialization method of this unit. }
var
  C : TFormClass;
  SomeObject: TObject;
begin
  C := TFormClass(FindClass(AClassName));
  SomeObject := C.Create(nil);
  Result := SomeObject;
end;


procedure GetBaseClassInfo(AClass: TObject; AStrings: TStrings);
{ This method obtains some basic RTTI data from the given object and adds that
  information to the AStrings parameter. }
var
  ClassTypeInfo: PTypeInfo;
  ClassTypeData: PTypeData;
  EnumName: String;
begin
  ClassTypeInfo := AClass.ClassInfo;
  ClassTypeData := GetTypeData(ClassTypeInfo);
  with AStrings do
  begin
    Add(Format('Class Name:     %s', [ClassTypeInfo.Name]));
    EnumName := GetEnumName(TypeInfo(TTypeKind), Integer(ClassTypeInfo.Kind));
    Add(Format('Kind:           %s', [EnumName]));
    Add(Format('Size:           %d', [AClass.InstanceSize]));
    Add(Format('Defined in:     %s.pas', [ClassTypeData.UnitName]));
    Add(Format('Num Properties: %d',[ClassTypeData.PropCount]));
  end;
end;

procedure GetClassAncestry(AClass: TObject; AStrings: TStrings);
{ This method retrieves the ancestry of a given object and adds the
  class names of the ancestry to the AStrings parameter. }
var
  AncestorClass: TClass;
begin
  AncestorClass := AClass.ClassParent;
  { Iterate through the Parent classes starting with Sender's
    Parent until the end of the ancestry is reached. }
  AStrings.Add('Class Ancestry');
  while AncestorClass <> nil do
  begin
    AStrings.Add(Format('    %s',[AncestorClass.ClassName]));
    AncestorClass := AncestorClass.ClassParent;
  end;
end;


procedure GetClassProperties(AClass: TObject; AStrings: TStrings);
{ This method retrieves the property names and types for the given object
  and adds that information to the AStrings parameter. }
var
  PropList: PPropList;
  ClassTypeInfo: PTypeInfo;
  ClassTypeData: PTypeData;
  i: integer;
  NumProps: Integer;
begin

  ClassTypeInfo := AClass.ClassInfo;
  ClassTypeData := GetTypeData(ClassTypeInfo);

  if ClassTypeData.PropCount <> 0 then
  begin
    // allocate the memory needed to hold the references to the TPropInfo
    // structures on the number of properties.
    GetMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
    try
      // fill PropList with the pointer references to the TPropInfo structures
      GetPropInfos(AClass.ClassInfo, PropList);
      for i := 0 to ClassTypeData.PropCount - 1 do
        // filter out properties that are events ( method pointer properties)
        if not (PropList[i]^.PropType^.Kind = tkMethod) then
          AStrings.Add(Format('%s: %s', [PropList[i]^.Name, PropList[i]^.PropType^.Name]));

      // Now get properties that are events (method pointer properties)
      NumProps := GetPropList(AClass.ClassInfo, [tkMethod], PropList);
      if NumProps <> 0 then begin
        AStrings.Add('');
        AStrings.Add('   EVENTS   ================ ');
        AStrings.Add('');
      end;
      // Fill the AStrings with the events. 
      for i := 0 to NumProps - 1 do
          AStrings.Add(Format('%s: %s', [PropList[i]^.Name, PropList[i]^.PropType^.Name]));

    finally
      FreeMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
    end;
  end;

end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  // Add some example classes to the list box.
  lbSampClasses.Items.Add('TApplication');
  lbSampClasses.Items.Add('TButton');
  lbSampClasses.Items.Add('TForm');
  lbSampClasses.Items.Add('TListBox');
  lbSampClasses.Items.Add('TPaintBox');
  lbSampClasses.Items.Add('TMidasConnection');
  lbSampClasses.Items.Add('TFindDialog');
  lbSampClasses.Items.Add('TOpenDialog');
  lbSampClasses.Items.Add('TTimer');
  lbSampClasses.Items.Add('TComponent');
  lbSampClasses.Items.Add('TGraphicControl');
end;

procedure TMainForm.lbSampClassesClick(Sender: TObject);
var
  SomeComp: TObject;
begin
  lbBaseClassInfo.Items.Clear;
  lbPropList.Items.Clear;

  // Create an instance of the selected class. 
  SomeComp := CreateAClass(lbSampClasses.Items[lbSampClasses.ItemIndex]);
  try
    GetBaseClassInfo(SomeComp, lbBaseClassInfo.Items);
    GetClassAncestry(SomeComp, lbBaseClassInfo.Items);
    GetClassProperties(SomeComp, lbPropList.Items);
  finally
    SomeComp.Free;
  end;
end;

initialization
begin
  RegisterClasses([TApplication, TButton, TForm, TListBox, TPaintBox,
    TMidasConnection, TFindDialog, TOpenDialog, TTimer, TComponent,
    TGraphicControl]);
end;

end.
[code]

Wie kann ich dann die Feldinhalte verändern, wie das im Objektinspektot der IDE möglich ist?

Benutzeravatar
theo
Beiträge: 9911
Registriert: Mo 11. Sep 2006, 19:01

Re: Wie PPropInfo verwenden???

Beitrag von theo »

thosch hat geschrieben:
So 22. Jan 2023, 21:47
Wie kann ich da den Inhalt des Datenfeldes (property oder Feldvariable) verändern?
z.B. so:

Code: Alles auswählen

 SetPropValue(Button1, 'Caption', 'Test');    
Eine Liste der Properties bekommt man z.B. so:

Code: Alles auswählen

procedure PropDump(AObject: TObject);
var
  Loop,count :Integer;
  List: TPropList;
begin
    Count := GetPropList(AObject.ClassInfo, [tkInteger, tkChar, tkFloat,
      tkInt64, tkUString,tkWString, tkWChar, tkLString, tkEnumeration
      ,tkSString, tkBool, tkQWord, tkUChar, tkAString
      ], @List);
    for Loop := 0 to Pred(Count) do
     Form1.Memo1.Lines.Add(List[Loop]^.Name+' ('+List[Loop]^.PropType^.Name+')');
end;  

PascalDragon
Beiträge: 670
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Wie PPropInfo verwenden???

Beitrag von PascalDragon »

thosch hat geschrieben:
So 22. Jan 2023, 21:47
In derjenigen für Freepascal fehlt die function NameFld und Name
NameFld wurde erst in vergleichsweise junger Vergangenheit zu Delphis TPropInfo hinzugefügt und gibt letztlich einfach nur ein Hilfsrecord zurück, welches dafür sorgt, dass der String, der von Name beschrieben wird, die richtige Kodierung hat (da Delphi Unicode Identifier unterstützt). FPC braucht dies (noch) nicht und deswegen kannst du direkt Name verwenden (welcher nicht fehlt).
thosch hat geschrieben:
So 22. Jan 2023, 21:47
Ich sehe auch kein Feld, das die Daten eines Feldes einer Klasse aufnimmt.
Die Daten des Feldes selbst sind auch nicht in der RTTI enthalten. Die RTTI beschreibt wie du zusammen mit einer Klasseninstanz auf die Daten zugreifen kannst.
thosch hat geschrieben:
So 22. Jan 2023, 21:47
Wie kann ich da den Inhalt des Datenfeldes (property oder Feldvariable) verändern?
Wie theo geschrieben hat sind GetPropValue, SetPropValue, Get<Type>Prop und Set<Type>Prop zuständig, welche sich um die schmutzigen Details des Ganzen kümmern. Alternativ kannst du auch die objekt orientierten Typen der Rtti Unit nutzen.
FPC Compiler Entwickler

thosch
Beiträge: 307
Registriert: Mo 10. Jul 2017, 20:32

Re: Wie PPropInfo verwenden???

Beitrag von thosch »

Danke für Eure Antworten.

Leider erhalte ich einen EPropertyError, wenn ich Eigenschaften zuweisen oder deren Werte erhalten will.

Wie muss ich die Eigenschaft angeben?

AUsgehend von Theo's Tipp habe ich nach seiner Anleitung die Eigenschaften so hier gesammlt. Momentan nur die Eigenschaft Name.

var Count,Loop: Integer; List: TProplIst;

Count := GetPropList(aCheckBox.ClassInfo, [tkInteger, tkChar, tkFloat,
tkInt64, tkUString,tkWString, tkWChar, tkLString, tkEnumeration
,tkSString, tkBool, tkQWord, tkUChar, tkAString
], @List);

Dann lese ich so hier den Wert meiner Eigenschaft aus:

aTextField := TTextField.Create(aForm,'tkClass',20,70,GetStrProp(aCheckBox,List[Loop]^.Name );

Wenn ich nur 'Name' als Namen der Eigenschaft, die wirklich 'Name' heißt, erhalte ich deselbe Exception. Mit SetStrProp das Gleiche.

Was muss ich da anders machen?

Benutzeravatar
theo
Beiträge: 9911
Registriert: Mo 11. Sep 2006, 19:01

Re: Wie PPropInfo verwenden???

Beitrag von theo »

thosch hat geschrieben:
Di 24. Jan 2023, 19:56
Was muss ich da anders machen?
Da müsste man zuerst verstehen, was du machst und was überhaupt dein Ziel ist.
Was soll denn TTextField.Create() sein? Woher sollen wir das wissen?

thosch
Beiträge: 307
Registriert: Mo 10. Jul 2017, 20:32

Re: Wie PPropInfo verwenden???

Beitrag von thosch »

Mein Ziel ist, einen Objektinspektor nachzubauen wie es ihn in Lazarus gibt, der die eingetragenen Eigenschafts- und Ereigniswerte sowohl aus der/den darin angezeigten Klasse(n) sowohl verändern, als auch die betoffenen Klassen(n) mit den veränderten Werten anzuzeigen. Eben das was Lazatus damit macht. Nur mit völlig eigenem Code.

Wobei ich die Möglichkeiten von Object Pascal schon nutzen möchte, aber nicht die RTTI Controls, weil die wohl schon Dinde enthalten deren Arbeitsweise ich verstehen will.

So stammen meine Controls nicht von TComponent ab. Sie stammen von TObject ab, sollen aber die codierten Eigenschaften (Propertis) in einem selber hergestellten Objektinspektor angezeigt und verändert werden können.

TTextField.Create() ist mein Konstriktor für ein eigenes Objekt, das nicht von TComponent sondern von TObject abstammt und sich wie TLabel aus Lazarus verhält, womit ich also Text anzeigen kann. aForm ist auch eine eigene TForm Klasse, die von TObjekt abstammt, sich wie ein TForm aus Lazarus verhalten soll.

Kann ja sein, dass Lazarus TForm da einfacher zu nutzen wäre, aber warum ist das so, wie funktioniert das Zusammenspiel von TForm und dem Objektinspektor?

Wie erforchen Physiker das Weltall, wenn sie nicht zum Beobachtungsobjekt reisen können? Seie experimentieren auf der Erde, simulieren die Vorgänge, zum Beispiel im Sonneninneren.

Ich baue einen eigenen Objektinspektor und suche nun die Möglichkeit die Eigenschafts- und Ereigniswerte da rein zu bringen und wieder daraus zu lesen.

Socke
Lazarusforum e. V.
Beiträge: 3105
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Wie PPropInfo verwenden???

Beitrag von Socke »

thosch hat geschrieben:
Mi 25. Jan 2023, 12:34
So stammen meine Controls nicht von TComponent ab. Sie stammen von TObject ab, sollen aber die codierten Eigenschaften (Propertis) in einem selber hergestellten Objektinspektor angezeigt und verändert werden können.
Damit der Compiler RTTI für eine Klasse generiert müssen zwei Bedingungen erfüllt sein: Vielleicht zeigst du uns einmal ein wenig mehr Code, z.B. die Deklaration von aCheckBox. Auch ist der Fehlertext in EPropertyError hilfreich um die genaue Ursache zu identifizieren.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
theo
Beiträge: 9911
Registriert: Mo 11. Sep 2006, 19:01

Re: Wie PPropInfo verwenden???

Beitrag von theo »

thosch hat geschrieben:
Mi 25. Jan 2023, 12:34
Wie erforchen Physiker das Weltall, wenn sie nicht zum Beobachtungsobjekt reisen können? Seie experimentieren auf der Erde, simulieren die Vorgänge, zum Beispiel im Sonneninneren.
Gutes Beispiel. :D
Es entspricht aber nicht deinem Fall.
Dein Beobachtungsobjekt ist sogar ohne Fernrohr zu sehen in: <lazarus>/components/ideintf/objectinspector.pp

Und es kann anscheinend sogar ausserhalb der IDE verwendet werden. Jedenfalls steht dort:
// IMPORTANT: the object inspector is a tool and can be used in other programs
// too. Don't put Lazarus IDE specific things here.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1214
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Wie PPropInfo verwenden???

Beitrag von fliegermichl »

theo hat geschrieben:
Mi 25. Jan 2023, 13:32
thosch hat geschrieben:
Mi 25. Jan 2023, 12:34
Wie erforchen Physiker das Weltall, wenn sie nicht zum Beobachtungsobjekt reisen können? Seie experimentieren auf der Erde, simulieren die Vorgänge, zum Beispiel im Sonneninneren.
Gutes Beispiel. :D
Es entspricht aber nicht deinem Fall.
Dein Beobachtungsobjekt ist sogar ohne Fernrohr zu sehen in: <lazarus>/components/ideintf/objectinspector.pp

Und es kann anscheinend sogar ausserhalb der IDE verwendet werden. Jedenfalls steht dort:
// IMPORTANT: the object inspector is a tool and can be used in other programs
// too. Don't put Lazarus IDE specific things here.
Kann es. Ich hatte das in einem Programm verwendet. Man muß nur das Package IDEIntf zu den Anforderungen hinzufügen.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5783
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Wie PPropInfo verwenden???

Beitrag von af0815 »

theo hat geschrieben:
Mi 25. Jan 2023, 13:32
Und es kann anscheinend sogar ausserhalb der IDE verwendet werden. Jedenfalls steht dort:
// IMPORTANT: the object inspector is a tool and can be used in other programs
// too. Don't put Lazarus IDE specific things here.
Das ist richtig, ich habe ihn schon unabhängig verwendet. Ist nicht unbedingt die einfachste Kost :-)

Da habe ich, soweit mich mein Gedächnis nicht in Stich lässt, herumgespielt https://github.com/afriess/LazarusBug0032919 keine Ahnung ob der Code dort noch irgendwie geht.

Edit: Nein er geht nicht, da es einige Änderungen im System gegeben hat.

Edit1: Der Code geht, wenn man folgende Procedure auskommentiert: BuCreateFormIntfClick
Einfach starten und "Create normal Form" Button verwenden, es öffnet sich der OI mit einem dynamisch erzeugten Form mit Panels, die verschachtelt sind.

Edit2: Projekt mal angehängt
Dateianhänge
GuiOITest.zip
(65.99 KiB) 5-mal heruntergeladen
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

wennerer
Beiträge: 411
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon, Lazarus Stable 2.2.0 (rev lazarus_2_2_0) FPC 3.2.2 x86_
CPU-Target: x86_64-linux-gtk2

Re: Wie PPropInfo verwenden???

Beitrag von wennerer »

@af0815: Danke für das schöne Beispiel.
Viele Grüße
Bernd

thosch
Beiträge: 307
Registriert: Mo 10. Jul 2017, 20:32

Re: Wie PPropInfo verwenden???

Beitrag von thosch »

fliegermichl hat geschrieben: Es entspricht aber nicht deinem Fall.
Dein Beobachtungsobjekt ist sogar ohne Fernrohr zu sehen in: <lazarus>/components/ideintf/objectinspector.pp

... nur wenn man den Quellcode auch versteht. Sonst ist es wie eine Blckbox, die ich zwar verwenden kann aber deren Arbeitsweise ich nicht verstehe. Deshalb das eigene Projekt, was ich da codiert habe, verstehe ich dann auch. Insofern ist mein Beobachtungsobjekt zwar "sogar ohne Fernrohr zu sehen" aber dennoch unverständlich wie ein völlig neues unerforschtes astronomisches Objekt nahe unseres Sonnensystems oder mittendrin.
fliegermichl hat geschrieben: Und es kann anscheinend sogar ausserhalb der IDE verwendet werden. Jedenfalls steht dort: ...
@af0815 hat ja dafür ein Beispiel gegeben, Danke dafür, werd ich testen. Aber wie Betriebssystemunabhängig ist dieser Objektinspektor. Der enthält doch sicher einige Abhängigkeiten von Linux oder Windows? Wie schaut es damit bei den anderen Units in diesem Ordner aus?

Ich habe jetzt die Basisklasse von TmyCheckBox von TPersistent abgeleitet und in der Unit mit der Basisklasse, nun von TPersistent, nicht mehr von TObject abgeleitet, den Compilerschalter {$TypInfo} gesetzt.

Leider erhalte ich noch immer die Exception EPropertyError!

Was kann ich noch tun?


[EDIT] Bei dem GUIOITest Projekt werden viele Units nicht gefunden, nur weil die Projektdatei mit den Pfadeinträgen unvollständig ist. [EDIT]
Zuletzt geändert von thosch am Mi 25. Jan 2023, 20:56, insgesamt 1-mal geändert.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5783
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Wie PPropInfo verwenden???

Beitrag von af0815 »

Der OI ist Betriebsystemstabil. Die Abhängigkeiten sind nie ein Problem

Bezüglich dem EPropertyerror, das ist sehr empfindlich, man muss vom richtigen Objekt ableiten, deine Elternobjekte kommen mir zu früh abgeleitet vor.
Zuletzt geändert von af0815 am Mi 25. Jan 2023, 21:08, insgesamt 1-mal geändert.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

thosch
Beiträge: 307
Registriert: Mo 10. Jul 2017, 20:32

Re: Wie PPropInfo verwenden???

Beitrag von thosch »

@af0815:

Gilt das auch für Betriebssysteme die nicht explizit berücksichtigt, durch Unterordner benannt, oder die schon sehr alt sind?
Wenn nämlich nicht, nehme ich doch lieber meine eigene Version.
Zuletzt geändert von thosch am Mi 25. Jan 2023, 21:23, insgesamt 1-mal geändert.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5783
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Wie PPropInfo verwenden???

Beitrag von af0815 »

Der OI ist eines der ältesten und zentralsten Elemente, du kannst sagen, dort wo es Lazarus gibt, gibt es den OI. Du kannst nur daran scheitern, das es kein Widgetset dafür gibt.

Hast du da eine genauere Spezifikation ?
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten