Ich habe ein Scanprogramm, dass die Bilder sofort verarbeitet und dem Benutzer Vorschläge für die beste Komprimierung machen soll. Für die Berechnung werden Threads verwendet. Das Komprimieren der Bilder (derzeit PNG und JPEG) erledige ich über den Code/die Units von Delphi. Und weil die Bilder größer sind, kann es manchmal dauern.
Wenn der Benutzer das Programm beendet, klappt das Dank Threads, hinterlässt aber Speicherlecks. Das gefällt mir natürlich nicht.
Der Code dazu sieht folgendermaßen aus:
Delphi-Quellcode:
Unit _CalcThread;
Interface
Uses
System.Classes,
Vcl.Graphics,
Vcl.Imaging.pngimage,
Vcl.Imaging.jpeg;
Type
TCodecType = (cdPNG, cdJPEG);
TOnCalcDone =
Procedure(Codec: TCodecType; Size: Int64)
Of Object;
TCalcThread =
Class(TThread)
Private
fSize : Int64;
fCodec : TCodecType;
fBitmap : TBitmap;
fOnCalcDone : TOnCalcDone;
Procedure EventOnCalcDone;
Public
Constructor Create(Codec: TCodecType; Bitmap: TBitmap;
OnCalcDone: TOnCalcDone);
Procedure Execute;
Override;
Destructor Destroy;
Override;
End;
Implementation
// Absicherung des Eriegnis-Aufrufs --------------------------------------------
Procedure TCalcThread.EventOnCalcDone;
Begin
If Assigned(fOnCalcDone)
Then // Sicherheitsprüfung
FOnCalcDone(fCodec, fSize);
// Ereignis auslösen
End;
// Ein Thread wird erstellt ----------------------------------------------------
Constructor TCalcThread.Create(Codec: TCodecType; Bitmap: TBitmap;
OnCalcDone: TOnCalcDone);
Begin
fSize:= -1;
// ungültigen Wert vorgeben
fCodec:= Codec;
// Codec merken
fOnCalcDone:=OnCalcDone;
// Zeiger auf Ereignis merken
fBitmap:=TBitmap.Create;
// TBitmap anlegen
fBitmap.Assign(Bitmap);
// Bild kopieren
FreeOnTerminate:=True;
// Speicher selbst freigeben
Inherited Create(True);
// Thread erstellen
Priority:= tpIdle;
// geringe Priorität
// Resume; // "Start" löst Exception aus!
Start;
// "Resume" ist veraltet
End;
// Der eigentliche Thread (=die Berechnung) ------------------------------------
Procedure TCalcThread.Execute;
Var
lMemStream : TMemoryStream;
Begin
lMemStream:=TMemoryStream.Create;
// Nur im Speicher arbeiten
Try
Case fCodec
Of
cdPNG :
With TPngImage.Create
Do // temp. PNG-Objekt anlegen
Try
Filters:=[pfNone, pfSub, pfUp, pfAverage, pfPaeth];
CompressionLevel:=9;
// Maximale Kompression
Assign(fBitmap);
// Bilddaten übernehmen
SaveToStream(lMemStream);
// virtuell abspeichern
fSize:=lMemStream.Size;
// Größe merken
Finally
Free;
// PNG-Objekt freigeben
End;
cdJPEG:
With TJPEGImage.Create
Do // tenp. JPEG-Objekt anlegen
Try
CompressionQuality:=80;
// 100 = max, 1 = min
Assign(fBitmap);
// Bilddaten übernehmen
SaveToStream(lMemStream);
// virtuell abspeichern
fSize:=lMemStream.Size;
// Größe merken
Finally
Free;
// JPEG-Objekt freigeben
End;
End;
Finally
lMemStream.Free;
// TMemoryStream freigeben
Synchronize(EventOnCalcDone);
// Das Ende mitteilen
End;
End;
// Der Thread wird beendet -----------------------------------------------------
Destructor TCalcThread.Destroy;
Begin
fBitmap.Free;
// TBitmap selbst freigeben
fOnCalcDone:=nil;
// Zeiger löschen
Inherited;
// den Rest ausführen
End;
End.
Da der Code unter
Execute
sehr kurz ist, macht es keinen Sinn auf
Terminated
zu reagieren.
Zusatzfrage:
Der Compiler sowie die Hilfe sagen mir,
TThread.Resume;
sei veraltet. Allerdings erzeugt
TThread.Start;
bei mir immer eine
Exception vom Typ
EInvalidPointer
. Und was noch schlimmer ist: bevor ich die Message dazu erhalte, zeigt mir Windows 8.1 an
"Das Programm ... funktioniert nicht mehr richtig. Online nach einer Lösung suchen...". Der Debugger stürzt ab, die exe-Datei lässt sich nicht mehr löschen und damit neu compilieren.
Wie löse ich diese Probleme?
Gruß, Alex