I'm pretty new to Delphi and am attempting to use different units to store TLists and TObjects, as well as generate other combobox options based on previous combobox selections. I am receiving the following error messages immediately after running:
Project Project1.exe raised exception class $C0000005 with message 'access violation at 0x004087d7: read of address 0x00000000
Access violation at address 00408813 in module 'Project1.exe'. Read of address 00000000.
I've traced through the steps in the debugging tool but I can't see why it won't work. I have googled the issue and have found a few solutions but nothing that seems like it applies to my current problem.
unit MainForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TfMain = class(TForm)
lCPU: TLabel;
cbCPU: TComboBox;
lGPU: TLabel;
cbGPU: TComboBox;
lMotherboard: TLabel;
cbMotherboard: TComboBox;
lRAM: TLabel;
cbRAM: TComboBox;
eCompatible: TEdit;
lCompatible: TLabel;
eCost: TEdit;
lCost: TLabel;
Label11: TLabel;
lbCPU: TListBox;
lbMotherboard: TListBox;
lbGPU: TListBox;
lbRAM: TListBox;
procedure FormShow(Sender: TObject);
procedure cbCPUChange(Sender: TObject);
private
{ Private declarations }
public
// procedure cbCPUpopulate();
end;
type TGPU = class(TObject)
public
name : string;
vram : integer;
clock : integer;
price : string;
end;
type TCPU = class(TObject)
public
name : string;
cores : integer;
clock : real;
intel : boolean;
amd : boolean;
price : string;
end;
type TMobo = class(TObject)
public
name : string;
intel : boolean;
amd : boolean;
price : string;
end;
type TRAM = class(TObject)
public
name : string;
memory : integer;
clock : integer;
price : string;
end;
var
fMain: TfMain;
implementation
{$R *.dfm}
uses CPUs, GPUs, Motherboards;
procedure TfMain.cbCPUChange(Sender: TObject);
var
i : integer;
mobo : TMobo;
begin
if TCPU(fMain.cbCPU.ItemIndex).intel = true then begin
for i := 0 to Motherboards.MoboPartsList.Count-1 do begin
mobo := TMobo(Motherboards.MoboPartsList.Items[i]);
if mobo.intel = true then begin
fMain.cbMotherboard.Items.AddObject(mobo.name, mobo);
end;
end;
end;
end;
procedure cbCPUpopulate();
var
i : integer;
CPU : TCPU;
begin
for i := 0 to CPUs.CPUPartsList.Count-1 do begin
CPU := TCPU(CPUs.CPUPartsList.Items[i]);
fMain.cbCPU.Items.AddObject(CPU.name, CPU);
end;
end;
procedure TfMain.FormShow(Sender: TObject);
begin
Motherboards.MakeMoboList;
CPUs.MakeCPUList();
GPUs.popgpulist();
cbCPUpopulate;
end;
end.
In cbCPUChange(), you are accessing the TCPU objects incorrectly.
The expression TCPU(fMain.cbCPU.ItemIndex) is type-casting a list index into an object pointer. You need to instead use the ItemIndex to access the ComboBox's Objects[] property, which is filled with TCPU objects by cbCPUpopulate().
Also, there is no need to use the fMain global variable from inside a method of the TfMain class. Use the method's implicit Self pointer instead.
Try something more like this:
procedure TfMain.cbCPUChange(Sender: TObject);
var
i : integer;
mobo : TMobo;
begin
cbMotherboard.Items.BeginUpdate;
try
cbMotherboard.Items.Clear;
i := cbCPU.ItemIndex;
if i < 0 then Exit;
if TCPU(cbCPU.Items.Objects[i]).intel then begin
for i := 0 to Motherboards.MoboPartsList.Count-1 do begin
mobo := TMobo(Motherboards.MoboPartsList.Items[i]);
if mobo.intel then begin
cbMotherboard.Items.AddObject(mobo.name, mobo);
end;
end;
end;
finally
cbMotherboard.Items.EndUpdate;
end;
end;
On a side note, the declarations of the TCPU, TMobo, and TRAM types do not belong in the MainForm unit. They should be declared in the other units that are actually creating object lists of those types, eg: TCPU in the CPUs unit, TMobo in the Motherboards unit, etc:
unit MainForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TfMain = class(TForm)
lCPU: TLabel;
cbCPU: TComboBox;
lGPU: TLabel;
cbGPU: TComboBox;
lMotherboard: TLabel;
cbMotherboard: TComboBox;
lRAM: TLabel;
cbRAM: TComboBox;
eCompatible: TEdit;
lCompatible: TLabel;
eCost: TEdit;
lCost: TLabel;
Label11: TLabel;
lbCPU: TListBox;
lbMotherboard: TListBox;
lbGPU: TListBox;
lbRAM: TListBox;
procedure FormShow(Sender: TObject);
procedure cbCPUChange(Sender: TObject);
private
{ Private declarations }
procedure cbCPUpopulate;
public
end;
var
fMain: TfMain;
implementation
{$R *.dfm}
uses
CPUs, GPUs, Motherboards;
procedure TfMain.cbCPUChange(Sender: TObject);
var
i : integer;
mobo : Motherboards.TMobo;
begin
cbMotherboard.Items.BeginUpdate;
try
cbMotherboard.Items.Clear;
i := cbCPU.ItemIndex;
if i < 0 then Exit;
if CPUs.TCPU(cbCPU.Items.Objects[i]).intel then begin
for i := 0 to Motherboards.MoboPartsList.Count-1 do begin
mobo := Motherboards.TMobo(Motherboards.MoboPartsList.Items[i]);
if mobo.intel then begin
cbMotherboard.Items.AddObject(mobo.name, mobo);
end;
end;
end;
finally
cbMotherboard.Items.EndUpdate;
end;
end;
procedure TfMain.cbCPUpopulate;
var
i : integer;
CPU : CPUs.TCPU;
begin
for i := 0 to CPUs.CPUPartsList.Count-1 do begin
CPU := CPUs.TCPU(CPUs.CPUPartsList.Items[i]);
cbCPU.Items.AddObject(CPU.name, CPU);
end;
end;
procedure TfMain.FormShow(Sender: TObject);
begin
Motherboards.MakeMoboList;
CPUs.MakeCPUList;
GPUs.popgpulist;
cbCPUpopulate;
end;
end.
User contributions licensed under CC BY-SA 3.0