-1

I created a simple application with a button on a form Tform1 which is used to create another form Tform2. Form2 contains a toolbar with 2 buttons at each corner and a label with a tabcontrol below it. I keep getting this memory leak below. Im sure i created and destroyed the forms correctly.

Screenshot of error

program Project1;

uses
  System.StartUpCopy,
  FMX.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
   ReportMemoryLeaksOnShutdown := True;
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

here is the form1 that creates the form2

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, Unit2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
begin
        Hide;
        Application.CreateForm(TForm2, Form2);
        Form2.Show;
        Form2.WindowState := TWindowState.wsMaximized;
end;

end.

Form 2 with the toolbar and tabcontrol

Form2 layout pic

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.TabControl, FMX.Controls.Presentation;

type
  TForm2 = class(TForm)
    ToolBar1: TToolBar;
    Button1: TButton;
    TabControl1: TTabControl;
    TabItem1: TTabItem;
    TabItem2: TTabItem;
    Label1: TLabel;
    Button2: TButton;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := TCloseAction.caFree;
  // ShowMessage('Form Freed now closing whole application') ;
  Application.MainForm.Close;
end;

end.
Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54
AceWan
  • 11
  • 3
  • 1
    Use full fast MM version to get stack traces relating to allocation. However it looks like you close the main form twice. – David Heffernan Mar 07 '16 at 07:45
  • @DavidHeffernan i just hid the first form after creating the second form2 and then close the mainform from form2 using Application.MainForm.Close wondering where i close mainform twice – AceWan Mar 07 '16 at 07:58
  • When you close the main form, the application is destroyed. That then destroys all the objects it owns. Including Form2. Which closes the main form. Again. – David Heffernan Mar 07 '16 at 08:01
  • so basically im freeing the form then closing the main form application which tries again to free the form i just freed. Does this apply to forms at runtime? – AceWan Mar 07 '16 at 08:06
  • FWIW, I can't reproduce the memoty leak. Win7 and XE7 though. – Tom Brunberg Mar 07 '16 at 08:07
  • That's what it looks like to me. I don't know what you mean about runtime. When else are leaks an issue? – David Heffernan Mar 07 '16 at 08:09
  • The leak only occurs after i interact with the interface and let it sit for couple minutes. Im wonder if it has anything to do with the style buttons – AceWan Mar 07 '16 at 08:15
  • I just want to know if im using the correct method to close the whole application from another form. If anyone got a better method than what i did above please post. I cant believe im getting worked up over something so simple. @TomBrunberg i might downgrade again if it works in XE7 coming from XE10 with same issue – AceWan Mar 07 '16 at 08:24
  • 1
    I don't think it is a matter of Delphi version, I tried with XE8 and no memory leaks. What do you mean with "interact with the interface"? There's very little to interact with in the UI. Do you have exactly the same code as you have presented here? I tend to design with a simple rule: "Whatever creates, also destroys", so I would not kill the app from a secondary form. That doesn't seem to be your problem though. – Tom Brunberg Mar 07 '16 at 08:36
  • 1
    If we cannot reproduce this, how can we help. Perhaps you need to work on creating a [mcve]. – David Heffernan Mar 07 '16 at 08:56
  • 3
    `189-204 bytes: TApplication x 1` this mean that process terminated abnormally – kami Mar 07 '16 at 10:02
  • Closing the main Form does not destroy the Application object. It signals the main message loop to exit. The Application object is destroyed during app cleanup. Setting `Action:=caFree` in an `OnClose` event does not destroy the calling Form right away, a message is posted to the Form's window after the handler exits to have the Form destroy itself when the message is processed. So, if you `caFree` a Form and then close the main form immediately, the main message loop will exit before the Form destroys itself, so the Form's Owner, if any, will be responsible for destroying it. – Remy Lebeau Mar 07 '16 at 16:22
  • @RemyLebeau Would application.terminate be better. Whats the proper way to exit the application completely? – AceWan Mar 07 '16 at 17:07
  • @AceWan: `Application.Terminate()` simply posts a `WM_QUIT` message to the main message loop (after verifying that it is OK to do so). That is what the main form calls when it is closed. – Remy Lebeau Mar 07 '16 at 19:14

1 Answers1

1

As I said in the comments, I can not reproduce the memory leak reports with the code you have shown. But maybe you are chasing the wrong goose, so I'll take a chance with some guesswork.

You never explained why you are treating Form1 the way you are, but I got this strange feeling that it may be a login form. If that is the case I would do it as follows:

Project1.dpr

program Project1;

uses
  System.StartUpCopy,
  FMX.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2},
  System.UITypes;  // for the modal result

{$R *.res}

begin
  ReportMemoryLeaksOnShutdown := True;
  Application.Initialize;

  Form1 := TForm1.Create(Application);
  try
  if Form1.ShowModal <> System.UITypes.mrOK then
    Exit;
  finally
    Form1.Free;
  end;

  Application.CreateForm(TForm2, Form2);
  Application.Run;
end.

Note that Form1 is not created with Application.CreateForm. The reason is that the first form created with Application.CreateForm becomes the main form of the application and closing it would close the application. Instead we let Form2, where the main UI is, become the main form so we can free Form1 which is not needed after login.

Now we also need to set the modal result of the login form (Form1), for example (but you will want to make it more secure):

procedure TForm1.Button1Click(Sender: TObject);
begin
  if (Edit1.Text = 'User') and (Edit2.Text = 'pass') then
    modalresult := System.UITypes.mrOK;
end;

Let me know if this is not at all what you are looking for and I'll delete.

Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54
  • Thank you =). You were right on the mark i had a login form that utilizes a database connection through unidac. That method above work treating the login form as a modal form. I don't get that error anymore. – AceWan Mar 07 '16 at 23:25
  • @kami No blinking cursor in the `Tedit`s. I tested your suggestion (in one of linked discussions) of reassigning `Application.Mainform` and it worked ok. So the alternative would be to create both forms normally (with loginform first), if login succeeds, change main form and release the login form. Funny though that the docs say "MainForm cannot be modified at run time (it is read-only at run time)". Seems to be a doc error. – Tom Brunberg Mar 08 '16 at 10:50
  • @TomBrunberg, Thank you! I just want to know "original" behavior on XE7/8 – kami Mar 08 '16 at 15:41