Auf Data von per Assign kopierten TreeView.Items zugreifen

Für Fragen von Einsteigern und Programmieranfängern...
cle
Beiträge: 30
Registriert: Mi 31. Jan 2018, 11:54
OS, Lazarus, FPC: Winux (L trunc FPC 3.3.1)
CPU-Target: 64Bit

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von cle »

Alles was ich gemacht habe ist ein neues Projekt starten, 2 TreeViews auf das Formular ziehen, ein Label dazu und die zwei onChange Ereignisse. Dazu den Code von oben. Keine weiteren Properties oder Compilereinstellungen. Michl hat es ja nachvollziehen können und als Bug bezeichnet. Ich verstehe nur nicht, dass es bei seinem Trunk funktioniert hat und bei meinem nicht (BS?) und warum bei Windows das 'if assigned(..)' schützt und bei Linux nicht. Aber wenn du meinst, bastel ich dir gerne eine Paket, melde mich bei einer Cloud an und lade die Dateien hoch.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

Habe das Projekt noch rumliegen. Mal sehen, ob das Anhängen wieder funktioniert?!

[Edit] Scheint zu funktionieren :D
Dateianhänge
Test.zip
(2.24 KiB) 65-mal heruntergeladen

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

cle hat geschrieben:Ich habe vor zwei Tagen mit fpcupdeluxe Lazarus Trunk aktualisiert (Linux, 64, gtk2) und damit stürzt das Programm von oben wie gehabt ab. Mit Windows hat fpcupdeluxe nicht funktioniert, bei einem Laz 1.8.2 hat immerhin das if Assigned(Node.Data) gegriffen, im Gegensatz zu Linux.
Nur zur nachträglichen Info @Michl
Tatsächlich. Eben probiert: Unter Windows geht es, unter Linux GTK2 bzw. Qt4 geht es nicht (gleiche Trunk-Versionen). Ist also ein WidGetSet-Problem.

Bitte im Bugtracker melden!

[Edit] Habe es mal kurz debuggt. Es könnte auch ein 64bit Problem sein (obwohl es hier unter einem 64bit Windows funktioniert). TOldTreeNodeInfo.Data ist vom Typ Cardinal.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

Ja, es ist ein 64bit Problem. Wenn ich TOldTreeNodeInfo.Data auf PtrUInt umstelle, geht es auch unter einem 64bit Linux. Blöd ist halt, daß der Streamreader auch für die .lfm genutzt wird. Eine Änderung hier würde sich auf das Öffnen von 32bit erstellten Projekten mit einem 64bit Lazarus (und vice versa) auswirken.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

cle
Beiträge: 30
Registriert: Mi 31. Jan 2018, 11:54
OS, Lazarus, FPC: Winux (L trunc FPC 3.3.1)
CPU-Target: 64Bit

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von cle »

Vielen Dank, dass du die Sachlage mal geklärt hast.
Nur - jetzt hast du mich etwas überfordert ;)
Soll ich jetzt was melden und was?
Für mein eigenes Programm werde ich wohl - wie gehabt - über den Umweg AbsoluteIndex auf die Daten vom TV1 zugreifen.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Michl hat geschrieben:Ja, es ist ein 64bit Problem. Wenn ich TOldTreeNodeInfo.Data auf PtrUInt umstelle, geht es auch unter einem 64bit Linux. Blöd ist halt, daß der Streamreader auch für die .lfm genutzt wird. Eine Änderung hier würde sich auf das Öffnen von 32bit erstellten Projekten mit einem 64bit Lazarus (und vice versa) auswirken.

Ich versteh das nicht: Kann man in der lfm-Datei überhaupt einen "richtigen" Pointer auf einen Speicherbereich speichern? Der Pointer zeigt nach dem Lesen der lfm-Datei mit sicherheit nicht auf die richtige Speicherstelle. Stattdessen wird man hier immer nur als Pointer "verkleidete" Integerwerte haben. Damit müsste man das Problem lösen können, wenn beim Lesen OldInfo.Data durch einen Cast auf PtrInt die 32-Bit Zahl OldInfo.Data bei 64-Bit auf die benötigte Pointer-Größe erweitert. Und beim Schreiben müsste man dasselbe tun und den zu großen PtrInt durch Cast auf Cardinal auf 32-Bit zurechtstutzen:

Code: Alles auswählen

procedure TTreeNode.ReadData(Stream: TStream; StreamVersion: integer);
    ...
    Data := Pointer(PtrInt(OldInfo.Data)); // war: {%H-}Pointer(OldInfo.Data);
    ...

und

Code: Alles auswählen

procedure TTreeNode.WriteData(Stream: TStream; StreamVersion: integer);
    ...
    OldInfo.Data := Cardinal(PtrInt(Data))//  war: {%H-}Cardinal(Data);
    ...

Bei 32-bit für OldInfo.Data muss man wahrscheinlich bleiben, um Kompatibilität zur Delphi's binären dfm-Dateien zu behalten.

Ich habe z.Zt kein 64-Bit Linux zur Hand und kann's nicht testen.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

Die Ausgabe

Code: Alles auswählen

procedure TTreeNode.WriteData(Stream: TStream; StreamVersion: integer);
...
    writeln('TTreeNode.WriteData ', IntToHex(PtrUInt(Data), 16))
zeigt, daß dieser unter einem 64bit Linux voll genutzt wird:

Code: Alles auswählen

TTreeNode.WriteData 00007FFFF7FDE760

Unter dem 64bit Windows hier sind die erste 4 Byte alle 0, daher crasht es hier nicht:

Code: Alles auswählen

TTreeNode.WriteData 00000000010E3EC0

wp_xyz hat geschrieben: Kann man in der lfm-Datei überhaupt einen "richtigen" Pointer auf einen Speicherbereich speichern?
Ist gar nicht notwendig. Der 32bit Pointer wird einfach als "Platzhalter" mit gespeichert, für das Einlesen wird er nicht verwendet. Wenn man allerdings PtrUInt verwendet, ist unter 32bit der Platzhalter 4 Byte und unter 64bit 8 Byte groß. Daher verschieben sich alle Einträge, je nachdem ob man ein 32 oder 64bit Lazarus verwendet, um 4 Byte des Packed-Records TOldTreeNodeInfo. Es gibt neben der TOldTreeNodeInfo noch die TTreeNodeInfo ohne den Data Pointer. Weiß nicht, warum dieser Stream nicht für die .lfm verwendet wird?!

wp_xyz hat geschrieben:Ich versteh das nicht: Kann man in der lfm-Datei überhaupt einen "richtigen" Pointer auf einen Speicherbereich speichern? Der Pointer zeigt nach dem Lesen der lfm-Datei mit sicherheit nicht auf die richtige Speicherstelle. Stattdessen wird man hier immer nur als Pointer "verkleidete" Integerwerte haben. Damit müsste man das Problem lösen können, wenn beim Lesen OldInfo.Data durch einen Cast auf PtrInt die 32-Bit Zahl OldInfo.Data bei 64-Bit auf die benötigte Pointer-Größe erweitert. Und beim Schreiben müsste man dasselbe tun und den zu großen PtrInt durch Cast auf Cardinal auf 32-Bit zurechtstutzen:
Bei der .lfm mag das gehen, da die Pointer nicht benötigt werden, für das Assign sind jedoch die richtigen Pointer/Speicheradressen notwendig. Wie willst du das im Stream-Reader/-Writer unterscheiden, ob gerade die .lfm gelesen/geschrieben wird oder ob eine Speicheroperation vorliegt?

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Michl hat geschrieben:Bei der .lfm mag das gehen, da die Pointer nicht benötigt werden, für das Assign sind jedoch die richtigen Pointer/Speicheradressen notwendig. Wie willst du das im Stream-Reader/-Writer unterscheiden, ob gerade die .lfm gelesen/geschrieben wird oder ob eine Speicheroperation vorliegt?

Aber diese TOldTreeNodeInfo wird beim Assign gar nicht benötigt. sondern nur während ReadData/WriteData. Assign benutzt den Stream-Reader/Writer gar nicht - oder sehe ich da etwas falsch?

Code: Alles auswählen

procedure TTreeNode.Assign(Source: TPersistent);
var
  ANode: TTreeNode;
begin
  if Owner<>nil then Owner.ClearCache;
  if Source is TTreeNode then
  begin
    ANode := TTreeNode(Source);
    Text := ANode.Text;
    Data := ANode.Data;     // <------ hier: ANode.Data ist ein Pointer, kein Cardinal!
    ...
  end
  else inherited Assign(Source);
end


Michl hat geschrieben:Es gibt neben der TOldTreeNodeInfo noch die TTreeNodeInfo ohne den Data Pointer. Weiß nicht, warum dieser Stream nicht für die .lfm verwendet wird?!

Es gibt auch noch diesen Versions-Parameter der an ReadData/WriteData übergeben wird. Dadurch wird, wahrscheinlich je nach Lazarus-Version, einmal der Record mit und einmal ohne Data-Wert eingelesen - den Werte ohne Pointer braucht man, wenn man eine lfm-Datei einer alten Version lesen will (warum ausgerechnet der andere Record mit "Old" tituliert ist, verstehe ich nicht).

Michl hat geschrieben:Der 32bit Pointer wird einfach als "Platzhalter" mit gespeichert, für das Einlesen wird er nicht verwendet.

Das glaube ich nicht In dem schon oben zitierten Code sieht man, dass der eingelesene OldNode.Data (Cardinal) dem Data-Pointer des Node zugewiesen wird. Einen 32-bit Wert muss man nehmen, weil dfm-Dateien von Delphi (zumindest Delphi 7) nur 32-Bit sein können.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

Ach ich bin doch blöd. Ich hatte gestern unter einem 32bit Lazarus und unter einem 64bit Lazarus jeweils TOldTreeNodeInfo.Data zu PtrUInt geändert, in einem Lazarus ein Projekt mit einem TTreeView mit ein paar Einträgen erstellt und mit der anderen Lazarusversion geöffnet. Dabei stürzte Lazarus ab. Dabei hatte ich unter dem Lazarus einmal zuvor ein defektes Package getestet, was ich vergessen hatte, wieder zu deinstallieren. Folglich hatte ich das Ergebnis ganz falsch interpretiert.

Lange Rede, kurzer Sinn, es funktioniert Data zu PtrUInt zu ändern und man kann ein Projekt trotzdem unter einem 32 oder 64bit Lazarus öffnen. Hatte mich schon gewundert, warum TOldTreeNodeInfo und nicht TTreeNodeInfo für das Speichern verwendet wird (was ja auch nicht stimmt). IMHO war es ein Copy&Paste Problem von TDelphiNodeInfo (Data hat da auch 32bit).

Ist jetzt gefixt in Lazarus Trunk Revision 58510.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

wp_xyz hat geschrieben:Aber diese TOldTreeNodeInfo wird beim Assign gar nicht benötigt. sondern nur während ReadData/WriteData. Assign benutzt den Stream-Reader/Writer gar nicht - oder sehe ich da etwas falsch?
Bei obrigen Beispiel wird die von dir aufgeführte Methode procedure TTreeNode.Assign(Source: TPersistent); gar nicht ausgeführt, da TreeView2.Items.Assign ReadData/WriteData ausführt (kannst ja einfach mal einen Breakpoint in die Methode setzen).

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Michl hat geschrieben:Lange Rede, kurzer Sinn, es funktioniert Data zu PtrUInt zu ändern und man kann ein Projekt trotzdem unter einem 32 oder 64bit Lazarus öffnen. Hatte mich schon gewundert, warum TOldTreeNodeInfo und nicht TTreeNodeInfo für das Speichern verwendet wird (was ja auch nicht stimmt). IMHO war es ein Copy&Paste Problem von TDelphiNodeInfo (Data hat da auch 32bit).

Ist jetzt gefixt in Lazarus Trunk Revision 58510.

Aber jetzt haben wir das - zugegebenermaßen seltene - Problem, dass ein 64-Bit-Lazarus kein unter Delphi binär abgespeichertes Formular mehr ins Text-Format konvertieren kann. Denn Delphi hat für Node.Data einen 32-Bit-Wert in die dfm-Datei geschrieben, und das 64-Bit-Lazarus liest in Node.ReadData den 64-Bit-PtrUInt-Wert von OldInfo.Data, was die Datenzuordnung im Stream zerstört. Oder sehe ich das falsch?

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von Michl »

Ich habe es jetzt nicht getestet, doch procedure TTreeNode.ReadDelphiData(Stream: TStream; Info: PDelphiNodeInfo); nutzt weiterhin folgendes: TDelphiNodeInfo.Data: Cardinal; // Always 32-bit, assigned to a Pointer.

In TTreeNode.ReadDelphiData bzw. TTreeNode.WriteDelphiData wird weiterhin zu Pointer <-> Cardinal gecastet. Sollte daher passen.

Die Records TOldTreeNodeInfo, TTreeNodeInfo und TDelphiNodeInfo sind von Haus aus nicht deckungsgleich und könnten somit sowieso nicht direkt aufeinander kopiert werden.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Auf Data von per Assign kopierten TreeView.Items zugreif

Beitrag von wp_xyz »

Michl hat geschrieben:Ich habe es jetzt nicht getestet, doch procedure TTreeNode.ReadDelphiData(Stream: TStream; Info: PDelphiNodeInfo); nutzt weiterhin folgendes: TDelphiNodeInfo.Data: Cardinal; // Always 32-bit, assigned to a Pointer.

In TTreeNode.ReadDelphiData bzw. TTreeNode.WriteDelphiData wird weiterhin zu Pointer <-> Cardinal gecastet. Sollte daher passen.

Die Records TOldTreeNodeInfo, TTreeNodeInfo und TDelphiNodeInfo sind von Haus aus nicht deckungsgleich und könnten somit sowieso nicht direkt aufeinander kopiert werden.

Ach so, ich hatte gar nicht gesehen, dass es für Delphi-Dateien eine eigene ReadData/WriteData-Routine gibt.

Antworten