AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

TVirtualStringTree AddChild access violation

Ein Thema von EricMeyer · begonnen am 28. Nov 2019 · letzter Beitrag vom 9. Dez 2019
Antwort Antwort
Seite 1 von 2  1 2   
EricMeyer

Registriert seit: 31. Mai 2012
Ort: Berlin
23 Beiträge
 
#1

TVirtualStringTree AddChild access violation

  Alt 28. Nov 2019, 19:07
Ich habe ein Problem damit zu verstehen, warum ich eine access violation bekomme.
ich bekomme in der Zeile "Item := Tree.AddChild(Item);" ein access violation beim zweiten Schleifendurchlauf. Ich habe darüber LData := Tree.GetnodeData(Item); geschrieben, um zu gucken, ob das Item ein bereits definierter Node ist und Daten sind da. Meines Verständnisses nach müsste das Access Violation in der Zeile bedeutet, dass das Item sich weigert ein neues Child anzunehmen. Das ergibt in meinem Kopf keinen Sinn.

Delphi-Quellcode:
procedure TFrameTreeView.GetDirectories(Tree: TVirtualStringTree; Directory: string; Item: PVirtualNode; IncludeFiles: Boolean);
var
  SearchRec: TSearchRec;
  ItemTemp: PVirtualNode;
  LNode : PVirtualNode;
  LData : PTreeData;
  Lteststring : string;
  I: Integer;
  begin
  Tree.BeginUpdate;
  if Directory[Length(Directory)] <> '\then
    Directory := Directory + '\';
  if FindFirst(Directory + '*.*', faDirectory, SearchRec) = 0 then
  begin

    repeat //loop
      if (SearchRec.Attr and faDirectory = faDirectory) and (SearchRec.Name[1] <> '.') then
      begin
        if (SearchRec.Attr and faDirectory > 0) then
          begin
            try
              if not Assigned(Item) then
                  break;
              LData := Tree.GetnodeData(Item);
              Item := Tree.AddChild(Item); //<------ access violation
              LData := Tree.GetnodeData(Item);
              LData^.FPath := Directory+SearchRec.Name;
              LData^.FFolderName := SearchRec.Name;
              LData^.FFirstFolder := false;
              Tree.NodeDataSize := SizeOf(TTreeData);
              Tree.ValidateNode(Item, False);
            except
              break;
            end;
          end;
        ItemTemp := Item.Parent;
        LData := Tree.GetnodeData(ItemTemp);
        GetDirectories(Tree, Directory + SearchRec.Name, Item, IncludeFiles);
        Item := ItemTemp;
      end
    until FindNext(SearchRec) <> 0;

    FindClose(SearchRec);
  end;
  Tree.EndUpdate;
end;
Delphi-Quellcode:
function TFrameTreeView.CreateFirstNode(Adir : string):PVirtualNode ;
var
  LFirstNode : PVirtualNode;
  LData : PTreeData;
begin
   LFirstNode := TreeView2.AddChild(nil);
   LData := TreeView2.GetnodeData(LFirstNode);
   LData^.FFirstFolder := True;
   LData^.FPath := ADir;
   LData^.FFolderName := Adir;
   TreeView2.NodeDataSize := SizeOf(TTreeData);
   TreeView2.ValidateNode(LFirstNode, False);
   Result := LFirstNode;
end;
Delphi-Quellcode:
function TFrameTreeView.NodePath(Node: TTreeNode): string;
var
  anode : TTreeNode;
begin
  result := '';

  if Node <> nil then begin
    result := Node.Text +'\';

    anode := Node.Parent;
    while anode <> nil do begin
      result := anode.Text +'\' +result;
      anode := anode.Parent;
    end;
  end;
end;
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.173 Beiträge
 
#2

AW: TVirtualStringTree AddChild access violation

  Alt 28. Nov 2019, 19:29
Wenn ich du wäre würde ich das alles noch einmal komplett umschreiben.
AddChild benutzt man nicht!

Man fügt Nodes über RootNodeCount hinzu und einer Liste mit Klasseninstanzen.
  Mit Zitat antworten Zitat
EricMeyer

Registriert seit: 31. Mai 2012
Ort: Berlin
23 Beiträge
 
#3

AW: TVirtualStringTree AddChild access violation

  Alt 28. Nov 2019, 20:03
Bin nur diesem Tutorial gefolgt: http://www.soft-gems.net/supplement/download.php?ID=79

Aber dann lese ich mich mal in RootNodeCount ein. Danke dafür!
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.173 Beiträge
 
#4

AW: TVirtualStringTree AddChild access violation

  Alt 28. Nov 2019, 20:18
Ganz grob also:

du hast eine Klasse mit Proberties und Informationen von allem was du so sin deinen Baum brauchst.
Davon erzeugst du Instanzen und packst die in eine Liste. RootNodeCount ist so groß wie deine Liste groß ist.
In Den VST Events greifst du jetzt auf diese Liste zu.
  Mit Zitat antworten Zitat
Aviator

Registriert seit: 3. Jun 2010
1.610 Beiträge
 
Delphi 10.3 Rio
 
#5

AW: TVirtualStringTree AddChild access violation

  Alt 28. Nov 2019, 21:53
Ich habe ein Problem damit zu verstehen, warum ich eine access violation bekomme.
ich bekomme in der Zeile "Item := Tree.AddChild(Item);" ein access violation beim zweiten Schleifendurchlauf. Ich habe darüber LData := Tree.GetnodeData(Item); geschrieben, um zu gucken, ob das Item ein bereits definierter Node ist und Daten sind da. Meines Verständnisses nach müsste das Access Violation in der Zeile bedeutet, dass das Item sich weigert ein neues Child anzunehmen. Das ergibt in meinem Kopf keinen Sinn.
Du hast in deinem Beispiel LNode als PVirtualNode deklariert, benutzt diese aber nicht. Stattdessen weist du die neu erstellte Node deiner Variablen zu, die du als Übergabeparameter bekommen hast. Vermutlich fliegt dir hier etwas um die Ohren. Ändere das mal und schau, ob es funktioniert. Dann siehst du schon mal, ob dein generelles Vorhaben klappt.

ABER: DieDolly hat schon Recht wenn Sie sagt, dass man AddChild nicht benutzt. Zumindest nicht benutzen sollte. RootNodeCount und ChildCount (vom Event InitChildren) ist auf jeden Fall die bessere Wahl. Zudem entfällt das ganze FreeNode() Zeugs. Und du hast die Daten in einer strukturierten Liste und nicht in einer visuellen Komponente gespeichert.
  Mit Zitat antworten Zitat
EricMeyer

Registriert seit: 31. Mai 2012
Ort: Berlin
23 Beiträge
 
#6

AW: TVirtualStringTree AddChild access violation

  Alt 8. Dez 2019, 14:07
Teil 1:
Ich habe mich nochmal mit dem Code von unten auseinander gesetzt. Es schein am Befehl Size Of oder ValidateNode zu liegen. Wenn ich danach versuche wieder auf die Daten zuzugreifen, killt er sich.

Delphi-Quellcode:
              
              LData := (LNode.GetData);
              LData^.FPath := Directory+SearchRec.Name;
              LData^.FFolderName := SearchRec.Name;
              LData^.FFirstFolder := false;
              TreeView2.NodeDataSize := SizeOf(PTreeData);
              TreeView2.ValidateNode(LNode, False);
              LData := (LNode.GetData); //<--- Will nicht
Teil2:
Ich habe mich mit den Vorschlägen auseinander gesetzt, es über RootNodeCount zu lösen. Ich habe also ein Recusives Object erzeugt, in dem alle Daten sind, habe die Oberste Ebene fertig, aber muss ich jetzt wirklich in IniChild die Kinder erzeugen und dann davon die Kindeskinder? Muss ich das recusive aufrufen, oder wird nachdem ich ein Kind erzeugt habe für dieses, das wieder aufgerufen? Ich finde das ganze unglaublich aufwändig dafür, dass ich nur eine Ordnerstruktur abbilden will.
  Mit Zitat antworten Zitat
Aviator

Registriert seit: 3. Jun 2010
1.610 Beiträge
 
Delphi 10.3 Rio
 
#7

AW: TVirtualStringTree AddChild access violation

  Alt 8. Dez 2019, 14:40
Teil 1:
Ich habe mich nochmal mit dem Code von unten auseinander gesetzt. Es schein am Befehl Size Of oder ValidateNode zu liegen. Wenn ich danach versuche wieder auf die Daten zuzugreifen, killt er sich.

Delphi-Quellcode:
              
              LData := (LNode.GetData);
              LData^.FPath := Directory+SearchRec.Name;
              LData^.FFolderName := SearchRec.Name;
              LData^.FFirstFolder := false;
              TreeView2.NodeDataSize := SizeOf(PTreeData);
              TreeView2.ValidateNode(LNode, False);
              LData := (LNode.GetData); //<--- Will nicht
Du setzt die NodeDataSize auf die Größe des Pointers. Also wahrscheinlich 4 Byte. Deine Struktur in der die Daten gespeichert werden ist aber sehr wahrscheinlich ungleich 4 Byte. Deshalb schmiert er bei ValidateNode() ab. Also einfach die NodeDataSize mit TreeView2.NodeDataSize := SizeOf(TTreeData); // <-- ACHTUNG: TTreeData nicht PTreeData initialisieren. Dann sollte das funktionieren.

Teil2:
Ich habe mich mit den Vorschlägen auseinander gesetzt, es über RootNodeCount zu lösen. Ich habe also ein Recusives Object erzeugt, in dem alle Daten sind, habe die Oberste Ebene fertig, aber muss ich jetzt wirklich in IniChild die Kinder erzeugen und dann davon die Kindeskinder? Muss ich das recusive aufrufen, oder wird nachdem ich ein Kind erzeugt habe für dieses, das wieder aufgerufen? Ich finde das ganze unglaublich aufwändig dafür, dass ich nur eine Ordnerstruktur abbilden will.
Das Event InitChildren wird immer dann aufgerufen, wenn dem Tree die Daten der ChildNodes bekannt sein muss. Im Normalfall erst dann, wenn man die Parent Node expandiert. Da du dann im InitChildren die ParentNode als Parameter übergeben bekommst, kannst du dir per GetNodeData() den Pointer auf das Objekt in deiner Liste geben lassen. In dem Objekt gibt es dann ja eine Liste mit Verzeichnissen. Die Liste hat eine bestimmte Anzahl an Verzeichnissen. Und diese Anzahl wird dem var Parameter ChildCount zugewiesen. Der Tree erstellt dann automatisch die Anzahl der Nodes auf dieser Ebene und ruft für jede Node das Event InitNode() auf. Im InitNode musst du dich dann immer an der ParentNode orientieren wenn der NodeLevel > 0 ist. Und so kannst du dir sehr schnell den Tree aufbauen. Ist eigentlich nicht wirklich aufwendig. Nur der erste Versuch klingt immer abschrecken.

Hoffe die Erklärung bringt dich weiter. Versuch einfach mal mit den Events zu arbeiten. Wenn du nicht weiter kommst, dann stell den SourceCode hier rein und wir schauen nochmal drüber.
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#8

AW: TVirtualStringTree AddChild access violation

  Alt 9. Dez 2019, 08:14
Wenn ich du wäre würde ich das alles noch einmal komplett umschreiben.
AddChild benutzt man nicht!

Man fügt Nodes über RootNodeCount hinzu und einer Liste mit Klasseninstanzen.
Das wird seit Jahren immer wieder erzählt. Ich benutze den VST als vollständigen Ersatz für den Standard-TTreeView. Den nutze ich gar nicht mehr. In Szenarien, wo man kleine Bäume hat mit ~ 100 Nodes, arbeitet es sich mit AddChild wesentlich eleganter als mit über zig Events verteilten Initialisierungen. Selbst bei 10.000 Nodes fällt auf modernen Rechnern der Laufzeitunterschied kaum mehr ins Gewicht, besonders wenn man BeginUpdate und EndUpdate drumherum macht.

Der VST entstand IMHO zu Zeiten von Delphi 4 und Pentium-Prozessoren mit 90 MHz Takt. Da war der Unterschied zum TTreeView noch gewaltig, selbst bei kleineren Bäumen.

Klar kannst du mit GetTickCount messen, dass zwischen einer AddChild-Schleife und einer RootNodeCount-Zuweisung soundsoviel Millisekunden Unterschied liegen. Aber das ist Jammern auf hohem Niveau. Im vorliegenden Fall dürfte ohnehin das Auslesen der Datenträgerinfos zum Flaschenhals werden und nicht das Initialisieren vom Tree.
Ich mache grundsätzlich keine Screenshots. Schießen auf Bildschirme gibt nämlich hässliche Pixelfehler und schadet der Gesundheit vom Kollegen gegenüber. I und E zu vertauschen hätte den selben negativen Effekt, würde aber eher dem Betriebsklima schaden
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.173 Beiträge
 
#9

AW: TVirtualStringTree AddChild access violation

  Alt 9. Dez 2019, 09:49
Das wird seit Jahren immer wieder erzählt
Das wird erzählt, weil man die Daten im besten Fall in einer Liste speichert. In dem Beispiel hier unten, was ich von oben kopiert habe, wird nichts in einer Liste gespeichert
Delphi-Quellcode:
function TFrameTreeView.CreateFirstNode(Adir : string):PVirtualNode ;
var
  LFirstNode : PVirtualNode;
  LData : PTreeData;
begin
   LFirstNode := TreeView2.AddChild(nil);
   LData := TreeView2.GetnodeData(LFirstNode);
   LData^.FFirstFolder := True;
   LData^.FPath := ADir;
   LData^.FFolderName := Adir;
   TreeView2.NodeDataSize := SizeOf(TTreeData);
   TreeView2.ValidateNode(LFirstNode, False);
   Result := LFirstNode;
end;
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#10

AW: TVirtualStringTree AddChild access violation

  Alt 9. Dez 2019, 10:21
Das wird erzählt, weil man die Daten im besten Fall in einer Liste speichert.
Was ich damit sagen will: Es gibt Fälle, wo die Erzeugung und Verwaltung einer Liste nur Overhead produziert, aber keinen Mehrwert. Der klassische Anwendungsfall vom VST waren dynamisch erzeugte Records. Der VST verwaltete Zeiger darauf, die Nodes im Baum waren in sich selbst eine Liste. Die einzelnen Records kann man z.B. auch per Zeiger verketten. In dem Fall nützt dir eine Liste herzlich wenig. Deshalb würde ich nicht vom "besten Fall" sprechen. Vielmehr muss man sich fallspezifisch damit auseinander setzen, was Sinn macht und was nicht.

Im vorliegenden Fall hast du eine Dateisystem-Struktur. Willst du jetzt für jeden Ordner, jedes Laufwerk und jeden Symlink ein extra Objekt instantiieren und in einer ObjectList verwalten, nur damit du dir ein bisschen Arbeit beim Freenode sparen kannst?

Ich möchte am Beispiel des Erstposters dezent darauf hinweisen, dass ein TSearchRec bereit ein Record ist. Warum nicht den gleich an den Node beppen, wenn er denn schon mal da ist? Subnodes würde ich gar nicht initial mit erzeugen sondern erst on-demand wenn der Anwender einen Ordner-Node aufklappt. So macht das auch der Explorer. Sonst würde das Öffnen auf einer normalen Windows-Installation ja jedesmal 5 Minuten dauern, weil ein komplettes rekursives Listing gemacht werden müsste

@EricMeyer: Die Zugriffsverletzung in deinem Codebeispiel kommt daher, dass du zuerst versuchst, den Node zu erzeugen und danach erst NodeDataSize zuweist. NodeDataSize brauchst du in deinem Fall nur ein einziges Mal zuweisen und zwar bevor du überhaupt in deine Repeat-Schleife gehst. Der Hinweis mit dem TTreeData statt PTreeData war übrigens richtig. Man kann übrigens auch völlig verschiedene Records innerhalb des selben VST benützen. Deshalb speichert jeder PVirtualNode die NodeDataSize auch separat. Für solche Fälle gibt es das Event OnGetNodeDataSize, das z.B. von der Methode GetNodeData aufgerufen wird.
Ich mache grundsätzlich keine Screenshots. Schießen auf Bildschirme gibt nämlich hässliche Pixelfehler und schadet der Gesundheit vom Kollegen gegenüber. I und E zu vertauschen hätte den selben negativen Effekt, würde aber eher dem Betriebsklima schaden

Geändert von Codehunter ( 9. Dez 2019 um 10:29 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:54 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz