Auf Data von per Assign kopierten TreeView.Items zugreifen
-
- 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
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.
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
Habe das Projekt noch rumliegen. Mal sehen, ob das Anhängen wieder funktioniert?!
[Edit] Scheint zu funktionieren
[Edit] Scheint zu funktionieren

- Dateianhänge
-
Test.zip
- (2.24 KiB) 74-mal heruntergeladen
Code: Alles auswählen
type
TLiveSelection = (lsMoney, lsChilds, lsTime);
TLive = Array[0..1] of TLiveSelection;
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
Tatsächlich. Eben probiert: Unter Windows geht es, unter Linux GTK2 bzw. Qt4 geht es nicht (gleiche Trunk-Versionen). Ist also ein WidGetSet-Problem.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
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;
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
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;
-
- 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
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.
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.
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
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: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.
Code: Alles auswählen
procedure TTreeNode.ReadData(Stream: TStream; StreamVersion: integer);
...
Data := Pointer(PtrInt(OldInfo.Data)); // war: {%H-}Pointer(OldInfo.Data);
...
Code: Alles auswählen
procedure TTreeNode.WriteData(Stream: TStream; StreamVersion: integer);
...
OldInfo.Data := Cardinal(PtrInt(Data)); // war: {%H-}Cardinal(Data);
...
Ich habe z.Zt kein 64-Bit Linux zur Hand und kann's nicht testen.
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
Die Ausgabe zeigt, daß dieser unter einem 64bit Linux voll genutzt wird:
Unter dem 64bit Windows hier sind die erste 4 Byte alle 0, daher crasht es hier nicht:
Code: Alles auswählen
procedure TTreeNode.WriteData(Stream: TStream; StreamVersion: integer);
...
writeln('TTreeNode.WriteData ', IntToHex(PtrUInt(Data), 16));
Code: Alles auswählen
TTreeNode.WriteData 00007FFFF7FDE760
Code: Alles auswählen
TTreeNode.WriteData 00000000010E3EC0
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: Kann man in der lfm-Datei überhaupt einen "richtigen" Pointer auf einen Speicherbereich speichern?
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?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:
Code: Alles auswählen
type
TLiveSelection = (lsMoney, lsChilds, lsTime);
TLive = Array[0..1] of TLiveSelection;
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
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?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?
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;
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:Es gibt neben der TOldTreeNodeInfo noch die TTreeNodeInfo ohne den Data Pointer. Weiß nicht, warum dieser Stream nicht für die .lfm verwendet wird?!
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 hat geschrieben:Der 32bit Pointer wird einfach als "Platzhalter" mit gespeichert, für das Einlesen wird er nicht verwendet.
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
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.
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;
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
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).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?
Code: Alles auswählen
type
TLiveSelection = (lsMoney, lsChilds, lsTime);
TLive = Array[0..1] of TLiveSelection;
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
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 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.
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
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.
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;
Re: Auf Data von per Assign kopierten TreeView.Items zugreif
Ach so, ich hatte gar nicht gesehen, dass es für Delphi-Dateien eine eigene ReadData/WriteData-Routine gibt.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.