FormDestroy - wenn wird die GUI zerstört?

Rund um die LCL und andere Komponenten
Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

FormDestroy - wenn wird die GUI zerstört?

Beitrag von Timm Thaler »

Ich möchte beim Schließen eines Programms die Inhalte einiger Objekte der GUI in ein XML speichern. Dazu wollte ich eine Procedure SaveParams aus FormDestroy aufrufen.

Allerdings musste ich feststellen, dass beim Aufruf die Objekte schon nicht mehr vorhanden sind. Ich dachte, die werden erst am Ende zerstört, nachdem ich meinen Kram abgearbeitet habe.

Rufe ich die Procedure SaveParams über einen Button auf, funktioniert das Abspeichern.

Wie macht das TXMLPropStorage, das wird doch auch erst beim Schließen aufgerufen?

Da ich erstens die Objekte im Programm erst erstelle und zweitens das XML anders formatieren möchte, kann ich TXMLPropStorage hier nicht verwenden.

Code: Alles auswählen

// Programm beenden => wirft eine Exception wegen nicht mehr vorhandener Objekte
 
procedure Tgui.FormDestroy(Sender: TObject);
begin
  gui.SaveParams();
end;
 
// Einstellungen speichern => geht
 
procedure Tgui.g_btnsaveClick(Sender: TObject);
begin
  gui.SaveParams();
end;
 

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von mse »

Man kann auch den Destructor überschreiben:

Code: Alles auswählen

 
 tgui = class(tform)
[...]
  protected
   procedure saveparams();
  public
   destructor destroy(); override;
[...]
 end;
implementation
[...]
destructor tgui.destroy();
begin
 saveparams();
 inherited;
end;
[...]
 

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von Timm Thaler »

Aber prinzipiell ist es so, dass erst die GUI zerstört wird und dann Form.Destroy aufgerufen wird?

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von mse »

Nein. TForm.destroy() gibt die Komponenten frei. Oder meinst du TForm.OnDestroy?

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von Timm Thaler »

Ähm ja, das war unkorrekt: Das Ereignis "OnDestroy" ruft die Prozedur "procedure Tgui.FormDestroy(Sender: TObject);" auf. In dieser wollte ich die Einträge in der GUI abspeichern, aber da sind die Objekte anscheinend schon zerstört.

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von Mathias »

Anstelle von FormDestroy, könnte man auch FormClose nehmen.
Ich denke, dies wäre der einfachste Weg.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von Mathias »

Ich habe mal schnell eine Versuch mit einer Ini-Datei gemacht.
Dieser Test-Code funktioniert auch mit FormDestroy.

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
var
  iniFile: TIniFile;
begin
  iniFile := TIniFile.Create(IniDatei);
  Width := IniFile.ReadInteger('Form', 'Width', 200);
  Height := IniFile.ReadInteger('Form', 'Height', 200);
  iniFile.Free;
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
var
  iniFile: TIniFile;
begin
  iniFile := TIniFile.Create(IniDatei);
  IniFile.WriteInteger('Form', 'Width', Width);
  IniFile.WriteInteger('Form', 'Height', Height);
  iniFile.Free;
end
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von mse »

Timm Thaler hat geschrieben:Ähm ja, das war unkorrekt: Das Ereignis "OnDestroy" ruft die Prozedur "procedure Tgui.FormDestroy(Sender: TObject);" auf. In dieser wollte ich die Einträge in der GUI abspeichern, aber da sind die Objekte anscheinend schon zerstört.

Das sollte nicht so sein. OnDestroy wird in dodestroy() <- beforedestruction() aufgerufen.

Code: Alles auswählen

 
procedure TCustomForm.DoDestroy;
begin
  try
    if Assigned(FOnDestroy) then FOnDestroy(Self);
  except
    if not HandleDestroyException then
      raise;
  end;
end;
 
procedure TCustomForm.BeforeDestruction;
begin
  // set csDestroying
  inherited BeforeDestruction;
  //debugln(['TCustomForm.BeforeDestruction ',DbgSName(Self),' ',csDestroying in ComponentState]);
  // EndWrite will happen in the destructor
  GlobalNameSpace.BeginWrite;
  Screen.FSaveFocusedList.Remove(Self);
  RemoveFixupReferences(Self, '');
  if FormStyle <> fsMDIChild then Hide;
  DoDestroy;
  // don't call the inherited method because it calls Destroying which is already called
end;
 

BeforeDestruction() wird vor destroy() aufgerufen:
http://www.freepascal.org/docs-html/cur ... bject.html
procedure BeforeDestruction; virtual; Method called before the destructor is called.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von Timm Thaler »

Mathias hat geschrieben:Ich habe mal schnell eine Versuch mit einer Ini-Datei gemacht.
Dieser Test-Code funktioniert auch mit FormDestroy.


Haha! Ja, für das Formular. Aber nicht für die Objekte im Formular.

Code: Alles auswählen

      parentnode := prefs.CreateElement('input');
        TDOMElement(parentnode).SetAttribute('nr', IntToStr(i));
        TDOMElement(parentnode).SetAttribute('testx', IntToStr(gui.Width)); <= hab ich mal testweise eingefügt
        TDOMElement(parentnode).SetAttribute('testy', IntToStr(gui.Height));
        TDOMElement(parentnode).SetAttribute('name', gui.g_lab_in[i].Caption); <= kommt genau bis hierhin
        TDOMElement(parentnode).SetAttribute('timer', gui.g_tim_in[i].Text);
 


Obiger Ausschnitt aufgerufen aus FormDestroy liest gui.Width und gui.Height noch aus, bei gui.objekt.Caption kommt eine Fehlermeldung "hat Exception Klasse SIGSEGV ausgelöst", und zwar in control.inc:

Code: Alles auswählen

function TControl.GetText: TCaption;
var
  len: Integer;
  GetTextMethod: TMethod;
begin
  // Check if GetTextBuf is overridden, otherwise we can call RealGetText directly
  Assert(Assigned(@Self.GetTextBuf), 'TControl.GetText: GetTextBuf Method is Nil');
  GetTextMethod := TMethod(@Self.GetTextBuf); <= da ist das Objekt anscheinend schon weg
 


Mathias hat geschrieben:Anstelle von FormDestroy, könnte man auch FormClose nehmen.


Ja, das wiederum funktioniert.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von mse »

Was ist "gui.g_lab_in"?

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von Timm Thaler »

mse hat geschrieben:Was ist "gui.g_lab_in"?


Das ist mein Array mit den erstellten Objekten.

Timm Thaler hat geschrieben:Da ich erstens die Objekte im Programm erst erstelle


Code: Alles auswählen

  private
    g_lab_in : array[1..5] of TLabel;
...
procedure Tgui.FormCreate(Sender: TObject);
...
  for i := 1 to 5 do
  begin
    g_lab_in[i] := TLabel.Create(Application);
    with g_lab_in[i] do
    begin
      Left := 40; Top := yo + i * cdys; Width := 80; Height := 20;
      Parent := gui;
      Caption := 'IN ' + IntToStr(i);
    end;
...
 


Ich habe schon vermutet, dass vielleicht das Array schon eher nicht mehr verfügbar ist. Sollte doch aber auch erst am Ende freigegeben werden, oder?

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von mse »

Timm Thaler hat geschrieben:

Code: Alles auswählen

 
    g_lab_in[i] := TLabel.Create(Application);
 


Dadurch werden deine labels in "Application.destroy()" freigegeben. Probiere

Code: Alles auswählen

 
    g_lab_in[i] := TLabel.Create(self);
 

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von Timm Thaler »

mse hat geschrieben:Dadurch werden deine labels in "Application.destroy()" freigegeben.


Das hatte ich auch schon in Verdacht. Also wird Application.destroy vor Form.destroy aufgerufen?

Wo erfährt man sowas? Wenn ich das richtig verstanden habe, läuft das so ab:

Click auf Schließen => OnClose-Ereignis => Application.destroy => OnDestroy-Ereignis => Form.destroy => Formular wird freigegeben.

Wann erfolgen den Speicherfreigaben für Array, Variablen?

Von früher kannte ich das noch so: Mit dem END. Aber das scheint inzwischen etwas komplizierter zu sein.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von mse »

Timm Thaler hat geschrieben:
mse hat geschrieben:Dadurch werden deine labels in "Application.destroy()" freigegeben.


Das hatte ich auch schon in Verdacht. Also wird Application.destroy vor Form.destroy aufgerufen?

Scheint so. Vermutlich werden die Forms durch application.destroy() freigegeben, auf jeden Fall ist das bei MSEgui so.
Wo erfährt man sowas? Wenn ich das richtig verstanden habe, läuft das so ab:

Click auf Schließen => OnClose-Ereignis => Application.destroy => OnDestroy-Ereignis => Form.destroy => Formular wird freigegeben.

Wenn du in tform.destroy() auf Komponenten zugreifen möchtest, sollte deren Freigabe durch das Formular und nicht durch irgend etwas Anderes vorgenommen werden.
Wann erfolgen den Speicherfreigaben für Array, Variablen?

In deinem Fall ist "gui.g_lab_in" ein Bestandteil des Instanz-Blocks von TGui und wird in tobject.freeinstance() freigegeben:

Code: Alles auswählen

 
      procedure TObject.FreeInstance;
 
        begin
           CleanupInstance;
           FreeMem(Pointer(Self)); <<<------------hier
        end;
 

http://www.freepascal.org/docs-html/cur ... tance.html
Freeinstance wird nach destroy() aufgerufen.

Mathias
Beiträge: 6194
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: FormDestroy - wenn wird die GUI zerstört?

Beitrag von Mathias »

@Timm Thaler
Was ist der Grund, das du die XML-Speicherung in Destroy, anstelle von Close machen willst ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten