2
votes

I am working on Delphi 7. I am getting access violation error while freeing the form.

1) Create new Application Delphi 7 (Unit1)

2) Add new form (Unit2)

3) For Unit1 uses clause add Unit2 and write below code

unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  form2 : TForm2;
begin
  try
    form2 := TForm2.Create(Application);
    //form2.ShowModal;
  finally
    //FreeAndNil(form2);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Form2 <> nil then
    FreeAndNil(form2);
end;

end.

4) Run the Application and close Form1 -- Getting below error.

enter image description here

I don't want to free Form2 Object on OnClose or OnCloseQuery, Because i have some code on these events.I want to free Form2 object after OnClose or OnCloseQuery.

why i am getting error and how to fix this?

2
In the destroy event the variable Form2is unknown, where did you declare it ? where did you created it ? From the code you posted this should not even compileGuidoG
Form2is probably declared in unit2and never created in this unit. Either remove the declaration of Form2from your formCreate here, or move it to the private section of this form and remove the declaration in unit2GuidoG
it compiled because Form2 is declared in unit2. read my prior commentGuidoG
Simply Remove declaration of Form2 from TForm1.FormCreate(Sender: TObject);BMS
@BMS That would mean creating an instance of a global declared object. I am not sure but that could create other troubles when Form1 is created more than one time for exampleGuidoG

2 Answers

4
votes

The problem you describe can be reproduced in Delphi 7, following your steps. The reason for the access violation is that Form2 is autocreated in the .dpr file (and that makes the application the owner of the form), but you also attempt to control its lifetime by calling FreeAndNil(Form2) in the OnDestroy event of Form1. At the time you attempt to free the form, it is already freed by the application.

It is not really clear, why you attempt to free Form2 when the application (presumably) is about to terminate (since the main form is being destroyed). As the application is the owner you can safely let the application take care of its duties.

You need to decide, who you want to be the owner of Form2.

  1. If it's going to be you, you should remove Form2 from the list of autocreated forms (see Project - Options - Forms and move Form2 to Available forms).
  2. If you want the application to be the owner, simply don't Free the form, use hide and show methods to control its visibility, if needed, but leave it to the application to destroy it according its design.

By the way, the code you have in TForm1.FormCreate() has nothing to do with the AV you have seen. That Form2 is a separate instance, and if the intention is to show a splash form, it's ok, as follows:

procedure TForm1.FormCreate(Sender: TObject);
var
  form2 : TForm2;
begin
  form2 := TForm2.Create(nil);
  try
    form2.ShowModal;
  finally
    form2.Free;
  end;
end;

The form doesn't need an owner, therefore the nil in Create. Since the variable form2 is local, there's absolutely no need to nil it.

3
votes

the problem is that Form2will be declared in unit2 and again in the FormCreate of this form. You are simply using the wrong one.

I suggest removing the declaration of Form2from unit2 and only use the one in this unit, and also move the declaration to the private section

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    Form2: TForm2; // Declare the form here, not in unit2
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form2 := TForm2.Create(nil);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // no need to check if Form2 exists, it is created in the FormCreate and will thus always exist
  // unless there is code somewhere that we cant see that destroys it.
  // Also no need to set the variable to nil as mentioned in the Tom's answer
  Form2.Free;
end;