Aus Neugier: was ist denn damit gemeint?kralle hat geschrieben: Sa 8. Mär 2025, 23:15 - FreeAndNil: Abfrage auf NIL fehlt. Warum? Ursachenforschung.
Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
- m.fuchs
- Lazarusforum e. V.
- Beiträge: 2785
- Registriert: Fr 22. Sep 2006, 19:32
- OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
- CPU-Target: x86, x64, arm
- Wohnort: Berlin
- Kontaktdaten:
Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
- kralle
- Lazarusforum e. V.
- Beiträge: 1187
- Registriert: Mi 17. Mär 2010, 14:50
- OS, Lazarus, FPC: Manjaro Linux, Mint und Windows 10 ,Lazarus 3.99, FPC-Version: 3.3.1
- CPU-Target: 64Bit
- Wohnort: Bremerhaven
- Kontaktdaten:
Re: Doppeltes FreeAndNil
Moin Michael,
"Alter Pascaler" sagte an einen paar Beispielen, welche Probleme es gibt, wenn man speicher nicht wieder freigibt.
Wir sprachen dann auch darüber ob man oder einsetzen sollte.
So kam es das man Testhalber zweimal im Code untereinander stehen hatte.
Das zweite führte dann zu einer Fehlermeldung, weil es im Code keine Abfrage auf "nil" gibt.
Die anderen können das vielleicht noch besser erklären, was wir dann aber vielleicht in einen eignen Thread auslagern sollten.
Gruß Kralle
"Alter Pascaler" sagte an einen paar Beispielen, welche Probleme es gibt, wenn man speicher nicht wieder freigibt.
Wir sprachen dann auch darüber ob man
Code: Alles auswählen
free
Code: Alles auswählen
freeandnil
So kam es das man Testhalber zweimal
Code: Alles auswählen
freeandnil
Das zweite
Code: Alles auswählen
freeandnil
Die anderen können das vielleicht noch besser erklären, was wir dann aber vielleicht in einen eignen Thread auslagern sollten.
Gruß Kralle
OS: Manjaro Linux, Linux Mint und Windows 10
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1
- m.fuchs
- Lazarusforum e. V.
- Beiträge: 2785
- Registriert: Fr 22. Sep 2006, 19:32
- OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
- CPU-Target: x86, x64, arm
- Wohnort: Berlin
- Kontaktdaten:
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Das verstehe ich nicht, du meinst folgende Problemstellung?
Das führt - wie eigentlich auch zu erwarten - zu keinem Problem. Wenn .Free bei einem Objekt aufgerufen wird welches nil ist, passiert eigentlich gar nichts. Deswegen wäre eine zusätzliche Abfrage innerhalb von FreeAndNil überflüssig.
Code: Alles auswählen
program FreeAndNilTest;
{$MODE ObjFPC}
{$H+}
uses
Classes, SysUtils;
var
o: TObject;
begin
o := TObject.Create;
FreeAndNil(o);
FreeAndNil(o);
WriteLn('Done.');
end.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
- AlterPascaler
- Beiträge: 80
- Registriert: Mo 26. Jun 2023, 18:56
- OS, Lazarus, FPC: Linux, Lazarus, Free Pascal
- CPU-Target: xxBit
- Wohnort: Deutschland, NRW
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Es ging darum aufzuzeigen wo die Unterschiede zwischen
und
sind.
Weiter haben wir uns damit beschäftigt, wo die Unterschiede zwischen "o.Free" und "o,Destroy" sind, und ob man FreeAndNil verwenden soll, da es Fehler evtl. verschleiert haben wir auch diskutiert, kamen aber auf keinen gemeinsamen Nenner.
Viele Grüße
AP
Code: Alles auswählen
begin
o := TObject.Create;
FreeAndNil(o);
FreeAndNil(o);
WriteLn('Done.');
end.
Code: Alles auswählen
begin
o := TObject.Create;
o.Free;
o.Free;
WriteLn('Done.');
end.
Weiter haben wir uns damit beschäftigt, wo die Unterschiede zwischen "o.Free" und "o,Destroy" sind, und ob man FreeAndNil verwenden soll, da es Fehler evtl. verschleiert haben wir auch diskutiert, kamen aber auf keinen gemeinsamen Nenner.
Viele Grüße
AP
Viele Grüße
AlterPascaler
AlterPascaler
- kralle
- Lazarusforum e. V.
- Beiträge: 1187
- Registriert: Mi 17. Mär 2010, 14:50
- OS, Lazarus, FPC: Manjaro Linux, Mint und Windows 10 ,Lazarus 3.99, FPC-Version: 3.3.1
- CPU-Target: 64Bit
- Wohnort: Bremerhaven
- Kontaktdaten:
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Moin,
"Nummer8" und "Pluto" haben doch auch in den Quellcode von geschaut und dann wurde doch gesagt, das in der Procedure/Funktion eine Abfrage auf "nil" fehlen würde oder habe ich die Diskussion in dem Punkt falsch verstanden?
Gruß Kralle
"Nummer8" und "Pluto" haben doch auch in den Quellcode von
Code: Alles auswählen
FreeAndnil
Gruß Kralle
OS: Manjaro Linux, Linux Mint und Windows 10
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Aber die nil-Abfrage ist doch in Free enthalten:
Code: Alles auswählen
procedure TObject.Free;
begin
// the call via self avoids a warning
if self<>nil then
self.destroy;
end;
- kralle
- Lazarusforum e. V.
- Beiträge: 1187
- Registriert: Mi 17. Mär 2010, 14:50
- OS, Lazarus, FPC: Manjaro Linux, Mint und Windows 10 ,Lazarus 3.99, FPC-Version: 3.3.1
- CPU-Target: 64Bit
- Wohnort: Bremerhaven
- Kontaktdaten:
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Jetzt bin ich verwirrt.
In dem Code, den ich sah, war die Abfrage nicht vorhanden.
Gruß Kralle
In dem Code, den ich sah, war die Abfrage nicht vorhanden.
Gruß Kralle
OS: Manjaro Linux, Linux Mint und Windows 10
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1
-
- Beiträge: 750
- Registriert: Di 23. Aug 2016, 14:25
- OS, Lazarus, FPC: Windows 11
- CPU-Target: 64Bit
- Wohnort: Berlin
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Guten Morgen,
ohne jetzt zusätzlich Verwirrung stiften zu wollen, vielleicht fehlt mir auch noch ein Kaffee...
wundere ich mich jetzt auch:
Ich bin mal mit der rechten Maustaste auf die Zeile FreeAndNil(o) gegangen
dann "Finde Deklaration FreeAnd Nil" geklickt.
es öffnet sich die Datei sysutilh.inc
Dort steht:
nun klicke ich auch hier auf FreeAndNil und dann rechte Maustaste auf
"Suchen > springe zur Prozedur FreeAndNil"
nun lande ich in sysutils.inc und dort steht:
Also habe ich dort mal einen Breakpoint gesetzt, aber da landet er garnicht..... 
muss ich evtl. Lazarus neu compilieren mit Debuginfo...
wo landet er denn beim Aufruf von FreeAndNil(o); bzw. wo ist diese Procedure definiert.
--edit:
dem Assemblerfenster nach könnte das aber ein solcher Code sein ?? [Klugscheissermodus ein]übrigens wäre das NilAndFree und nicht FreeAndNil, was ich für Multithreads sogar SEHR sinnvoll finde
ohne jetzt zusätzlich Verwirrung stiften zu wollen, vielleicht fehlt mir auch noch ein Kaffee...

wundere ich mich jetzt auch:
Ich bin mal mit der rechten Maustaste auf die Zeile FreeAndNil(o) gegangen
dann "Finde Deklaration FreeAnd Nil" geklickt.
es öffnet sich die Datei sysutilh.inc
Dort steht:
Code: Alles auswählen
procedure FreeAndNil(var obj);
"Suchen > springe zur Prozedur FreeAndNil"
nun lande ich in sysutils.inc und dort steht:
Code: Alles auswählen
procedure FreeAndNil(var obj);
var
temp: tobject;
begin
temp:=tobject(obj);
pointer(obj):=nil;
temp.free;
end;

muss ich evtl. Lazarus neu compilieren mit Debuginfo...
wo landet er denn beim Aufruf von FreeAndNil(o); bzw. wo ist diese Procedure definiert.
--edit:
dem Assemblerfenster nach könnte das aber ein solcher Code sein ?? [Klugscheissermodus ein]übrigens wäre das NilAndFree und nicht FreeAndNil, was ich für Multithreads sogar SEHR sinnvoll finde

Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...
- fliegermichl
- Lazarusforum e. V.
- Beiträge: 1609
- Registriert: Do 9. Jun 2011, 09:42
- OS, Lazarus, FPC: Lazarus Fixes FPC Stable
- CPU-Target: 32/64Bit
- Wohnort: Echzell
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Ich verwende FreeAndNil auch häufig. Oft habe ich Felder in einer Klasse, welche Zeiger auf Strukturen enthalten, welche an unterschiedlichen Stellen freigegeben werden müssen.
Würde ich stattdessen nur Free aufrufen, würde die Referenz in fListe nicht auf nil gesetzt und ein weiterer Aufruf von method1 würde eine Zugriffsverletzung hervorrufen.
Welchen Fehler das verschleiern soll, ist wiederum mir schleierhaft.
Code: Alles auswählen
procedure TMyClass.method1;
begin
if fListe <> nil then FreeAndNil(fListe);
...
end;
Welchen Fehler das verschleiern soll, ist wiederum mir schleierhaft.
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Zum Beispiel sowas:fliegermichl hat geschrieben: Mo 10. Mär 2025, 08:21 Welchen Fehler das verschleiern soll, ist wiederum mir schleierhaft.
Code: Alles auswählen
type
TMyObject = class
DoSomething;
end;
var
m: TMyObject;
begin
m := TMyObject.Create;
// --- viel Code dazwischen
FreeAndNil(m);
// --- viel Code dazwischen
if m <> ni then m.DoSomething;
Ich will FreeAndNil nicht verteufeln und setze es selbst immer wieder ein, aber man muss das mit Bedacht tun, z.B. bei Klassen, die einen weiten Gültigkeitsbereich haben (was an sich aber auch schon wieder ein Problem sein kann)
Zuletzt geändert von wp_xyz am Mo 10. Mär 2025, 11:30, insgesamt 1-mal geändert.
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
TObject.Free und FreeAndNil sind RTL-Routinen. Um dort mit dem Debugger reinzukommen, musst du FPC (nicht nur Lazarus) mit Debug-Informationen neu kompileren.siro hat geschrieben: Mo 10. Mär 2025, 06:35 Also habe ich dort mal einen Breakpoint gesetzt, aber da landet er garnicht.....
muss ich evtl. Lazarus neu compilieren mit Debuginfo...
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Was erwartest du davon?siro hat geschrieben: Mo 10. Mär 2025, 06:35 Also habe ich dort mal einen Breakpoint gesetzt, aber da landet er garnicht.....
muss ich evtl. Lazarus neu compilieren mit Debuginfo...
Du kannst den Code ja auch so verfolgen.
FreeAndNil ruft halt TObject.Free auf und dies wiederum macht nach einem Nil check TObject.Destroy.
Ende Gelände.
-
- Beiträge: 2105
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Um da mal ein bisschen weiter drauf einzugehen, also das Grundproblem ist das das Manuelle Speicherverwalten ein sehr schweres Problem ist, im sinne von wissen wann Speicher im code nicht mehr benötigt wird kann je nach Code sehr kompliziert werden. Deshalb gibt es mehrere Methoden damit umzugehen, Grundsätzlich bewährt sich ein Ownership model.wp_xyz hat geschrieben: Mo 10. Mär 2025, 10:16 Ich will FreeAndNil nicht verteufeln und setze es selbst immer wieder ein, aber man muss das mit Bedacht tun, z.B. bei Klassen, die einen weiten Gültigkeitsbereich haben (was an sich aber auch schon wieder ein Problem sein kann)
Die Grundannahme dabei ist das ein Objekt in einem von zwei Stati sein kann. Entweder ein Objekt ist Valide und man darf drauf Zugreifen, oder es ist das nicht und man darf es nicht. Die Idee hinter dem Ownership Modell ist damit recht simpel, es gibt einen Owner der die Lebenszeit des Objektes vorgibt, und jeder andere Teil des Codes der dieses Objekt benutzt muss die Lebenszeit des Owners beherzigen.
Als Beispiel nehmen wir mal einen sehr einfachen Fall, eine Lokale Instanz einer TStringList um eine .ini Datei zu lesen:
Code: Alles auswählen
procedure LoadConfig(const FileName: String);
var
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.LoadFromFile(FileName);
LoadConfigPart1(sl);
LoadConfigPart2(sl);
finally
sl.Free;
end;
end;
Im prozeduralen ist das ganze sehr einfach Umzusetzen, in OOP wird das ganze schon komplizierter, da nun Objekte andere Objekte halten können. Dabei ist der Owner normalerweise ein Objekt mit längerer Laufzeit. Beispiel im Component System von Lazarus, kan jede Komponente andere Komponenten enthalten, und im Destruktor der Komponente werden dann alle Enthaltenen Komponenten gefreed.
Das Problem ist nun wenn man Nil mit reinnimmt gibts plötzlich einen Dritten Status: Valide aber nicht vorhanden. Beispiel von oben:
Code: Alles auswählen
procedure LoadConfig(const FileName: String);
var
sl: TStringList;
begin
// SL nicht zugewiesen, zugriff darauf nicht erlaubt
sl := TStringList.Create;
try
// SL zugewiesen zugriff erlaubt
sl.LoadFromFile(FileName);
LoadConfigPart1(sl);
LoadConfigPart2(sl);
finally
sl.Free;
end;
// SL nicht mehr zugewiesen zugriff nicht erlaubt
end;
Es gibt also wirklich einen Haufen an tricks die man machen kann um sicherzustellen das die Lebenszeiten Respektiert werden und man nicht Objekte anfasst die nicht mehr existieren.
Mit Nil wirds jetzt aber Kompliziert:
Code: Alles auswählen
procedure LoadConfig(const FileName: String);
var
sl: TStringList = nil;
begin
// SL ist zugewiesen und damit valide, verweist aber auf kein Objekt
sl := TStringList.Create;
try
// SL zugewiesen und verweist auf Objekt
sl.LoadFromFile(FileName);
LoadConfigPart1(sl);
LoadConfigPart2(sl);
finally
FreeAndNil(sl);
end;
// SL ist zugewiesen aber verweist auf kein Objekt
end;
Auch Informationstheoretisch ist es eine ganz andere Hausnummer, die Anzahl an States in denen ein Programm sein kann ist rein Kombinatorisch die Möglichen Stati des Objektes hoch der Anzahl der der Möglichen Zugriffe auf das Objekt, also wenn man Binär denkt, ein Objekt ist entweder Valide oder Invalide, bei 5 Zugriffen gibts Theoretisch nur 2^5=32 mögliche Kombinationen. Wenn man noch Nil als dritten Zustand hinzufügt ists 3^5=243. Hierbei explodiert die Komplexität sehr schnell.
Es gibt situationen in denen ist das Ownership Modell nicht das richtige, und Nil sehr nützlich. Beispiel hierfür ist wenn man einen Thread hat und wenn der Thread fertig ist, freed er sich selbst und setzt die Thread variable auf Nil (natürlich synchronized/locked). Der Main thread kann dann schauen ob die Thread variable Nil ist oder nicht, ob der Thread noch läuft. Das funktioniert weil hier das Ownership prinzip nicht greift, da der Thread vom Mainthread erstellt wird, von sich selbst aber gefreed wird, somit gibt es keinen klaren Owner der die Lebenszeit vorgibt, und NIL wird als Signal verwendet ob das Objekt noch am leben ist oder nicht.
Das Ding ist, während es solche Fälle gibt, sind sie extrem selten. Die allermeisten vorkommen von Klassennutzung sind innerhalb des Ownership Modells. Entweder als Teil einer Funktion (wie SL oben), oder als Feld einer Klasse (und wird im Destruktor gefreed).
In all diesen Fällen braucht man kein NIL, und wie oben beschrieben, ist die Einführung von NIL sogar kontraproduktiv, da hierbei viele der Mechanismen die FPC bereitstellt um solche Fehler zu finden, aktiv umgangen werden.
Daher ist meine Faustregel: Nil sollte man nur benutzen wenn man es aktiv braucht, wenn man nicht vor hat nil aktiv zu verwenden, dann sollte man einfach die Finger von lassen
- fliegermichl
- Lazarusforum e. V.
- Beiträge: 1609
- Registriert: Do 9. Jun 2011, 09:42
- OS, Lazarus, FPC: Lazarus Fixes FPC Stable
- CPU-Target: 32/64Bit
- Wohnort: Echzell
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Naja, ich benutze das sehr häufig.
In meinem CAD Programm haben viele Objekte zusätzliche Eigenschaften, welche aber nur bei Bedarf ermittelt werden.
Beim ersten Zugriff auf die Eigenschaft prüfe ich, ob diese noch nil ist.
In dem Fall wird eine Methode aufgerufen, welche die zusätzlichen Informationen erstellt (z.B. Area einer Fläche). Das Ergebnis wird dann einem privaten Feld zugewiesen.
Bei einem erneuten Zugriff sind die entsprechenden Informationen schon vorhanden und müssen nicht neu ermittelt werden (Cache System)
Zwischendrin kann dann aber auch schonmal die Methode Invalidate aufgerufen werden. (z.B. ein zusätzlicher Punkt wurde in eine Fläche eingefügt)
Dann wird das lokale Feld wieder mit FreeAndNil freigegeben.
Im Destructor wird geprüft, ob das Feld <> nil ist und dann eben freigegeben.
In meinem CAD Programm haben viele Objekte zusätzliche Eigenschaften, welche aber nur bei Bedarf ermittelt werden.
Beim ersten Zugriff auf die Eigenschaft prüfe ich, ob diese noch nil ist.
In dem Fall wird eine Methode aufgerufen, welche die zusätzlichen Informationen erstellt (z.B. Area einer Fläche). Das Ergebnis wird dann einem privaten Feld zugewiesen.
Bei einem erneuten Zugriff sind die entsprechenden Informationen schon vorhanden und müssen nicht neu ermittelt werden (Cache System)
Zwischendrin kann dann aber auch schonmal die Methode Invalidate aufgerufen werden. (z.B. ein zusätzlicher Punkt wurde in eine Fläche eingefügt)
Dann wird das lokale Feld wieder mit FreeAndNil freigegeben.
Im Destructor wird geprüft, ob das Feld <> nil ist und dann eben freigegeben.
- AlterPascaler
- Beiträge: 80
- Registriert: Mo 26. Jun 2023, 18:56
- OS, Lazarus, FPC: Linux, Lazarus, Free Pascal
- CPU-Target: xxBit
- Wohnort: Deutschland, NRW
Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)
Wahrscheinlich nerve ich schon wieder den einen oder anderen, aber ich möchte an dieser Stelle nochmal auf TInterfaceObject hinweisen.
Bei Objekten wo ich ganz schwer die Lebensdauer abschätzen kann, verwende ich TInterfacedObject. Seitdem ist das Entwicklerleben viel leichter geworden. TInterfacedObject hat die schöne Eigenschaft das es sich selber auflöst, wenn es nicht mehr gebraucht wird.
Realisiert wird das durch einen internen Referenzzähler. Bei jeder Zuweisung wird der Zähler um 1 erhöht. Bei jeder Auflösung (Verlassen einer Funktion oder Klasse) wird der Referenzzähler um 1 erniedrigt. Beim wechsel von 1 auf 0 wird die Klasse freigegeben.
Hier ein kleines Demo Programm was das Verhalten von TInterfacedObject zeigen soll.
Also übt nicht soviel Kritik:)
Viele Grüße
AP
Bei Objekten wo ich ganz schwer die Lebensdauer abschätzen kann, verwende ich TInterfacedObject. Seitdem ist das Entwicklerleben viel leichter geworden. TInterfacedObject hat die schöne Eigenschaft das es sich selber auflöst, wenn es nicht mehr gebraucht wird.
Realisiert wird das durch einen internen Referenzzähler. Bei jeder Zuweisung wird der Zähler um 1 erhöht. Bei jeder Auflösung (Verlassen einer Funktion oder Klasse) wird der Referenzzähler um 1 erniedrigt. Beim wechsel von 1 auf 0 wird die Klasse freigegeben.
Hier ein kleines Demo Programm was das Verhalten von TInterfacedObject zeigen soll.
Also übt nicht soviel Kritik:)
Code: Alles auswählen
program demo_ifo_01;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
Classes
{ you can add units after this };
type
ICfg = interface
function Data : String;
end;
{ TICfg }
TICfg = class(TInterfacedObject, ICfg)
private
CfgData: String;
public
constructor Create(const aCfgData: String);
destructor destroy; override;
function Data : String;
end;
{ TCfgThread }
TCfgThread = class(TThread)
private
Cfg: ICfg;
protected
procedure Execute; override;
public
constructor Create(aCfg: ICfg);
end;
{ TICfg }
constructor TICfg.Create(const aCfgData: String);
begin
inherited Create;
CfgData:= aCfgData;
WriteLn('TICfg.Create');
end;
destructor TICfg.Destroy;
begin
WriteLn('TICfg.Destroy');
inherited;
end;
function TICfg.Data: String;
begin
Result:= CfgData;
end;
{ TCfgThread }
procedure TCfgThread.Execute;
begin
while not Terminated do begin
Sleep(Random(5000));
WriteLn(Cfg.Data);
Terminate;
end;
end;
constructor TCfgThread.Create(aCfg: ICfg);
begin
Cfg:= aCfg;
FreeOnTerminate:= True;
inherited Create(False);
end;
procedure DemoIfo;
var
cfg: ICfg;
begin
cfg:= TICfg.Create('InterfacedObject Demo');
TCfgThread.Create(cfg);
TCfgThread.Create(cfg);
TCfgThread.Create(cfg);
// Es gibt kein cfg.Free, und trotzdem wird aufgeräumt wenn das Object
// nicht mehr gebraucht wird.
end;
begin
Randomize;
DemoIfo;
TThread.Sleep(7000);
WriteLn('Bye');
end.
AP
Viele Grüße
AlterPascaler
AlterPascaler