![]() |
Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Ich habe eine generische TObjectList, in der u.a. auch Vorschaubilder als TBitmap gespeichert sind. Ich möchte diese Vorschaubilder gern auf der Platte speichern, damit sie nicht jedes Mal unnötiger Weise erstellt werden müssen.
Ich habe jetzt eine funktionierende Lösung, die mit Streams arbeitet. Nachteil nur, dass nach dem Wiedereinlesen in die TObjectList bestimmte Bilder auf dem Kopf stehen. Das hängt meiner Vermutung nach damit zusammen, dass bei Bitmaps gern bei der untersten Scanline angefangen wird und nicht bei der obersten. Ich weiß, dass ich den Bitmap-Header auslesen könnte, um die Leserichtung zu ermitteln. Meine Idee wäre jetzt aber: Die Bitmap liegt doch im Speicher exakt so vor, wie sie richtig ist. Kann ich sie nicht einfach als einfache Bytes unter Umgehung aller Bitmap-Routinen in einen Stream schreiben und - das vor allem - aus dem (File-)Stream wieder in die Objektliste? |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Wie speicherst du die Bitmaps denn jetzt?
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Delphi-Quellcode:
Die exakte Größe der Bitmap im Speicher müsste nach
procedure SchreibeBilderListeInDB;
var BildNr:integer; Writer: TWriter; Stream : TFileStream; begin Try Stream := TFileStream.Create(Dateiname,fmCreate); Except exit; End; Writer := TWriter.Create(Stream,4096); Try Writer.WriteListBegin; // Anzahl Datensätze Writer.WriteInteger(Bilderliste.Count); For BildNr := 1 to BilderListe.Count do WriteBitmapToWriter(Bilderliste[BildNr - 1].VSB,Writer); Writer.WriteListEnd; Writer.FlushBuffer; Finally Stream.Free; Writer.Free; End; end; procedure WriteBitmapToWriter(Bmp: TBitmap; Writer: TWriter); var Stream: TMemoryStream; //----------------------------------------------------------------------------------------------------------------------------- procedure WriteStreamToWriter(Stream: TMemoryStream; Writer: TWriter); const BufBytes = 1024*16; // 16 Kb var ReadBytes: Integer; Buf: array [0..BufBytes-1] of Byte; begin Stream.Position := 0; Writer.WriteInteger(Int64(Stream.Size)); while Stream.Position < Stream.Size do begin ReadBytes := Stream.Read(Buf, BufBytes); Writer.Write(Buf, ReadBytes); end; end; //----------------------------------------------------------------------------------------------------------------------------- begin Stream := TMemoryStream.Create; try Bmp.SaveToStream(Stream); WriteStreamToWriter(Stream, Writer); finally Stream.Free; end; end;
Delphi-Quellcode:
InfoHeaderSize + ImageSize sein. Ich stelle mir also vor, diese Anzahl Bytes ab Addr(Bilderliste[BildNr - 1].VSB) auszulesen und in eine Datei zu schreiben. Lesen kann auch kein Problem sein. Aber wie schreibe ich die Bytes in den Speicher? Speicher reservieren, Bytes reinschreiben, und dann den Pointer von Bilderliste[BildNr - 1].VSB auf diese Adresse setzen oder wie?
GetDIBSizes(Bmp.Handle, InfoHeaderSize, ImageSize);
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Also wenn ich Bitmaps aus einer Liste in einen Stream speichern wollte, dann würde ich das so machen:
Delphi-Quellcode:
Das Lesen ist etwas aufwändiger (aber nicht wirklich)
procedure TBitmapList.SaveToStream( AStream: TStream );
var idx: Integer; begin AStream.WriteData( Count ); for idx := 0 to Count - 1 do Self[ idx ].SaveToStream( AStream ); end;
Delphi-Quellcode:
und zur Vollständigkeit
procedure TBitmapList.LoadFromStream( AStream: TStream );
var idx, lCount: Integer; lBitmap : TBitmap; begin Clear; AStream.ReadData( lCount ); for idx := 0 to lCount - 1 do begin lBitmap := TBitmap.Create; try lBitmap.LoadFromStream( AStream ); Add( lBitmap ); lBitmap := nil; finally lBitmap.Free; end; end; end;
Delphi-Quellcode:
type
TBitmapList = class( TObjectList<TBitmap> ) public procedure SaveToStream( AStream: TStream ); procedure LoadFromStream( AStream: TStream ); end; |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Eine sehr schöne, kurze und elegante Lösung - aber die Bilder stehen nach dem Einlesen aus der Datei auf dem Kopf.
Das muss mit den Bitmap-Routinen zu tun haben, die ich aus diesem Grund ja vermeiden wollte. Ich habe mir mittlerweile einen Weg gebastelt, die Bytes direkt in einen Stream zu schreiben und sie dann auch wieder auszulesen. Das Schreiben funktioniert auch, aber - obwohl die Anzahl der Bytes genau stimmt - meldet Delphi beim Einlesen "Ungültiges Bitmap". Ich werde morgen die Routinen mal nach deinem Vorbild anpassen und dann sehen, ob es funktioniert. Vielen Dank einstweilen. |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Die eigentlichen Daten verwaltet das GDI (BitmapInfoHeader, Palette, Buffer der Pixeldaten), die liegen also nicht alle zusammen schön geordnet in einem Speicherbereich. Wenn man die Pixeldaten per Scanline einlesen möchte, vorher also mindestens diese Schritte: - Erzeugen der TBitmap-Instance - Setzen von Pixelformat, Breite, Höhe |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Danke für die Antworten, ich komme heute leider nicht mehr zu weiteren Erforschungen.
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Und manchmal auch noch in einer anderen Reihenfolge, als man denkt, z.B. die Zeilen gern von unten nach oben. :stupid: |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
So, und für Folgendes bitte ich um eine Erklärung.
Ich lade ein Bitmap aus einer Datei, zeichne sie auf die Form, speichere sie per Stream in einer Datei, lese sie wieder aus und zeichne sie erneut. Ergebnis: Beide Bilder werden richtig angezeigt. Danach lade ich ein JPG aus einer Datei, extrahiere eine Bitmap und zeige sie an. Ergebnis: Sie wird richtig angezeigt. Danach speichere sie per Stream in einer Datei, lese sie wieder aus und zeichne sie erneut. Ergebnis: Das Bild wird auf dem Kopf stehend angezeigt. In allen 4 Fällen ist im InfoHeader der Wert von biHeight positiv.
Delphi-Quellcode:
procedure TForm1.ZeigeBitmap;
var Bmp:TBitmap; Stream : TMemoryStream; FStream:TFileStream; BildAnz:integer; const Verz = 'C:\Temp\'; JPGDatei = 'Test.JPG'; BMPDatei = 'Test.bmp'; DBDatei = 'Test.db'; begin // Bitmap laden Bmp := TBitmap.Create; Bmp.LoadFromFile(Verz + BMPDatei); // Auf Form zeichnen Self.Canvas.Draw(10,10,Bmp); // In DB speichern FStream := TFileStream.Create(Verz + DBDatei,fmCreate); BildAnz := 1; FStream.WriteData(BildAnz); Bmp.SaveToStream(FStream); FStream.Free; Bmp.Free; // Wieder laden und anzeigen Bmp := TBitmap.Create; Stream := TMemoryStream.Create; Stream.LoadFromFile(Verz + DBDatei); Stream.ReadData(BildAnz); Bmp.LoadFromStream(Stream); Self.Canvas.Draw(10,250,Bmp); Bmp.Free; // Thumbnail aus JPG-Datei Bmp := TBitmap.Create; ExtractThumbnail(Bmp,Verz + JPGDatei,400,0); // Auf Form zeichnen Self.Canvas.Draw(450,10,Bmp); // In DB speichern FStream := TFileStream.Create(Verz + DBDatei,fmCreate); BildAnz := 1; FStream.WriteData(BildAnz); Bmp.SaveToStream(FStream); FStream.Free; Bmp.Free; // Wieder laden und anzeigen Bmp := TBitmap.Create; Stream := TMemoryStream.Create; Stream.LoadFromFile(Verz + DBDatei); Stream.ReadData(BildAnz); Bmp.LoadFromStream(Stream); Self.Canvas.Draw(450,250,Bmp); Bmp.Free; Stream.Free; end; procedure ExtractThumbnail(Bitmap:TBitmap; FileName:string; DesiredWidth:integer; DesiredHeight:integer); var Malloc:IMalloc; DesktopFolder,SourceFolder:IShellFolder; eaten,flags,prio:cardinal; id:PItemIDList; ex:IExtractImage; s:TSize; h:HBITMAP; w:WideString; begin try OleCheck(SHGetMalloc(Malloc)); OleCheck(SHGetDesktopFolder(DesktopFolder)); flags:=0; w:=ExtractFilePath(FileName); OleCheck(DesktopFolder.ParseDisplayName(0,nil,PWideChar(w),eaten,id,flags)); try OleCheck(DesktopFolder.BindToObject(id,nil,IShellFolder,SourceFolder)); finally Malloc.Free(id); end; w:=ExtractFileName(FileName); OleCheck(SourceFolder.ParseDisplayName(0,nil,PWideChar(w),eaten,id,flags)); If SourceFolder.GetUIObjectOf(0,1,id,IExtractImage,nil,ex) <> S_OK then exit; s.cx:=DesiredWidth; s.cy:=DesiredHeight; If (s.cx > 160) or (s.cy > 90) then flags:=IEIFLAG_SCREEN or IEIFLAG_OFFLINE or IEIFLAG_QUALITY or IEIFLAG_ORIGSIZE else flags:=IEIFLAG_SCREEN or IEIFLAG_OFFLINE or IEIFLAG_QUALITY; prio:=0; SetLength(w,MAX_PATH); OleCheck(ex.GetLocation(PWideChar(w),Length(w)*2,prio,s,32,flags)); OleCheck(ex.Extract(h)); Bitmap.Handle:=h; finally Malloc.Free(id); DesktopFolder:=nil; SourceFolder:=nil; Malloc:=nil; end; end; |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Hört sich irgendwie nach diesem Fehler an:
![]() |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Du kannst übrigens den upside-down Effekt auch schon beim ersten Zeichnen hervorrufen:
Delphi-Quellcode:
Damit ist eigentlich schon sehr wahrscheinlich, daß Windows offenbar das biHeight im InfoHeader falsch angibt.
ExtractThumbnail(Bmp, Verz + JPGDatei, 400, 0);
Bmp.Dormant; Es hat somit nicht mit dem Speichern und Laden zu tun. |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Nein, das Bild hat natürlich immer eine positive Höhe,
aber wie intern die Daten im RAM liegen, das hat damit absolut nichts zu tun, sondern das hängt vom Datenformat ab. Beispiel: BigEndian und LittleEndian :stupid: |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Na dann. :oops:
Aber wer glaubt schon einer Dokumentation? :stupid: Wenn ich das täte, dann wäre in Delphi Vieles nicht "vorhanden", weil die OH immer behauptet "hier steht nix". :lol: |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Delphi-Quellcode:
lBitmap := TBitmap.Create;
try // Übergabe des Lifetime-Managements ... // Jetzt bin ich nicht mehr verantwortlich lBitmap := nil; finally lBitmap.Free; end; |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Edit: Ah, jetzt hab ich's kapiert. Im Falle einer Exception greift das Free ja noch. :wall: |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Irgendwie entzieht sich mir der Sinn des Ganzen. Wenn ich doch bereits eine generische TObjectList habe, wieso verwendet man dann nicht einfach die Methoden, die das Objekt bereitstellt oder ergänzt das Objekt um eben diese Methoden? Wenn ich z.B. ein Objekt erstelle, das sagen wir mal ein TImage und ein TLabel enthält, dann sieht das z.B. so aus:
Delphi-Quellcode:
So, jetzt brauche ich eine Methode, die das Bild speichert. Also ergänze ich das Objekt um die benötigte Methode. Wäre in diesem Fall, da ich bereits ein TImage verwende, nicht notwendig, da TImage (z.B. in TBitmap oder TPicture) bereits mehrere Methoden zum Speichern bereitstellt.
UNIT Bilder;
INTERFACE USES Classes, ExtCtrls, StdCtrls, Graphics, Controls; TYPE TBild = CLASS PRIVATE { Private-Deklarationen } Var fBild : TImage; fTitel : TLabel; Function GetfBild : TImage; Procedure SetfBild(Const Value : TImage); Function GetfTitel : TLabel; Procedure SetfTitel(Const Value : TLabel); PUBLIC { Public-Deklarationen } Constructor Create(); Destructor Destroy; override; Property Bild : TImage read GetfBild write SetfBild; Property Titel : TLabel read GetfTitel write SetfTitel; END; IMPLEMENTATION { TBild } Function TBild.GetfBild: TImage; begin Result := fBild; end; Procedure TBild.SetfBild(Const Value: TImage); begin fBild.Assign(Value); end; Function TBild.GetfTitel: TLabel; begin Result := fTitel; end; Procedure TBild.SetfTitel(Const Value: TLabel); begin fTitel.Assign(Value); end; Constructor TBild.Create; begin inherited; fBild := TImage.Create(nil); fBild.Visible := False; fBild.AutoSize := False; fBild.Stretch := True; fBild.Proportional := True; fBild.Center := True; fBild.Picture.Bitmap.Canvas.Brush.Style := bsClear; fBild.Picture.Bitmap.Canvas.Pen.Color := clRed; fBild.Picture.Bitmap.Canvas.Pen.Style := psSolid; fBild.Picture.Bitmap.Canvas.Pen.Width := 5; fTitel := TLabel.Create(nil); fTitel.AutoSize := False; fTitel.Alignment := taCenter; fTitel.Layout := tlCenter; fTitel.WordWrap := False; fTitel.Visible := False; end; Destructor TBild.Destroy; begin If Assigned(fBild) Then fBild.Free; If Assigned(fTitel) Then fTitel.Free; inherited; end; end. Wieso muß ich da umständlich ermitteln, wo das Bild im Speicher liegt, wo es beginnt und wo es aufhört, wenn ich das Ganze doch sowieso in einer TObjectList verwalte?
Delphi-Quellcode:
Wozu dann überhaupt die TObjectList? Die TObjectList weiß doch genau, wo ihre Objekte liegen, darum muß mich doch gar nicht mehr kümmern. Ich greife einfach über den Index auf das jeweilige Objekt zu:
...
USES Windows, Messages, ..., Bilder, Generics.Collections, ...; ... PRIVATE { Private-Deklarationen } Var BList : Generics.Collections.TObjectList<TBild>;
Delphi-Quellcode:
Procedure TFormMain.SaveBitmapFromList(Const Id : Integer; Const Datei : String);
begin BList[Id].Bild.Picture.Bitmap.SaveToFile(Datei); end; |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Hmm..
Anscheinend wird bei dem Bitmap, welches per ExtractThumbnail geholt wird etwas bei SaveToStream anders gemacht, wie bei Draw.. Wenn Du zunächst das Thumbnail mit einem 2. Bitmap holts und es dann auf dein eigentliches Bitmap malst (.Draw) dann gehts..
Delphi-Quellcode:
So ist dann das Bitmap in bmp mit der korrekten Orientierung.// Thumbnail aus JPG-Datei Bmp := TBitmap.Create; Bmp2:= TBitmap.Create; try ExtractThumbnail(Bmp2,Verz + JPGDatei,400,0); // Auf Form zeichnen Self.Canvas.Draw(450,10,Bmp2); Bmp.Assign(bmp2); Bmp.Canvas.Draw(0,0,bmp); .. finally Bmp2.Free; Bmp.Free; end; |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Delphi-Quellcode:
zu tun. Ein simples
SaveToStream
Delphi-Quellcode:
, was lediglich ein neues GDI-Objekt forciert, zeigt schon den Fehler. Vielleicht hat man den verlinkten Bug in der Implementation des IExtractImage Interfaces nicht mit gefixt.
bmp.Dormant
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Danke an euch alle. Ich bin natürlich froh, dass ich jetzt nicht als Depp dastehe.
Die Lösung von HolgerX ist ziemlich schräg, funktioniert aber (und warum?). Das doppelte Zeichnen ist unter Umständen verschmerzbar, weil ja nur die sichtbaren Vorschaubilder gezeichnet werden, das ist überschaubar. Übrigens war es bei mir so, dass nicht immer alle Bilder auf dem Kopf standen, sondern nur die Hochkantbilder, während die querkantigen richtig herum waren. Ich werde mal sehen, ob HolgerX' Lösung auch hier wirkt. @Perlsau: Danke für deine Mühe. Ich möchte natürlich alle Bilder in einer einzigen Datei speichern. Das könnte ich natürlich auch in die Klasse der Objectlist integrieren. Der direkte Speicherzugriff war ja doch nur wegen der Rotationsproblematik. |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Die Implementierung von HolgerX' Idee hat nicht funktioniert und sie war mir ohnehin nicht so geheuer, weil ich nicht nachvollziehen konnte, warum sie überhaupt manchmal funktioniert.
Ich bin jetzt den Weg gegangen, einfach biHeight manuell negativ zu setzen, denn die Bitmap muss doch gar nicht gedreht, sondern es müssen nur die Scanlines in anderer Richtung ausgelesen werden. Das funktioniert prima. Dabei bin auch auch gleich Perlsaus Rat gefolgt, die Funktion in die Klasse zu verlegen. Wird hier noch Verbesserungspotenzial gesehen (bis auf die Sünde, Umlaute zu verwenden)? Dieses doppelte "Free" zum Beispiel stört mich immer. Zur Erläuterung der PosBiHeight: Die Größe des FileHeaders ist immer fix bei 14 Bytes. Beim nachfolgenden DIB header gibt es 7 Größen, aber alle fangen mit 3 x 4 Byte an. Die hartkodierte numerische Angabe erscheint mir sicherer als so etwas wie SizeOf(BitmapFileHeader) + SizeOf(InfoHeader.biSize) + SizeOf(InfoHeader.biWidth).
Delphi-Quellcode:
function TBilderListe.LeseVSBAusDatei(Dateiname: string): Boolean;
var BildNr,AnzDS:integer; Stream,VSBStream:TMemoryStream; VSBBmp:TBitMap; VSBHöhe:integer; const PosBiHeight = 14 + 4 + 4; begin Result := False; If not FileExists(Dateiname) then exit; Stream := TMemoryStream.Create; Try Stream.LoadFromFile(Dateiname); Except Stream.Free; exit; End; Stream.Position := 0; Stream.ReadData(AnzDS); If AnzDS <> BilderListe.Count then exit; VSBStream := TMemoryStream.Create; Try For BildNr := 0 to AnzDS - 1 do begin VSBBmp := TBitMap.Create; VSBBmp.LoadFromStream(Stream); VSBStream.Clear; VSBBmp.SaveToStream(VSBStream); VSBStream.Position := PosBiHeight; VSBStream.ReadData(VSBHöhe,SizeOf(VSBHöhe)); VSBStream.Position := PosBiHeight; VSBStream.WriteData(-Abs(VSBHöhe)); VSBStream.Position := 0; VSBBmp.LoadFromStream(VSBStream); Self[BildNr].VSB := VSBBmp; end; Stream.Free; VSBStream.Free; Except Stream.Free; VSBStream.Free; exit; End; Result := True; end; |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Dann schau dir mal
Delphi-Quellcode:
an, denn dafür ist das da.
try finally
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Ja. Nur dass dann bei einer Exception der Code durchläuft und Result damit True ist.
Ich habe jetzt zu einem Sprunglabel gegriffen. Bei dem "exit" da oben kommt es ja auch zu einem Speicherleck. |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Delphi-Quellcode:
Wenn das Laden der Datei in den Stream schiefläuft, bleibt Result = False und somit wird die Methode nach dem Try-Finally-Block mit Exit verlassen. Normalerweise kann hier nichts schieflaufen, da ja die Existenz der Datei zuvor abgefragt wird. Einzige Fehlerquelle, die mir jetzt noch einfällt: Die Datei ist zu groß für den verfügbaren Arbeitsspeicher. Ich verwende daher immer noch einen Try-Except-Block innerhalb des Try-Finally-Blocks, um die Fehlermeldung zu erhalten, die in eine globale Fehlervariable geschrieben wird. Aber das kannst du letztendlich halten, wie du willst ...
function TBilderListe.LeseVSBAusDatei(Dateiname: string): Boolean;
var BildNr,AnzDS:integer; Stream,VSBStream:TMemoryStream; VSBBmp:TBitMap; VSBHöhe:integer; const PosBiHeight = 14 + 4 + 4; begin Result := False; If not FileExists(Dateiname) then exit; Stream := TMemoryStream.Create; Try Stream.LoadFromFile(Dateiname); Result := True; Finally Stream.Free; End; If Not Result Then Exit; ... |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Ich habe gerade noch ein bisschen gesucht. Nirgendwo wird man explizit darauf hingewiesen, dass es nach einer Exception nach dem Finally nicht weitergeht. Ich verstehe übrigens nicht, warum es nach all den vielen Jahren immer noch kein Try...except...finally...end gibt. Man muss immer noch einen Block zwischen Try und Finally dazwischenklemmen. |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Nirgendwo?
![]() ![]() |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Irgendwo bestimmt, aber auch in deiner Zitatstelle finde ich das nicht.
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Gar nicht so schwer zu finden.
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Sagen wir mal so: Wenn es die Datei eben noch gegeben hat, dann kann diese jetzt schon wieder über den Jordan sein. Wozu also der "DoppelTest" (Prüfen und beim Laden wird auch geprüft), wenn die Routine eh abgebrochen werden soll?
Ich würde da gar nicht so einen Aufriss von machen:
Delphi-Quellcode:
Wenn es jetzt rummst (sollte im Normalfall aber gar nicht rummsen), dann wieß man auch direkt warum :stupid:
procedure TBilderListe.LeseVSBAusDatei(Dateiname: string);
var BildNr,AnzDS: integer; Stream,VSBStream: TMemoryStream; VSBBmp: TBitMap; VSBHöhe: integer; const PosBiHeight = 14 + 4 + 4; begin Stream := nil; VSBStream := nil; Try Stream := TMemoryStream.Create; Stream.LoadFromFile(Dateiname); Stream.Position := 0; Stream.ReadData(AnzDS); If AnzDS <> BilderListe.Count then raise EInvalidOperation.Create('Die Anzahl der Bilder stimmt nicht'); VSBStream := TMemoryStream.Create; For BildNr := 0 to AnzDS - 1 do begin VSBBmp := TBitMap.Create; try VSBBmp.LoadFromStream(Stream); VSBStream.Clear; VSBBmp.SaveToStream(VSBStream); VSBStream.Position := PosBiHeight; VSBStream.ReadData(VSBHöhe,SizeOf(VSBHöhe)); VSBStream.Position := PosBiHeight; VSBStream.WriteData(-Abs(VSBHöhe)); VSBStream.Position := 0; VSBBmp.LoadFromStream(VSBStream); Self[BildNr].VSB := VSBBmp; VSBBmp := nil; finally VSBBmp.Free; end; end; finally Stream.Free; VSBStream.Free; End; end; |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Zitat:
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Na ja, nach dem Hinweis von Sir Rufo war die besagte Textstelle natürlich das Erste, was mir ins Auge gefallen ist. Ich möchte aber auf das "explizit darauf hingewiesen" abheben. Nachdem ich den Sachverhalt kannte, habe ich auch die Textstelle verstanden. Es sollte aber umgekehrt sein!
Wie bestimmt zahllose andere vor mir habe ich mir ein kleines Beispielprogramm (natürlich Divison durch Null) aufgesetzt, um die Sache zu verstehen. Ich denke, bei so ganz alte Hasen wie Sir Rufo und Uwe Raabe sind diese Dinge längst so verinnerlicht, dass sie die Irrwege von nicht so Beschlagenen nicht ganz nachvollziehen können. Natürlich steht das alles da, aber Meister der Didaktik sind weder bei Embarcadero noch bei Microsoft zu finden. Bei msdn werden die Erklärungen der API-Funktionen nicht umsonst regelmäßig von Kommentaren von Anwendern begleitet. Vielleicht fehlen mir die höheren Weihen, aber ich finde die Exception-Behandlung einfach bescheuert. Bei
Delphi-Quellcode:
ist zwar gesichert, dass ein bestimmter Code (Speicherfreigabe wohl in der Regel) ausgeführt wird, aber es knallt nach wie vor, da in Finally keine Exception-Behandlung enthalten ist. Soweit ich das sehe, ist es auch bei dem Codebeispiel von Sir Rufo so, dass das Programm anhält, wenn es knallt, da es ja kein Except gibt (oder???).
Try
... Finally ... End Das Ganze ist ja schon oft in der DP diskutiert worden. Der Standardvorschlag ist doch
Delphi-Quellcode:
Dann aber läuft der Code über das Finally hinaus, weil die Exception ja behandelt ist, und man muss irgendwie hinbekommen, dass Result dann trotzdem FALSE ist. Vermutlich durch ein Exit nach dem Except? Eingängig ist das nicht. Wirklich was für die guten alten Real Programmer.
Try
Try ... Except ... End Finally ... End; Sir Rufo hat aus meiner Function eine Procedure gemacht, was hier nicht in meinem Sinn ist, da ich wissen möchte, ob das Einlesen funktioniert hat. Wenn nicht (weil es noch keine DB gibt, die DB veraltet ist oder aus sonstigen Gründen), werden die Vorschaubilder neu erzeugt. Ansonsten wollte ich noch einiges an Fehlerbehandlung einfügen, aber nach der Kopfsteh-Problematik ging es erstmal um die Machbarkeit. In jedem Fall habe ich wieder was gelernt, danke dafür. |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Eine VCL/FMX Anwendung wird bei einer nicht abgefangenen
Delphi-Quellcode:
NICHT angehalten.
Exception
Es fallen auch keine Kühe oder Häuser um, es wird lediglich eine MessageBox angezeigt, die den Message-Text der
Delphi-Quellcode:
anzeigt.
Exception
Eine
Delphi-Quellcode:
ist nicht böse sondern bewahrt mich davor unnötig viel Code zu schreiben oder irgendetwas auszuführen was gar nicht ausgeführt werden darf.
Exception
Zudem kann man gezielt auf eine
Delphi-Quellcode:
reagieren, wenn man dafür einen Plan B hat. Sonst lässt man die einfach ziehen und somit zur Anzeige bringen.
Exception
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Yess, Sir!
Auch ich finde Exceptions nicht böse! Ich finde sie gut! Sehr gut sogar! Honestly! Ich bin nur der Meinung, das könne man besser machen. Programmiererfreundlicher. Nur als eine kleine Facette: Wie wäre es mit einem "Resume Next"? Und nicht angehalten - na ja, es kommt eine MessageBox für den Anwender, und weiter geht es nicht, bis er was tut. Das ist sehr oft nicht das, was man will. Und die Routine wird sehr wohl verlassen, eine Schleife nicht weitergeführt. Geht in aller Regel auch nicht. Wenn ich hier richtig mitlese, ist die endlose Kärrnerarbeit von zahllosen Try-Einfügungen auch bei Profis äußerst unbeliebt. Das kann man wirklich nicht besser machen? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:22 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