Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi TDictionary Zugriff über property (https://www.delphipraxis.net/214297-tdictionary-zugriff-ueber-property.html)

Kratos 20. Dez 2023 10:43

Delphi-Version: 10.3 Rio

TDictionary Zugriff über property
 
Hallo Leute,

aus Gründen des Betriebsgeheimnisses hab ich vorsichtshalber den gesamten Code anonymisiert. Ich möchte keine Probleme bekommen.


Programmaufbau:
- 1 zentrale Daten-Unit
- mehrere VCL-Units

Die Informationen, die ich brauche und in das Dictionary geschrieben werden sollen, kommen aus einer MSSQL-DB.
Delphi-Quellcode:
unit Data;
...
type
  TTest = class
    SomeText: string;
    ... // weitere Einträge
    SomeBitmap: TBitmap;
  end;

  TAnother = class
  private
    FDD: TDictionary<string,TTest>;
    GetSomeText(AMyKey: string): string;
    GetSomeBitmap(AMyKey: string): TBitmap;
    ...
  public
    property pSomeText[AMyKey: string]: string read GetSomeText;
    property pSomeBitmap[AMyKey: string]: TBitmap read GetSomeBitmap;
  end;


procedure LoadFromDB;
var
  MyKey: string;
  MyStringStream: TStringStream;
  MyBlobStream: TStream;
begin
  ...
  qry.SQL.Text := 'select MyKey, SomeText, Image from table';
  qry.Open();
  ...
  MyKey := qry.FieldByName('MyKey').AsString; // MyKey ist eineindeutig
  ...
  MyStringStream := StringStream.Create;
  TBlobField(qry.FieldByName('SomeText')).SaveToStream(MyStringStream);
  MyStringStream.Position := 0;
  ...
  MyBitmap := TBitmap.Create;
  MyBlobStream := qry.CreateBlobStream(qry.FieldByName('Image'), bmRead);
  MyBlobStream.Position := 0;
  MyBitmap.LoadFromStream(MyBlobStream);
  ...
  SaveToDictionary(MyKey, MyStringStream.ReadString(MyStringStream.Size), MyBitmap);
end;

procedure SaveToDictionary(AMyKey, ASomeText: string; AMyBitmap: TBitmap);
var
  Test: TTest;
begin
  Test := TTest.Create;
  Test.SomeText := ASomeText;
  // Test.SomeBitmap := TBitmap.Create -> keine Veränderung festgestellt, egal ob auskommentiert oder nicht
  Test.SomeBitmap := AMyBitmap;

  FDD.Add(AMyKey, Test);
and;

// funktioniert
function GetSomeText(AMyKey: string): string;
var
  Test: TTest;
begin
  Test := TTest.Create;
  FDD.TryGetValue(AMyKey, Test);
  Result := Test.SomeText;
end;

// funktioniert nicht
function GetSomeBitmap(AMyKey: string): TBitmap;
var
  Test: TTest;
begin
  Test := TTest.Create;
  FDD.TryGetValue(AMyKey, Test);
  Result := Test.SomeBitmap;
end;
Alle Keys des Dictionary erscheinen in einer ListBox. Desweiteren existiert ein TMemo und ein TImage.
Ein Eintrag in der ListBox soll ausgewählt werden. SomeText soll im TMemo und SomeBitmap im TImage angezeigt werden.
Delphi-Quellcode:
unit VCL_1;
...
procedure ListBoxOnClick;
var
  MyBitmap: TBitmap;
begin
  if MyListBox.ItemIndex > -1 then
  begin
    MyMemo.Text := pSomeText[MyListBox.Items[MyListBox.ItemIndex]]; // funktioniert
    ...
    MyBitmap := TBitmap.Create;
    MyBitmap := pSomeBitmap[MyListBox.Items[MyListBox.ItemIndex]]; // funktioniert nicht
  end;
end;
SomeText wird richtig aus dem Dictionary geladen und angezeigt.
SomeBitmap nicht. Im TImage ist nur ein 16x16 Pixel großes schwarzes Viereck zu sehen, egal welche Größe das Bild in der DB hat.
Ich hab Ewigkeiten rumgesucht, auch hier im Forum. Eventuell hab ich auch einfach nicht die richtigen Suchbegriffe gewählt.
ChatGPT hat auch keine hilfreichen Antworten.
Direkt nachdem ich einen Eintrag zum Dictionary hinzugefügt habe, habe ich mir testweise die Abmessungen der Bitmap des letzten Eintrags des Dictionary ausgeben lassen.
Die Höhe und Breite der Bitmap stimmen mit denen in der DB überein.
Erst wenn ich an einer späteren Stelle im Programm über die property das Bitmap holen will, tritt das Fehlverhalten auf und die Abmessungen betragen 16x16.
Vielleicht hab ich einen grundlegenden Denkfehler.
Ich weiß jedenfalls nicht mehr weiter.

Hat eventuell von euch jemand eine Idee?

Gruß
Kratos

Uwe Raabe 20. Dez 2023 11:03

AW: TDictionary Zugriff über property
 
Im ListBoxOnClick wird MyBitmap ein leeres per TMyBitmap.Create zugewiesen, direkt darauf aber durch das Bitmap aus dem Dictionary überschrieben. Damit zeigt MyBitmap direkt auf die Instanz in dem Direktory. Es ist also von entscheidender Bedeutung, was darauf mit MyBitmap geschieht.

Kratos 20. Dez 2023 11:22

AW: TDictionary Zugriff über property
 
Direkt danach soll es im TImage angezeigt werden.
Delphi-Quellcode:
TImage.Picture.Bitmap.Assign(MyBitmap);
Wenn das Bitmap gefüllt ist, dann wird ein schwarzes Viereck im TImage angezeigt, welches 16x16 Pixel groß ist.
Das ist aber nicht das Bild, welches in der DB gespeichert ist.

Anmerkung: Schon im
Delphi-Quellcode:
GetSomeBitmap
kommen die falschen Abmessungen heraus.

War das die Information, die du haben wolltest oder habe ich dich falsch verstanden?

Uwe Raabe 20. Dez 2023 11:32

AW: TDictionary Zugriff über property
 
Was geschieht im weiteren Verlauf mit MyBitmap? Wird es irgendwie manipuliert oder freigegeben?

Es wäre auch interessant zu wissen was in LoadFromDB mit MyBitmap passiert, was uns aktuell noch verborgen wird.

Kratos 20. Dez 2023 12:04

AW: TDictionary Zugriff über property
 
Zitat:

Was geschieht im weiteren Verlauf mit MyBitmap? Wird es irgendwie manipuliert oder freigegeben?
MyBitmap wird ganz zum Schluss in der ListBoxOnclick-Methode freigegeben.

Zitat:

Es wäre auch interessant zu wissen was in LoadFromDB mit MyBitmap passiert, was uns aktuell noch verborgen wird.
NACHDEM in
Delphi-Quellcode:
LoadFromDB
die Methode
Delphi-Quellcode:
SaveToDictionary
aufgerufen wird, wird auch hier MyBitmap freigegeben.

Ich hab jetzt mal BEIDE
Delphi-Quellcode:
MyBitmap.Free
auskommentiert.
Jetzt wird alles korrekt angezeigt!
So ein einfacher Fehler... das kann echt nicht sein.

Vielen Dank, wirklich!!!

[EDIT]: Ich hab
Delphi-Quellcode:
MyBitmap.Free
in
Delphi-Quellcode:
ListBoxOnClick()
mal wieder mit reingenommen.
Es funktioniert weiterhin.

Aber dann hab ich dennoch einen Denkfehler.
Denn ich nahm an, wenn ich eine extra Bitmap in
Delphi-Quellcode:
SaveToDictionary
anlege und diese neue Bitmap befülle und anschließend in das TDictionary wegspeichere, dass ich dann die alte Bitmap aus
Delphi-Quellcode:
LoadFromDB
freigeben darf. Während die neue Bitmap NICHT freigegeben wird.
Das erschließt sich mir gerade nicht.

Uwe Raabe 20. Dez 2023 12:53

AW: TDictionary Zugriff über property
 
TBitmap ist ja eine Klasse und in der TTest-Instanz wird nur ein Zeiger auf die TBitmap-Instanz gespeichert. Ebenso enthält das Directory nur Zeiger auf TTest-Instanzen.

Folgendes ist also problematisch:
Delphi-Quellcode:
function GetSomeText(AMyKey: string): string;
var
  Test: TTest;
begin
  Test := TTest.Create;
  FDD.TryGetValue(AMyKey, Test);
  Result := Test.SomeText;
end;
Das TryGetValue gibt die gefundene TTest-Instanz im Parameter Test zurück. Die vorher erzeugte Instanz verbleibt somit als Speicherleck. Besser wäre:
Delphi-Quellcode:
function GetSomeText(AMyKey: string): string;
var
  Test: TTest;
begin
  Result := ''; // Wert bei not found
  if FDD.TryGetValue(AMyKey, Test) then
    Result := Test.SomeText;
end;
Analog hier:
Delphi-Quellcode:
function GetSomeBitmap(AMyKey: string): TBitmap;
var
  Test: TTest;
begin
  Result := nil; // Wert bei not found
  if FDD.TryGetValue(AMyKey, Test) then
    Result := Test.SomeBitmap;
end;
Und hier wäre eventuell das besser:
Delphi-Quellcode:
procedure ListBoxOnClick;
var
  MyBitmap: TBitmap;
begin
  if MyListBox.ItemIndex > -1 then
  begin
    MyMemo.Text := pSomeText[MyListBox.Items[MyListBox.ItemIndex]];
    ...
    MyBitmap := pSomeBitmap[MyListBox.Items[MyListBox.ItemIndex]];
    Image.Picture.Graphic := MyBitmap;
    // kein MyBitmap.Free, da uns die Instanz hier nicht gehört. Andernfalls klappt es beim zweiten Mal vermutlich nicht mehr.
  end;
end;
Damit die Bitmaps auch am Ende freigegeben werden, sollte in einem
Delphi-Quellcode:
TTest.Destroy
noch ein
Delphi-Quellcode:
SomeBitmap.Free
erfolgen.

Kratos 20. Dez 2023 13:27

AW: TDictionary Zugriff über property
 
@Uwe Raabe

Vielen Dank für deine zusätzlichen Erklärungen und Tipps!


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:01 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