Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Nochmal: Ermiteln ob Objekt freigegeben wurde (https://www.delphipraxis.net/58826-nochmal-ermiteln-ob-objekt-freigegeben-wurde.html)

MaBuSE 13. Dez 2005 11:07


Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Hallo,
ich muß das Thema noch mal aufnehmen, da ich noch keine befriedigende Antwort / Lösung gefunden habe.

Folgendes Problem:

Ich habe ein von TForm abgeleitetes Objekt.
Dieses Objekt wird erzeugt und dargestellt.
Der Benutzer kann das Objekt schliessen und es soll dann auch Freigegeben werden.
Es gibt eine Funktion um das Form anzuzeigen und falls nicht vorhanden neu zu erzeugen.

Beispielcode:

Delphi-Quellcode:
// Unit1.pas
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(Form2) then
  begin
    Form2 := TForm2.Create(self);
    Memo1.Lines.Add(DateTimeToStr(now)+' Create');
  end
  else Memo1.Lines.Add(DateTimeToStr(now)+' schon da');

  Form2.Caption := 'Test';
  Form2.Show;
end;
...

// unit2.pas
...
type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;
...
procedure TForm2.Button1Click(Sender: TObject);
begin
  Close;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // hier werden noch viele weitere Dinge freigegeben
  Action := caFree;
end;
...
Mein Problem ist nun folgendes:
  • Wie kann ich das Formular auf nil setzen, damit Assignes klappt?
  • oder wie kann ich zuverlässig erkennen, ob das Form existiert? (Das Form kann auch unsichtbar sein)
Innerhalb der Form kann (darf) ich ja folgende Dinge nicht machen:
Delphi-Quellcode:
...
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(Self);
  // oder
  Free;
  Self := nil;
end;
...
Hat jemand eine Idee?

Folgende Beiträge habe ich auch schon gelesen, aber keine akzeptable Antwort erhalten:
http://www.delphipraxis.net/internal...ct.php?t=17825
http://www.delphipraxis.net/internal...ct.php?t=15099

Danke für Eure Hilfe

Bernhard Geyer 13. Dez 2005 11:10

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Evtl das Singleton-Pattern anwenden?

dataspider 13. Dez 2005 11:19

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Du musst die Methode Notification überschreiben. Da wirst du über das Freigeben des Formulares informiert:
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FForm2: TForm;
    { Private-Deklarationen }
  protected
    procedure Notification(AComponent : TComponent; Operation : TOperation);
        override;
  public
    constructor Create(AOwner: TComponent); override;
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  FForm2 := nil;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Form erzeugen, wenn nicht vorhanden
  if not Assigned(FForm2) then
  begin
    FForm2 := TForm2.Create(Self);
    // FForm2.FreeNotification(Self);
    FForm2.Show;
  end;
end;

procedure TForm1.Notification(AComponent : TComponent; Operation : TOperation);
begin
  inherited Notification( AComponent, Operation);
  if Assigned(FForm2) then
    if (Operation = opRemove) and (AComponent = FForm2) then
      FForm2 := nil;
end;

end.
Gruß, Frank

Union 13. Dez 2005 11:20

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Im FormClose der Objekte solltest Du folgendes machen:
Delphi-Quellcode:
Procedure MyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   Action := caFree;
end;
Dann genügt es, später die globale Variable auf nil abzufragen:
Delphi-Quellcode:
if frmMyForm = nil then
   frmMyForm := TMyForm.Create(Application);
frmMyForm.Show;

tomsel 13. Dez 2005 11:26

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
oder ganz einfach so:

Delphi-Quellcode:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // hier werden noch viele weitere Dinge freigegeben
  Action := caFree;
  Form2 := nil;
end;
Falls Form2 modal dargestellt wird, besser nach Schema
Create
Showmodal
Free
vorgehen

MarcoWarm 13. Dez 2005 11:39

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Eine Idee, wie es auf alle Fälle immer klappt, ist, einfach die Caption (z.B.) des Formulars zu ermitteln ... und das in einem try except block. Falls es schief geht, gibt es kein Objekt mehr. Falls es funktioniert ist dein Objekt noch da.

ist aber, zugegeben, nicht sehr elegeant.... aber funzt

MaBuSE 13. Dez 2005 12:16

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

Zitat von tomsel
oder ganz einfach so:
Delphi-Quellcode:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // hier werden noch viele weitere Dinge freigegeben
  Action := caFree;
  Form2 := nil;
end;

Funktioniert nicht.
Bekomme dann bei manchen Aktionen allgemeine Schutzverletzungen. :-(

Form ist nicht Modal.

MaBuSE 13. Dez 2005 12:46

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

Zitat von Union
Im FormClose der Objekte solltest Du folgendes machen:
Delphi-Quellcode:
Procedure MyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   Action := caFree;
end;
Dann genügt es, später die globale Variable auf nil abzufragen:
Delphi-Quellcode:
if frmMyForm = nil then
   frmMyForm := TMyForm.Create(Application);
frmMyForm.Show;

Das bringt nichts.
Das hatte ich ja auch schon im 1. Beitrag geschrieben.
  • In TCustomForm.Close wird falls Assigned das Ereignis onClose aufgerufen.
  • In OnClose wird die CloseAction auf caFree gesetzt.
  • Es wird dann im else Zweig der If Abfrage das Release aufgerufen.
  • Release schickt eine Nachicht CM_RELEASE an das Form
  • Die Methode CMRelease wird dadurch aufgerufen.
  • in CMRelease wird ein Free aufgerufen.
  • Das Ergebnis ist ein freigegebenes Formular
    Leider ist das aber nicht nil
    Ein "if frmMyForm = nil then" bringt genauso wie ein "if not assigned(frmMyForm) then" False und ich kann nicht erkennen ob dort ein existierendes Formular existiert.
Die isObject Funktion von choose ( http://www.delphipraxis.net/internal...=460126#460126 ), die in Zusammenarbeit mit negaH entstand bringt eine Exception.

Hier ein Auszug des Quelltextes der TCustomForm zum Nachvollziehen:

Delphi-Quellcode:
{*******************************************************}
{       Borland Delphi Visual Component Library        }
{  Copyright (c) 1995-2002 Borland Software Corporation }
{*******************************************************}
unit Forms;
...
type
  TCustomForm = class(TScrollingWinControl)
  private
...
    procedure CMRelease(var Message: TMessage); message CM_RELEASE;
...
  protected
...
    procedure DoClose(var Action: TCloseAction); dynamic;
...
  public
...
    procedure Close;
...
    procedure Release;
...
  end;
...
procedure TCustomForm.CMRelease;
begin
  Free;
end;
...
procedure TCustomForm.DoClose(var Action: TCloseAction);
begin
  if Assigned(FOnClose) then FOnClose(Self, Action);
end;
...
procedure TCustomForm.Close;
begin
...
      DoClose(CloseAction);
      if CloseAction <> caNone then
        if Application.MainForm = Self then Application.Terminate
        else if CloseAction = caHide then Hide
        else if CloseAction = caMinimize then WindowState := wsMinimized
        else Release;
...
end;
...
procedure TCustomForm.Release;
begin
  PostMessage(Handle, CM_RELEASE, 0, 0);
end;
...
[edit] Hatte Hagen statt negaH geschrieben[/edit]

dataspider 13. Dez 2005 12:51

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Hi MaBuSE,
was stört dich daran, die Methode Notification zu verwenden.
Die ist dafür da und wird vor allem in der Komponentenentwicklung für solche Fälle benutzt.
Cu, Frank

MaBuSE 13. Dez 2005 12:54

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

Zitat von dataspider
was stört dich daran, die Methode Notification zu verwenden.
Die ist dafür da und wird vor allem in der Komponentenentwicklung für solche Fälle benutzt.

Nichts ;-)
Ich bin gerade am ausprobieren und debuggen.
Ich wollte erst was dazu schreiben, wenn ich mir das angeschaut habe :stupid:
Aber Danke schon mal für die Antwort :thumb:

stoxx 13. Dez 2005 13:19

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

Zitat von dataspider
Hi MaBuSE,
was stört dich daran, die Methode Notification zu verwenden.
Die ist dafür da und wird vor allem in der Komponentenentwicklung für solche Fälle benutzt.
Cu, Frank

mich hat daran gestört, dass diese Methode nur einem Object zugewiesen werden kann.
Ich hab mir dazu mal einen "Objectbroker" gebastelt, der die ganze arbeit übernimmt.
Dort meldet sich ein Object an oder ab. alle Zeiger auf objekte werden da verwaltet.
Ein anderes Object fragt jetzt nicht mit assigned(Referenz) die Gültigkeit ab, sondern fragt den Objectbroker, ob objekt noch gültig ist.
Zusätzlich kann man dem Objektbroker noch sagen, von welchem Objekt man die Abmeldenachricht in einem Event bekommen will ( um die eigene Referenz auf nil zu setzen).
(Observer)
Der Objektbroker löscht sich auch gegenseitig korrekt die Referenzen, da ein Objekt ja aus der Benachrichtigungsliste entfernt werden muss, wenn es gelöscht wird.

bigg 13. Dez 2005 13:29

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Hi Mabuse,

es geht dir sicherlich um den Zeiger des Formulars, richtig?

Legst du dir zusätzlich eine Zeiger-Variable an?
Oder wird das Formular automatisch durch die Projektdatei erzeugt und besitzt "keine" Variable auf die du direkt zurückgreifen kannst?

Hast du die Zeiger-Variable auch global definiert?

tomsel 13. Dez 2005 13:46

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

tomsel hat folgendes geschrieben:
oder ganz einfach so:

Delphi-Quellcode:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // hier werden noch viele weitere Dinge freigegeben
  Action := caFree;
  Form2 := nil;
end;
Funktioniert nicht.
Bekomme dann bei manchen Aktionen allgemeine Schutzverletzungen.
Dann verwendest Du die Variable Form2 irgendwo, ohne sie auf nil zu überprüfen. Der Fall ist nicht so kompliziert, wie Du ihn offensichtlich machst. Setze Form2 in FormCLose auf NIL und baue vor jeder Verwendung von Form2 eine Prüfung ein.

MaBuSE 13. Dez 2005 13:56

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

Zitat von tomsel
Dann verwendest Du die Variable Form2 irgendwo, ohne sie auf nil zu überprüfen. Der Fall ist nicht so kompliziert, wie Du ihn offensichtlich machst. Setze Form2 in FormCLose auf NIL und baue vor jeder Verwendung von Form2 eine Prüfung ein.

Ich werd mal ein wenig debuggen, ob das funktioniert.
Das Programm ist nicht von mir. Es ist relativ komplex.
Es werden mehrere 100 Forms verwendet, die von TBasisForm (Forms) oder TBasisClientForm (Frames) abgeleitet sind, in TBasisForm und TBasisClientForm ist ein eigenes "Festerverwaltungssystem" implementiert.
Dort wird noch einiges gemacht. Ich muß da erst mal durchsteigen. Mir ist nur aufgefallen, das teilweise GPF auftreten, da obiges Problem besteht.

Das Problem muß ich nun zuerst lösen. (Anforderung der Anwender)

Danke für Eure Anregungen, ich werde nun mal einen Weile debuggen und dann berichten wie ich es gelöst habe.

mfg
MaBuSE

shmia 13. Dez 2005 14:50

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Du darfst die Variable Form2 nicht verwenden!!!
Empfehlung: globale Variable Form2 löschen.

Delphi-Quellcode:
function FindOrCreateForm(FormClass: TFormClass; var Reference):Boolean;
var
   i : Integer;
begin
   for i := Screen.CustomFormCount-1 downto 0 do
   begin
      if Screen.CustomForms[i].ClassType = FormClass then
      begin
         TCustomForm(Reference) := Screen.CustomForms[i];
         Result := True; // gefunden
         Exit;
      end;
   end;
   Application.CreateForm(FormClass, Reference);
   Result := False; // neu erzeugt
end;


procedure TForm1.Button1Click(Sender: TObject);
var
   frm : TForm2;
begin
  if FindOrCreateForm(TForm2, frm) then
  begin
    Memo1.Lines.Add(DateTimeToStr(now)+' Create');
  end
  else
  begin
    Memo1.Lines.Add(DateTimeToStr(now)+' schon da');
  end;

  Frm.Caption := 'Test';
  Frm.Show;
  Frm.WindowState := wsNormal; // falls minimiert -> sichtbar machen
end;

MaBuSE 13. Dez 2005 16:10

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

Zitat von shmia
Du darfst die Variable Form2 nicht verwenden!!!
Empfehlung: globale Variable Form2 löschen.

Warum darf ich die nicht verwenden?
Aber keine Angst, sie wird im Projekt nicht verwendet.

Ich habe aber ein anderes Problem entdeckt.

Einige Forms werden mehrfach (in versch. Variablen) erzeugt.

Bsp:
Delphi-Quellcode:
...
// in unit1.pas
procedure TForm1.methode;
begin
  if not assigned(privateForm1Var) then
  begin
    privateForm1Var := TForm2.Create(self);
  end;
  privateForm1Var.show
end;
// in unit3.pas
procedure TForm3.andereMethode;
begin
  if not assigned(privateForm3Var) then
  begin
    privateForm3Var := TForm2.Create(self);
  end;
  privateForm3Var.show
end;
...
In dem Form2 habe ich keinen Zugriff auf privateForm1Var.
1. ist die Unit1 und Unit3 nicht in der uses des Form1 und
2. ist die Variable privat (lokal)
Außerdem weis ich ja gar nicht in welcher Variable das TForm2 enthalten ist.
Damit es nicht langweilig wird, sind manchmal auch mehrere Instanzen gleichzeitig aktiv.
(also privateForm1Var <> privateForm3Var und beide <> nil)
Ich kann also folgenden SourceCode nicht ausführen:
Delphi-Quellcode:
...
// Vorschlag von [user]tomsel[/user]
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // hier werden noch viele weitere Dinge freigegeben
  Action := caFree;
  privateForm1Var := nil;
end;
...
Folgendes funktioniert auch nicht, da dann zwar self auf nil gesetzt wird, aber privateForm1Var immer noch auf den "alten" Speicherbereich zeigt.
Delphi-Quellcode:
...
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // hier werden noch viele weitere Dinge freigegeben
  Action := caFree;
  self := nil;
end;
...
Der Lösungsansatz von shmia gefällt mir prinzipiell gut, aber es kann nur ein Form pro Typ erzeugt werden. Das Form muss aber auch mehrmals "sichtbar" sein.

Die Idee von Bernhard Geyer das Singleton-Pattern anwenden, fällt leider auch weg, da es ja mehrfach erzeugt wird

Der Lösungsansatz von dataspider scheint wohl der Beste zu sein.
Das Problem dort ist allerdings, dass die lokale Variable im Form definiert sein muss und das bei jedem Objekt das eine Instanz erzeugt die Notification abgeleitet werden muss.

Ich werde mit wohl eine FormFactory bauen müssen, die mir die Forms erzeugt und intern in einer Liste abspeichert. Diese FormFactory muß dann auch das Notification implementieren und dann die entsprechenden Speicherbereiche auf Nil setzen.

Mal schauen ob das funktioniert.

Danke an alle für die Anregungen

MaBuSE 16. Dez 2005 08:36

Lösung: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Hallo,
ich habe eine einfache Lösung für mein Problem gefunden.

Man muß dem Objekt nur bekannt machen welche Variable es referenziert.
Diese kann man dann in onClose auf nil setzen.

Man muß nur darauf achten, das man sich keine Kopie der Instanzvariablen, sondern die Adresse selber merkt. (Pointer auf Form2 vom Typ ^TForm2)
Diesem Pointer weist man dann die Adresse zu := @Form2.

Das war's. Relativ einfach, oder?

ps: Vielen Dank an NicoDE, es war eigentlich seine Idee :thumb:

Unit1 mit dem MainForm:
Delphi-Quellcode:
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not assigned(Form2) then
  begin
    Form2 := TForm2.Create(self);
    Form2.MyInstance := @Form2;
  end;
  form2.show;
end;
...
Und hier ist die Unit2 mit dem SubForm:
Delphi-Quellcode:
...
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    MyInstance: ^TForm2;
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  MyInstance^ := nil;
end;

end.
...

NicoDE 16. Dez 2005 10:31

Re: Lösung: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

Zitat von MaBuSE
Das war's. Relativ einfach, oder?

Ein Hinweis...
Für die konkrete Problemstellung war es die schnellste (und 'dreckigste' ;)) Lösung mit möglichst wenig Änderungen am bestehenden Projekt. Kommt bei neuen Projekten bitte nicht auf die Idee diesen Lösungsweg als Vorlage zu verwenden.

tomsel 16. Dez 2005 10:40

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Begründung?????

NicoDE 16. Dez 2005 10:45

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Zitat:

Zitat von tomsel
Begründung?

Zum Beispiel:
Zitat:

Zitat von MaBuSE
Delphi-Quellcode:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  MyInstance^ := nil;
end;

...man muss sicherstellen, dass der Zeiger noch gültig ist. In dem Projekt ist dies sichergestellt, insofern kann man sich hier darauf verlassen...

jim_raynor 16. Dez 2005 10:47

Re: Nochmal: Ermiteln ob Objekt freigegeben wurde
 
Was machst du zum Beispiel, wenn du mehrere Referenzen auf das gleiche Objekt hast ;) Da hast ein kleines Problem mit nur einer Instanz Variable. Richtiger ist, über Events zu arbeiten. Ich habs so gelöst, dass ich mir eine Klasse gebastelt habe, mit der ich sehr einfach Events verwalten kann. So können dann auch mehrere Funktionen aufgeraufen werden, wenn ein Objekt zerstört wird.

Jeder der ne Referenz anlegt registriert sich auf das OnDestroy-Event und räumt dann auf, wenn es aufgerufen wird :) Das verwende ich natürlich nicht nur für OnDestroy sondern mittlerweile für alle Events.

Falls interesse besteht, kann ich ja mal meine Klasse TNotifyList zur Verfügung stellen.


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