Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Schutzverletzung beim Freigeben einer TStringList (https://www.delphipraxis.net/55043-schutzverletzung-beim-freigeben-einer-tstringlist.html)

Garfield 15. Okt 2005 17:37


Schutzverletzung beim Freigeben einer TStringList
 
Mit lkVCDxRip.exe wird eine SVCD gerippt/ein SVCD-Image extrahiert. Anschließend werden mit folgendem Quelltext aus der XML die Kapitelzeiten ausgelesen und in einer Textdatei gespeichert.

Delphi-Quellcode:
  sl := TStringList.Create; // Stringlist für das XML
  kl := TStringList.Create; // Stringlist für die Kapitelzeiten

  sl.LoadFromFile(xml); // Laden der XML

  for i := 0 to sl.Count - 1 do                 // Suchen der Tags mit den Kapitelzeiten
  if pos ('entry id=', sl.Strings[i]) > 0
  then begin
    von   := pos ('>', sl.Strings[i]);         //  Ende des Anfang-Tags
    bis   := pos ('</', sl.Strings[i]);        //  Anfang des Ende-Tags
    if bis > von
    then begin
      kap   := copy(sl.Strings[i], von + 1, bis - von - 4); // Zeit in Kapitelliste kopieren
      kl.Add (kap);
    end;
  end;

  sl := NIL; // Stringlist des XML freigeben
  sl.Free;

  if kl.Count > 0  // Punkt gegen Komma austaschen und den Wert Null entfernen
  then begin
    i := 0;
    while i < kl.Count
    do begin
      kap          := Tausch(kl.Strings[i], '.', ',' ,false, true);
      kl.Strings[i] := kap;
      if StrToFloat(kap) = 0
      then kl.Delete(i)
      else inc(i);
    end;
  end;

  if kl.Count > 0  // Zeitformat ändern
  then begin
    for i := 0 to kl.Count - 1
    do begin
      kap := kl.Strings[i];
      kl.Strings[i] := FormatSecS(kap); // Funktion in einer anderen Unit
    end;
  end;

  if kl.Count > 0  // Die Kapitelliste speichern
  then begin
    kap := (ChangeFileExt(Ziel, '.Kapitel')); // Ziel ist die *.mpg von der SVCD
    kl.SaveToFile(kap);
  end;

  while not FileExists(kap)
  do sleep(50)

  kl := NIL; // Kapitelliste freigeben
  kl.Free;
Beim Freigeben der Kapitelliste (kl.free) gibt es eine Schutzverletzung. Durch die Abfrage, ob die Kapiteldatei angelegt wurde, hat sich die Wahrscheinlichkeit, dass die Schutzverletzung auftritt, verringert. Wenn ich noch ein 'Sleep(1000);' verwende, tritt bei mir die Schutzverletzung nicht mehr auf. Allerdings bei anderen Anwendern.

Kann mir jemand erklären, warum an dieser Stelle eine Schutzverletzung auftritt und ob es eine bessere Lösung als ein Sleep gibt?

alzaimar 15. Okt 2005 17:40

Re: Schutzverletzung beim Freigeben einer TStringList
 
Die Zeilen
Delphi-Quellcode:
kl := Nil;
kl.Free;
Sind falschrum, dreh Sie um und es wird ein Schuh draus, oder verwende gleich
Delphi-Quellcode:
FreeandNil (kl);

ManuMF 15. Okt 2005 18:11

Re: Schutzverletzung beim Freigeben einer TStringList
 
und

Delphi-Quellcode:
sl := TStringList.Create;
try
  sl.LoadFromFile(...);
  ...
  ...
finally
  FreeAndNil(sl);
end;
Natürlich mit beiden.

Gruß,
ManuMF

alzaimar 15. Okt 2005 18:52

Re: Schutzverletzung beim Freigeben einer TStringList
 
Hupsa, selbstverständlich, ManuMF :oops: :wall:
[edit] :zwinker: [/edit]

Garfield 15. Okt 2005 18:56

Re: Schutzverletzung beim Freigeben einer TStringList
 
Ich danke Euch. :thumb:

Ist schon eigenartig. In meinem Programm stehen die beiden Befehle noch einige Male in dieser falschen Reihenfolge, aber nur an der einen Stelle kommt es zu einem Fehler.

tigerman33 15. Okt 2005 19:18

Re: Schutzverletzung beim Freigeben einer TStringList
 
Dann werden die anderen Zeilen durch if-Abfragen o.ä. vor dem Ausführen "geschützt". Sonst knallt das nämlich 100%ig. => alle unbedingt ändern.

Garfield 15. Okt 2005 19:34

Re: Schutzverletzung beim Freigeben einer TStringList
 
Einige werden maximal einmal und andere fast immer aufgerufen. So wie bei sl. Und es gab bisher keine Fehler. Habe es jetzt alles in FreeAndNil geändert. Schließlich ist Vorsicht die Mutter der Prozellankiste. :P

alzaimar 15. Okt 2005 21:03

Re: Schutzverletzung beim Freigeben einer TStringList
 
Free ist eine Class Procedure, die erst prüft, ob die Referenz <> nil ist, und erst dann ggf. den destructor aufruft. Also, eigentlich dürfte auch hier nichts passieren:
Delphi-Quellcode:
Var
  sl : TStringlist;

Begin
  sl := nil;
  sl.Free;
End;
Wenn aber doch, dann tippe ich, ehrlich gesagt, auf einen dieser fiesen Seiteneffekte, der sich dann einstellt, wenn irgendwo im Code was zerschossen wurde (Durch Überschreiben eines freigegebenen Speicherbereiches etwa).

GuenterS 15. Okt 2005 21:53

Re: Schutzverletzung beim Freigeben einer TStringList
 
Hallo, mir ist aufgefallen, dass Du die kl StringListe öfter als notwendig durchgehst (bzw. brauchst Du sie gar nicht durchgehen), habe deine procedure dahingehend überarbeitet, dass dies nicht passiert und auch schon die try ... finallys eingebaut.

Delphi-Quellcode:
procedure TueWas(xml, Ziel: string);
var
  sl, kl: TStringList;
  kap: string;
  i, von, bis: integer;
begin
  kl := TStringList.Create; // Stringlist für die Kapitelzeiten
  try
    sl := TStringList.Create; // Stringlist für das XML
    try
      sl.LoadFromFile(xml); // Laden der XML

      for i := 0 to sl.Count - 1 do // Suchen der Tags mit den Kapitelzeiten
        if pos('entry id=', sl.Strings[i]) > 0 then
        begin
          von := pos('>', sl.Strings[i]); //  Ende des Anfang-Tags
          bis := pos('</', sl.Strings[i]); //  Anfang des Ende-Tags
          if bis > von then
          begin
            kap := copy(sl.Strings[i], von + 1, bis - von - 4);
            kap := StringReplace(kap, '.', ',', rfReplaceAll);
            if StrToFloat(kap) <> 0 then
              kl.Add(FormatSecS(kap)); // Zeit in Kapitelliste kopieren
          end;
        end;
    finally
      sl.Free; //StringList des XML freigeben
    end;

    if kl.Count > 0 then {// Die Kapitelliste speichern }
    begin
      kap := (ChangeFileExt(Ziel, '.Kapitel'));
        // Ziel ist die *.mpg von der SVCD
      kl.SaveToFile(kap);
    end;

  finally
    kl.Free;
  end;
end;

tigerman33 16. Okt 2005 11:16

Re: Schutzverletzung beim Freigeben einer TStringList
 
@Alzaimar:
Free ist dazu da, den von der Instanz alloziierten Speicher wieder freizugeben. Wenn man vorher den Objektzeiger auf nil setzt, ist ein Aufruf von Free im besten Fall unsinnig und der Speicher ist halt verloren, oder es knallt.

alzaimar 16. Okt 2005 11:22

Re: Schutzverletzung beim Freigeben einer TStringList
 
Nein.

Free ist eine Procedure (keine Class Procedure, sorry) von TObject, und die ist so definiert:
Delphi-Quellcode:
procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;
Insofern stimmt Deine Aussage natürlich teilweise, weil Free eben den Speicher freigibt. Aber, wenn ich vorher die Objektreferenz auf Nil setze, passiert einfach N-I-X. Der Speicher wird nicht freigegeben, aber es knallt nicht, wie man am Code unschwer erkennen kann.

tigerman33 16. Okt 2005 11:33

Re: Schutzverletzung beim Freigeben einer TStringList
 
Gut, einigen wir uns in der Mitte:
Es knallt vielleicht nicht, aber die Speicherleiche hat man halt trotzdem. :wink:

Wobei ich mich frage, wie er die Procedure im Speicher wiederfindet, wenn ich den Zeiger "genilt" habe und es keine class-Procedure ist. :gruebel:

Garfield 16. Okt 2005 11:37

Re: Schutzverletzung beim Freigeben einer TStringList
 
Zitat:

Zitat von alzaimar
Wenn aber doch, dann tippe ich, ehrlich gesagt, auf einen dieser fiesen Seiteneffekte, der sich dann einstellt, wenn irgendwo im Code was zerschossen wurde (Durch Überschreiben eines freigegebenen Speicherbereiches etwa).

Hier wird mit lkVCDxRip.exe die SVCD in einen temporären Ordner kopiert und die XML erstellt. Anschließend wird die MPG verschoben und umbenannt, die Kapitelzeiten aus der XML ausgelesen, die Kapiteldatei gespeichert und dann kommt bei manchen Systemen eine Zugriffsverletzung bevor der temporäre Ordner wieder gelöscht wird. Vor allen anderen NIL und Free-Befehlen wird maximal eine kleine Graphik- oder Batchdatei gespeichert.

Zitat:

Zitat von GuenterS
Hallo, mir ist aufgefallen, dass Du die kl StringListe öfter als notwendig durchgehst (bzw. brauchst Du sie gar nicht durchgehen), habe deine procedure dahingehend überarbeitet, dass dies nicht passiert und auch schon die try ... finallys eingebaut.

Danke, die zwei Schleifen waren mir noch gar nicht bewußt geworden. Das werde ich nachher gleich einbauen.

Zitat:

Zitat von tigerman33
... oder es knallt.

In dem Fall gab es eine "Zugriffsverletzung bei Adresse 004c835f im modul "*.exe" lesen von Adresse 000000."

Zitat:

Zitat von tigerman33
Wobei ich mich frage, wie er die Procedure im Speicher wiederfindet, wenn ich den Zeiger "genilt" habe und es keine class-Procedure ist. :gruebel:

Vermutlich wird sie nicht gefunden und deshalb kommt es zu der Zugriffsverletzung?

tigerman33 16. Okt 2005 11:44

Re: Schutzverletzung beim Freigeben einer TStringList
 
Das war meine Vermutung. Aber alzaimar sagt ja, dass einfach nichts passiert, also auch keine AV. Damit keine AV kommt, muss er ja den Prozedurcode zumindest im Speicher wiederfinden. Und da frag ich mich halt, wie das funktionieren soll.

xaromz 16. Okt 2005 11:49

Re: Schutzverletzung beim Freigeben einer TStringList
 
Hallo,
Zitat:

Zitat von Garfield
Zitat:

Zitat von tigerman33
Wobei ich mich frage, wie er die Procedure im Speicher wiederfindet, wenn ich den Zeiger "genilt" habe und es keine class-Procedure ist. :gruebel:

Vermutlich wird sie nicht gefunden und deshalb kommt es zu der Zugriffsverletzung?

Nein, da Free eine Methode der untersten Basisklasse ist (TObject), weiss der Compiler immer, wohin er springen muss, nämlich nach TObject.Free. Ansonsten würde ja die Überprüfung auf nil in Free keinen Sinn ergeben. Im CPU-Fenster steht deshalb einfach "call TObject.Free".

Gruß
xaromz


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:49 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz