Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi GDI+: IStream oder TStreamAdapter (https://www.delphipraxis.net/169902-gdi-istream-oder-tstreamadapter.html)

ken_jones 20. Aug 2012 17:04

GDI+: IStream oder TStreamAdapter
 
Ich hatte bis eben gerade ein Problem, es nun gelöst, aber nicht ganz begriffen wieso es jetzt geht. Kann mir einer von euch Cracks eine Hinweis geben?

Meine Applikation verwendet GDI+ (unter DXE2,Win7) und versucht einen JPG-Stream in eine GPBitmap zu laden.

Folgender Code (wie er überall im Netz zu finden ist und funktionieren sollte) funktioniert bei mir nicht:

Delphi-Quellcode:
procedure TfrmMain.TestProc(Jpegdata: TMemoryStream);
var
  Image   : IGPBitmap;
  Graphics : IGPGraphics;
  SA      : TStreamAdapter;
begin
  Graphics := TGPGraphics.Create(imgVorschau.Canvas.Handle);
  try
    sa := TStreamAdapter.Create(JpegData);
    Image := TGPBitmap.Create(sa);
    Graphics.DrawImage(Image, 0, 0, Image.Width, Image.Height);
  except
    ...
  end;
end;
Folgender Code schon, aber wieso?

Delphi-Quellcode:
procedure TfrmMain.TestProc(Jpegdata: TMemoryStream);
var
  Image   : IGPBitmap;
  Graphics : IGPGraphics;
  SA      : IStream;
begin
  Graphics := TGPGraphics.Create(imgVorschau.Canvas.Handle);
  try
    sa := TStreamAdapter.Create(JpegData);
    Image := TGPBitmap.Create(sa);
    Graphics.DrawImage(Image, 0, 0, Image.Width, Image.Height);
  except
    ...
  end;
end;
Einzig hab ich den SA von TStreamAdapter auf IStream geändert, aber ich erzeuge nach wie vor den TStreamAdapter auf diese Variable.
Ist das Zufall, dass das jetzt bei mir läuft (und dann früher oder später doch wieder crashed) oder geht das so in Ordnung?

Danke für Hints!

shmia 20. Aug 2012 18:01

AW: GDI+: IStream oder TStreamAdapter
 
Die 1. Variante ist falsch und die 2. Variante ist richtig.
Der kommentierte Code macht es vielleicht verständlich:
Delphi-Quellcode:
var
  SA : TStreamAdapter;
...
    // hier wird ein Objekt vom Typ TStreamAdapter erzeugt
    sa := TStreamAdapter.Create(JpegData);

    // beim Aufruf wird im Hintergrund aus dem Objekt ein IStream-Interface erzeugt
    // und dessen Referenzzähler um 1 hochgezählt
    Image := TGPBitmap.Create(sa);
    // nach dem Aufruf wird der Referenzzähler um 1 runtergezählt
    // da nun aber der RefCount = 0 ist wird Free & Destroy aufgerufen

    // zu diesem Zeitpunkt ist der StreamAdapter schon tot, obwohl er eigentlich jetzt gebraucht würde
    Graphics.DrawImage(Image, 0, 0, Image.Width, Image.Height);
Rein theoretisch sollte Delphi merken, dass das übergebene IStream-Interface innerhalb von TGPBitmap noch benützt wird.
Dazu müsste die Klasse TGPBitmap intern aber _AddRef() aufrufen, tut es anscheinend aber nicht.

ken_jones 20. Aug 2012 19:11

AW: GDI+: IStream oder TStreamAdapter
 
Vielen herzlichen Dank für die schnelle Antwort!
Das ist eine sehr interessante Erklärung für dieses Phänomen. Ich werde mich bei nächster Gelegenheit mal dahinterklemmen und das Untersuchen.
Aber dein Tipp ist Gold wert! Danke!

Gruss,
ken

himitsu 20. Aug 2012 20:07

AW: GDI+: IStream oder TStreamAdapter
 
Objektreferenzen sind nicht mir einer Referenzzählung versehen ... niemals.
Also wird Delphi da auch niemals _AddRef aufrufen.

Bei Übergabe an den Constructor, als Interface, wird bei Aufruf des Constructor eine Interface-Referenz erstellt und demnach auch _AddRef aufgerufen.
Bei Austritt aus dem Custructor wird diese Referenz nicht mehr benötigt und _Release aufgerufen, womit die Referenzählung runterzählt, auf 0 kommt und das Interface/Objekt freigibt.

Antwort: Kombiniere niemals Objektreferenzen mit Interfacereferenzen. (nicht ohne die Referenzzählung für diesen Fall außer Kraft zu setzen),
denn Objektreferenzen werden immer über Free/Destroy freigegeben und Interfaces geben sich selber frei (durch die Referenzzählung ausgelöst) und des darf niemals mehrere "Owner" geben (wenn diese nicht alle voneinander wissen und sich gegenseitig Benachrichtigen, wenn einer alles auflöst, bzw. nicht ohne die anderen Owner irgendwie über die zusätzlichen Referenzen in den Ownern zu informieren).

In diesem Fall wird ein Interface (IStream) benötigt, also sollte auch ausschließlich nur mit Interface-Referenzen gearbeitet werden.

Bummi 21. Aug 2012 00:02

AW: GDI+: IStream oder TStreamAdapter
 
Ich hatte das seinerzeit weggekapselt um "es von der Backe zu haben"
Delphi-Quellcode:
    TGPImageWrapper=Class(TObject)
       private
       FImage: TGPImage;
       FStream: TMemoryStream;
       public
       Constructor Create(AGraphic:TGraphic);overload;
       Constructor Create(FileName: String);overload;
       Destructor Destroy;override;
       Public
       Property Image:TGPImage read FImage;
..........
.........


constructor TGPImageWrapper.Create(AGraphic: TGraphic);
begin
  inherited Create;
  FStream := TMemoryStream.Create;
  AGraphic.SaveToStream(FStream);
  Fimage:= TGPImage.Create(TStreamAdapter.Create(FStream));
end;

constructor TGPImageWrapper.Create(FileName: String);
begin
  inherited Create;
  Fimage:= TGPImage.Create(FileName);
end;

destructor TGPImageWrapper.Destroy;
begin
  FImage.Free;
  FStream.Free;
  inherited;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:07 Uhr.

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