VirtualTreeView - usuwanie wiersza i błąd AccessViolation

0

Witam

Wydaje się, że to jest prosty kod. Przechodzę po utworzonym wcześniej drzewie i jeśli wiersz spełnia warunek, zostaje usunięty. Następnie przechodzę do kolejnego liścia GetNextLeaf(Node), jednak tutaj otrzymuję błąd AV. I nie zależnie czy będzie to GetNext(Node) lub GetNextSibling(Node) błąd zawsze występuje. Detektor to EurekaLog7.
Nie mam pojęcia skąd to się wzięło. Może błąd jest w samym VirtualTreeView. Używam wersji: 5.5.2: (10 Nov 2014).

Najprawdopodobniej źle chodzę po gałęzi. Czy tutaj obowiązuje poruszanie się "od dołu", poprzez GetPreviousLeaf(Node) ?

var
 Node: PVirtualNode;
 Data: PTreeData;
begin
 Node := VST.GetFirstLeaf;
 while not (Node = nil) do
   begin
    Data := VST.GetNodeData(Node);
    if Data.FImage <> 0 then VST.DeleteNode(Node);

    Node := VST.GetNextLeaf(Node); // <------- AccessViolation ---------

    Continue;

    //if ... then
   end;
end;

Doradźcie coś, bo już tracę zmysły myślenia :)

5
Node := VST.GetNextLeaf(Node); // <------- AccessViolation ---------

Najpierw wywalasz węzeł Node z drzewa, a następnie podajesz usunięty węzeł w parametrze - obstawiam że wtedy wskazuje na martwą przestrzeń w pamęci, stąd wyjątek;

Spróbuj użyć dodatkowej zmiennej, w której najpierw zapamiętasz adres kolejnego liścia, a dopiero potem wykonaj walidację i ewentualne usunięcie węzła; Wtedy bez względu na to czy usuniesz bieżący węzeł czy nie, w drugiej zmiennej będziesz miał adres kolejnego; Czyli coś w tym stylu:

var
  pvnCurrent, pvnNext: PVirtualNode;
  ptdCurrent: PTreeData;
begin
  pvnCurrent := VST.GetFirstLeaf();

  while pvnCurrent <> nil do
  begin
    pvnNext := VST.GetNextLeaf(pvnCurrent);
    ptdCurrent := VST.GetNodeData(pvnCurrent);

    if pdtCurrent^.FImage <> 0 then  // tu ma być ^, bo odwołujemy się do zawartości spod wskaźnika
      VST.DeleteNode(pvnCurrent);

    pvnCurrent := pvnNext;
  end;
end;

Sprawdź w ten sposób - ja nie mam źródeł tego drzewa i sam nie przetestuję, ale powinno działać.

0

Dzięki @furious programming
Koncepcja dobra, sądziłem, że nie będzie problemu, jednak brak jest dalszego przejścia w drzewie. Pętla nieskończona.
Sprawdziłem i pobiera mi cały czas pierwszy wiersz.

Do tej pory ta funkcja nie sprawiała problemu, mam z nią kłopot tylko poprzez detekcję EurekaLog (normalnie nie występuje).

Próbowałem też pomijać indeksację VST.DeleteNode(pvnCurrent, False);

Spotkałem się też z innym przykładem, ale potrzebuję przeglądać drzewo "z góry na dół", a nie od końca:

var
  Node, TmpNode: PVirtualNode;
begin
  Node := Tree.GetLast;
  while Assigned(Node) do
  begin
    TmpNode := Tree.GetPrevious(Node);
    Tree.DeleteNode(Node);
    Node := TmpNode;
  end;
end;
1

No to ja już nie wiem... To co podałem to najzwyklejszy schemat usuwania węzłów z listy co najmniej jednokierunkowej i jako schemat jest jak najbardziej poprawny; Zobacz w źródła tego komponentu i sprawdź co robią metody GetFirstLeaf, GetNextLeaf i DeleteNode i co może psuć iterowanie po węzłach.

0

Dziękuję za pomoc.

Okazało się, po wielu godzinach prób, że to menadżer zarządzania pamięcią w EurekaLog jest winny.
Włączenie tej opcji powoduje dziwne problemy - być może jest tym module błąd (zgłosiłem do autora).

Korzystam też z biblioteki midas.dll i unitu MidasSpeedFix. Sądziłem, że tu jest przyczyna. Po wyłączeniu problem występował nadal. Pozostał więc Memory Manager z EurekaLog.

A teraz najlepsze... Problem ustąpił, więc usunięcie Noda poprzez:

  VST.DeleteNode(Node);

         {Iteracja do kolejnego dziecka}
         Node := VST.GetNextLeaf(Node);

nie powoduje błędu AV. Prawdopodobnie to DeleteNode reindeksuje wiersze, dzięki czemu błąd nie wystąpi.

Natomiast przykład @furious programming zapętla się :(

2

Jeżeli to:

  VST.DeleteNode(Node);
 
  {Iteracja do kolejnego dziecka}
  Node := VST.GetNextLeaf(Node);

jakimś cudem ci się nie wywala to zmienisz gdzieś coś w innym miejscu a zacznie się wywalać. Wtedy spędzisz sporo czasu na poszukiwania.

0

Odnalazłem przyczynę problemu. Błąd AV jest spowodowany włączeniem poniższej opcji w EurekaLog. Zgłosiłem do producenta, aby się temu przyjrzał.

0

@Opi, ale to nie jest wina EurekaLog tylko Twojego kodu;

VST.DeleteNode(Node);
Node := VST.GetNextLeaf(Node);

Taki kod musi wyrzucić wyjątek Access Violation, albowiem wykonuje sprzeczne ze sobą czynności; Nie możesz usunąć węzła, a później użyć go w kolejnej instrukcji, bo to jest sprzeczne;

Jeśli Twój kod w takiej postaci działał prawidłowo, jeśli opcja fill freed memory with zeros była wyłączona, to działał tylko i wyłącznie dlatego, że pamięć po zwolnionym węźle nie została wyzerowana i mogłeś użyć smieci z pamięci do pobrania adresu kolejnego węzła; Jeżeli ta opcja będzie włączona to usunięcie węzła będzie skutkować wyzerowaniem bloku pamięci po nim, czego rezultatem będzie AV w kolejnej instrukcji, czyli w GetNextLeaf; I prawidłowo, bo Node zostanie wyzerowany, a metoda dostanie (jak dobrze myślę) **Nil**a, a nawet jeśli sam wskaźnik Node nie będzie **Nil**em, to będzie wskazywał na wyzerowany obszar pamięci;

Tak więc według mnie kod przytoczony powyżej jest błędny i nie ma prawa działać, a opcja zerowania pamięci nie jest niczemu winna i działa prawidłowo, tak jak producent założył; Co więcej, zachowanie aplikacji, która używa tej opcji, zgadza się z treścią hinta:

Zeroing memory can help to catch accesses to released memory.

No i faktycznie pomogła znaleźć odwołanie do zwolnionego fragmentu pamięci.

0

Poprawiony kod według rozwiązania furious programming

furious programming napisał(a):

Spróbuj użyć dodatkowej zmiennej, w której najpierw zapamiętasz adres kolejnego liścia, a dopiero potem wykonaj walidację i ewentualne usunięcie węzła; Wtedy bez względu na to czy usuniesz bieżący węzeł czy nie, w drugiej zmiennej będziesz miał adres kolejnego

var
 ScanNode, DelNode: PVirtualNode;
 Data: PTreeData;
 Correct: Boolean;
begin
 ScanNode := VST.GetFirstLeaf;
 while ScanNode <> nil do
   begin
    Correct := False;
    Data := VST.GetNodeData(ScanNode);
    if Data.FImage <> 0 then Correct := True;
  
    DelNode := ScanNode;
    ScanNode := VST.GetNextLeaf(ScanNode);
    if Correct then VST.DeleteNode(DelNode);
 
    Continue;
 
    //if ... then
   end;
end;

1 użytkowników online, w tym zalogowanych: 0, gości: 1