I am trying to use a class to display a progress indicator.
If I declare ProgressIndicator as a variable within the calling procedure, everything works fine, and ANewForm displays as I would expect.
However, the following code produces an access violation. Can anyone help me to understand why?
unit Main;
interface
*uses
Winapi.Windows, Vcl.Forms,
System.Classes, Vcl.Controls, Vcl.StdCtrls,
Progress;
type
TProgressIndicator = class
private
public
ANewForm : TForm;
constructor Create;
end;
type
TfmMain = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
ProgressIndicator : TProgressIndicator;
end;
var
fmMain: TfmMain;
implementation
{$R *.dfm}
constructor TProgressIndicator.Create;
begin
ANewForm := TForm.Create(Application);
ANewForm.Show;
end;
procedure TfmMain.Button1Click(Sender: TObject);
begin
ProgressIndicator.Create;
end;
end.
CodePudding user response:
There's a difference between ProgressIndicator.Create
and TProgressIndicator.Create
. Usually, you want to use the second form which says, "create a new instance of class TProgressIndicator
". The first form says, "take an instance of TProgressIndicator
which is stored in the variable ProgressIndicator
and call its Create
method". The problem is, it doesn't create that instance. In your case, ProgressIndicator
is nil
, because all class members are initialized to a zero-like value at the construction time, which is not a problem per-se - it still is linked to the class data so it can actually call the Create
method. The method tries to create the form, which succeeds, and then tries to store it to the ANewForm
field, because the in-memory address of ANewForm
is Self
offset; for your code, the offset is probably 0, Self
is nil, which gives a final address of (nil 0) = 0
and the memory location 0 is located in a memory page which is prohibited from all access. That's why you get an Access Violation.
What you want is:
procedure TfmMain.Button1Click(Sender: TObject);
begin
ProgressIndicator := TProgressIndicator.Create;
end;
That will first create a new instance and then work with that.