etiennedemers
asked on
creating 2 instances of a form getting "invalid pointer error" on Free
Hello Experts,
I want to do things the right way and I thought I was heading the right direction but it seems I am missing something.
I have address.pas which is a simple form with edit boxes.
I have Personnel.pas which uses 2 instance of form address.pas.
Each instances are created as follow: (pseudo code)
var
FormResidentialAddress: TFormAddress;
FormPostalAddress: TFormAddress;
procedure TFormPersonnel.FormCreate( Sender: TObject);
begin
FormResidentialAddress := TFormAddress.Create(Self);
FormPostalAddress := TFormAddress.Create(Self);
end;
Problem occurs when I want to Free the 2 instances as follow:
procedure TFormPersonnel.FormDestroy (Sender: TObject);
begin
FormResidentialAddress.Fre e;
FormPostalAddress.Free;
end;
I am getting an "invalid pointer error" , but if I remove either: FormResidentialAddress.Fre e; or FormPostalAddress.Free; it solves the problem.
What atrocity am I committing?
I want to do things the right way and I thought I was heading the right direction but it seems I am missing something.
I have address.pas which is a simple form with edit boxes.
I have Personnel.pas which uses 2 instance of form address.pas.
Each instances are created as follow: (pseudo code)
var
FormResidentialAddress: TFormAddress;
FormPostalAddress: TFormAddress;
procedure TFormPersonnel.FormCreate(
begin
FormResidentialAddress := TFormAddress.Create(Self);
FormPostalAddress := TFormAddress.Create(Self);
end;
Problem occurs when I want to Free the 2 instances as follow:
procedure TFormPersonnel.FormDestroy
begin
FormResidentialAddress.Fre
FormPostalAddress.Free;
end;
I am getting an "invalid pointer error" , but if I remove either: FormResidentialAddress.Fre
What atrocity am I committing?
ASKER
unit Address;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, AdvSmoothPanel, ModifiedTypes,
Vcl.StdCtrls, AdvEdit, Data.DB, DBAccess, Uni, UniProvider, SQLiteUniProvider,
UniSQLMonitor, MySQLUniProvider,ModuleDB,MyGlobalConstants, DASQLMonitor,
MemDS;
type
TFormAddress = class(TForm)
AdvSmoothPanel1: TAdvSmoothPanel;
eAddress2: TEdit;
eAddress1: TEdit;
eCity: TEdit;
eState: TEdit;
eCountry: TEdit;
eZip: TEdit;
bSave: TButton;
lAddress1: TLabel;
lAddress2: TLabel;
lCity: TLabel;
lState: TLabel;
lCountry: TLabel;
lZip: TLabel;
liAddresses: TLabel;
UniQuery: TUniQuery;
UniMonitor: TUniSQLMonitor;
UniSQL: TUniSQL;
UniConnection: TUniConnection;
Datasource: TUniDataSource;
MySQLUniProvider: TMySQLUniProvider;
lFormMode: TLabel;
procedure FillEditBoxes(sIndex : String);
procedure bSaveClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
end;
var
FormAddress:TFormAddress;
ModuleDB : TModuleDB; //creating a varialble to use the ModuleDB.pas whi is instanciated in the form.creat event bellow
implementation
{$R *.dfm}
{$REGION 'Form controls Click events'}
procedure TFormAddress.bSaveClick(Sender: TObject);
//Save all the items in the edit boxes regardless of if they were modified
begin
//Load all items from the Residential Address into tbPersonnel
ModuleDB.MyDBUpdate(
tblA
,
fldAAddress1 + '=''' + eAddress1.Text + ''',' +
fldAAddress2 + '=''' + eAddress2.Text + ''',' +
fldACity + '=''' + eCity.Text + ''',' +
fldAState + '=''' + eState.Text + ''',' +
fldAZip + '=''' + eZip.Text + ''',' +
fldACountry + '=''' + eCountry.Text + ''''
,
fldAi + '=' + liAddresses.caption
) ;
ModalResult := mrOk;
end;
{$ENDREGION}
{$REGION 'Form custom procedures'}
procedure TFormAddress.FillEditBoxes(sIndex : String);
//Fills the desired EditBoxes on the form
Var
MyStringList : TStringlist;
begin
MyStringList:=TStringList.Create;
MyStringList:=ModuleDB.MyDBFillStringListWith1Row('*',tblA,fldAi + '=' + sIndex);
//Load edit boxes with values from DB
eAddress1.Text := TString(MyStringList.Objects[MyStringList.IndexOf(fldAAddress1)]).Str;
eAddress2.Text := TString(MyStringList.Objects[MyStringList.IndexOf(fldAAddress2)]).Str;
eCity.Text := TString(MyStringList.Objects[MyStringList.IndexOf(fldACity)]).Str;
eState.Text := TString(MyStringList.Objects[MyStringList.IndexOf(fldAState)]).Str;
eZip.Text := TString(MyStringList.Objects[MyStringList.IndexOf(fldaZip)]).Str;
eCountry.Text := TString(MyStringList.Objects[MyStringList.IndexOf(fldACountry)]).Str;
end;
{$ENDREGION}
{$REGION 'Form Events'}
procedure TFormAddress.FormCreate(Sender: TObject);
begin
//Set the connection parameters defined in MyGLobalConstants
UniConnection.ProviderName := MyProviderName; //MyProviderName in a constant declared in MyGLobalConstants
UniConnection.Port := MyPort; //MyPort in a constant declared in MyGLobalConstants
UniConnection.Database := MyDatabase; //MyDatabase in a constant declared in MyGLobalConstants
UniConnection.Username := MyUsername; //MyUsername in a constant declared in MyGLobalConstants
UniConnection.Server := MyServer; //MyServer in a constant declared in MyGLobalConstants
//This creates an instance of ModuleDB for this form
//In order to use ModuleDB it need to instanciate its Uni variables by passing this form's Uni variables who are declared as objects
ModuleDB := TModuleDB.Create(UniQuery,
UniMonitor,
UniSQL,
UniConnection,
Datasource,
MySQLUniProvider);
end;
procedure TFormAddress.FormDestroy(Sender: TObject);
begin
ModuleDB.Destroy; //Gets rid of the ModuleDB instance and free it's allocated memory
end;
procedure TFormAddress.FormShow(Sender: TObject);
begin
FillEditBoxes(liAddresses.caption); //Fills the desired EditBoxes on the form
end;
{$ENDREGION}
end.
ASKER
Problem goes away if I remove "ModuleDB.Destroy"
procedure TFormAddress.FormDestroy(S ender: TObject);
begin
ModuleDB.Destroy; //Gets rid of the ModuleDB instance and free it's allocated memory
end;
but I want to free the memory used on this instance of ModuleDB and it works well as long as I don't have 2 instances of a form using this module.
procedure TFormAddress.FormDestroy(S
begin
ModuleDB.Destroy; //Gets rid of the ModuleDB instance and free it's allocated memory
end;
but I want to free the memory used on this instance of ModuleDB and it works well as long as I don't have 2 instances of a form using this module.
ASKER
unit ModuleDB;
interface
uses System.Classes, Data.DB, DBAccess, Uni, UniProvider,
SQLiteUniProvider, MemDS, DASQLMonitor, UniSQLMonitor,ModifiedTypes,
MySQLUniProvider, Dialogs,MyGlobalConstants, Variants,System.SysUtils;
type
TModuleDB=Class(TObject)
ModuleUniConnection: TUniConnection;
ModuleUniQuery: TUniQuery;
ModuleDatasource: TUniDataSource;
ModuleUniSQL: TUniSQL;
ModuleUniMonitor: TUniSQLMonitor;
ModuleMySQLUniProvider: TMySQLUniProvider;
Constructor Create(MyUniQuery: TUniQuery;
MyUniMonitor: TUniSQLMonitor;
MyUniSQL: TUniSQL;
MyUniConnection: TUniConnection;
MyDatasource: TUniDataSource;
MyMySQLUniProvider: TMySQLUniProvider);
Destructor Destroy;override;
public
Function GetSQLItem(sSELECT,sFROM,sWHERE : String):Variant;
Procedure MyDBInsert(sTable,sField,sValue : String);
Procedure MyDBUpdate(sTable,sSet,sWhere : String);
Function GetTableTotalRows(sTable : String):Integer;
Procedure MyDBDelete(sTable,sWhere : String);
Function MyDBInsertNewRow(sTable,sField : String):Integer;
function MyDBFillStringListWith1Row(sSELECT,sFROM,sWHERE : String): TStringList;
Function MyDBFillStringList(sSQL,sSeparator : String): TStringList;
End;
implementation
Constructor TModuleDB.create(
MyUniQuery: TUniQuery;
MyUniMonitor: TUniSQLMonitor;
MyUniSQL: TUniSQL;
MyUniConnection: TUniConnection;
MyDatasource: TUniDataSource;
MyMySQLUniProvider: TMySQLUniProvider
);
begin
ModuleUniquery:= MyUniQuery;
ModuleUniMonitor:= MyUniMonitor;
ModuleUniSQL:= MyUniSQL;
ModuleUniConnection := MyUniConnection;
ModuleDatasource:= MyDataSource;
ModuleMySQLUniProvider:= MyMySQLUniProvider;
end;
Destructor TModuleDB.Destroy;
Begin
ModuleUniquery.Close;
ModuleUniConnection.Close;
ModuleUniquery.Free;
ModuleUniConnection.Free;
ModuleUniMonitor.Free;
ModuleUniSQL.Free;
ModuleDatasource.Free;
ModuleMySQLUniProvider.Free;
End;
Function TModuleDB.GetSQLItem(sSELECT,sFROM,sWHERE : String):Variant;
//GetSQLItem: gets one item from a db table
//sSELECT: fields you want to select
//sFROM: table
//sWHERE: condition
//Returns a variant corresponding to the result of the SQL query
var
sSQL:String;
begin
sSQL:= 'SELECT ' + sSELECT + ' FROM ' + sFROM + ' WHERE ' + sWHERE + ';'; //Build SQL Statement
ModuleUniConnection.Connect; //Open the connection to the Database
ModuleUniquery.SQL.Text:=sSQL; //Assign the sSQL to the query
ModuleUniquery.Open; //Opens "run" the query and stores results in uniquery object
ModuleDatasource.Dataset:=ModuleUniquery; //Fill the global datasource with the result of the uniquery SQL command
ModuleDataSource.DataSet.FindFirst; //Reset to the first record of the table
GetSQLItem:=ModuleDataSource.DataSet.FieldValues[sSELECT]; //Load the Value from the table specified in sSELECT into the array
ModuleUniquery.Close;
End;
Function TModuleDB.GetTableTotalRows(sTable : String):Integer;
Begin
ModuleUniconnection.Connect; //Open the connection to the Database
ModuleUniquery.SQL.Text:='Select * from ' + sTable + ';'; //Assign the sSQL to the query
ModuleUniquery.Open; //Opens "run" the query and stores results in uniquery object
GetTableTotalRows:=ModuleUniquery.RecordCount;
End;
Procedure TModuleDB.MyDBInsert(sTable,sField,sValue : String);
//MyDBUpdate: Generic function to update the database
//sTable: tbPersonnel
begin
try
try
ModuleUniconnection.Connect;
ModuleUnisql.SQL.Text:='insert into ' + sTable + ' (' + sField + ') Values(' + sValue + ')';
//'UPDATE tbPersonnel SET vcPersonnelLastName="test2" WHERE iXPersonnel = 1;' ;
ModuleUnisql.Execute;
except
//outputdebugstring(PChar(ModuleDB.unisql.SQL.Text));
end;
finally
end;
end;
Function TModuleDB.MyDBInsertNewRow(sTable,sField : String):Integer;
Begin
try
ModuleUniconnection.Connect;
ModuleUnisql.SQL.Text:='INSERT INTO ' + sTable + ' (' + sField + ') VALUES (NULL);';
ModuleUnisql.Execute;
ModuleUniquery.SQL.Text:= 'SELECT LAST_INSERT_ID();';
ModuleUniquery.open;
ModuleUniquery.First;
MyDBInsertNewRow := ModuleUniQuery.Fields[0].AsInteger;
except
end;
End;
Procedure TModuleDB.MyDBUpdate(sTable,sSet,sWhere : String);
//MyDBUpdate: Generic function to update the database
//sTable: table name
//sSet: set statement
//sWhere: Where statement
begin
ModuleUniconnection.Connect;
ModuleUnisql.SQL.Text:='UPDATE ' + sTable + ' SET ' + sSet + ' WHERE ' + sWhere + ';' ;
ModuleUnisql.Execute;
end;
Procedure TModuleDB.MyDBDelete(sTable,sWhere : String);
//MyDBDelete: Generic function to Delete a record from the database
//sTable: table name
//sWhere: Where statement
begin
ModuleUniconnection.Connect;
ModuleUnisql.SQL.Text:='DELETE FROM ' + sTable + ' WHERE ' + sWhere + ';' ;
ModuleUnisql.Execute;
end;
function TModuleDB.MyDBFillStringListWith1Row(sSELECT,sFROM,sWHERE : String): TStringList;
var
i:integer;
x: Integer;
sField,sValue,sSQL:String;
ots : TString;
begin
try
MyDBFillStringListWith1Row := TStringList.Create;
sSQL:= 'SELECT ' + sSELECT + ' FROM ' + sFROM + ' WHERE ' + sWHERE +';'; //Build SQL Statement
ModuleUniconnection.Connect; //Open the connection to the Database
ModuleUniquery.SQL.Text:=sSQL; //Assign the sSQL to the query
ModuleUniquery.Open; //Opens "run" the query and stores results in uniquery object
ModuleDatasource.Dataset:=ModuleUniquery; //Fill the global datasource with the result of the uniquery SQL command
i:=ModuleDataSource.DataSet.FieldCount; //Get the amount of lines to fill the array
for x := 1 to i do //Go from one record to the next until the end
begin
sField:=ModuleDataSource.DataSet.FieldList.Fields[x-1].DisplayName; //Load the Value into the array
if ModuleDataSource.DataSet.FieldValues[ModuleDataSource.DataSet.FieldList.Fields[x-1].DisplayName] = Null then sValue:=''
else
begin
sValue:=ModuleDataSource.DataSet.FieldValues[ModuleDataSource.DataSet.FieldList.Fields[x-1].DisplayName]; //Load the Value into the array
end;
ots:= TString.Create(sValue);
MyDBFillStringListWith1Row.AddObject(sField, ots);
end;
except
showmessage(ModuleUniquery.SQL.Text);
end;
End;
function TModuleDB.MyDBFillStringList(sSQL,sSeparator : String): TStringList;
var
i,x,y,z:integer;
sString:String;
begin
MyDBFillStringList := TStringList.Create; //TComboBox.Create(Nil);
//Load the database info
Moduleuniconnection.Connect; //Open the connection to the Database
Moduleuniquery.SQL.Text:=sSQL; //Assign the sSQL to the query
Moduleuniquery.Open; //Opens "run" the query and stores results in uniquery object
ModuleDatasource.Dataset:=Moduleuniquery; //Fill the global datasource with the result of the uniquery SQL command
ModuleDataSource.DataSet.FindFirst; //Done for RecordCount to be accurate
ModuleDataSource.DataSet.FindLast; //Done for RecordCount to be accurate
i:=ModuleDataSource.DataSet.RecordCount; //Get the amount of lines to fill the array
z:=ModuleDataSource.DataSet.FieldCount;
ModuleDataSource.DataSet.FindFirst; //Reposition the cursor to the first record of the table
sString:='';
for x := 1 to i do //Go from one record to the next until the end
begin
for y := 2 to z do
begin
if sString = '' then sString:=ModuleDataSource.DataSet.FieldValues[ModuleDataSource.DataSet.FieldList.Fields[y-1].DisplayName]
else
begin
sString:=sString + sSeparator + ModuleDataSource.DataSet.FieldValues[ModuleDataSource.DataSet.FieldList.Fields[y-1].DisplayName];
end;
end;
MyDBFillStringList.AddObject(sString, TObject(StrToInt(ModuleDataSource.DataSet.FieldValues[ModuleDataSource.DataSet.FieldList.Fields[0].DisplayName])));
ModuleDataSource.DataSet.FindNext; //Move to the next record of the table
sString:='';
end;
end;
end.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
oops sorry yeah, this is me fiddling around with code trying different things.
the right code is:
procedure TFormAddress.FormDestroy(S ender: TObject);
begin
ModuleDB.Free; //Gets rid of the ModuleDB instance and free it's allocated memory
end;
But the problem is still there.
I think I am doing something wrong in ModuleDB. Should I change the following?:
Destructor TModuleDB.Destroy;
Begin
ModuleUniquery.Close;
ModuleUniConnection.Close;
ModuleUniquery.Free;
ModuleUniConnection.Free;
ModuleUniMonitor.Free;
ModuleUniSQL.Free;
ModuleDatasource.Free;
ModuleMySQLUniProvider.Fre e;
End;
the right code is:
procedure TFormAddress.FormDestroy(S
begin
ModuleDB.Free; //Gets rid of the ModuleDB instance and free it's allocated memory
end;
But the problem is still there.
I think I am doing something wrong in ModuleDB. Should I change the following?:
Destructor TModuleDB.Destroy;
Begin
ModuleUniquery.Close;
ModuleUniConnection.Close;
ModuleUniquery.Free;
ModuleUniConnection.Free;
ModuleUniMonitor.Free;
ModuleUniSQL.Free;
ModuleDatasource.Free;
ModuleMySQLUniProvider.Fre
End;
ASKER
Actually, even if I put Moduledb.destroy as comments, I still get the fault...
I can still clear the fault if I remove the ModuleDB.Free or if I remove
one of these guys:
FormResidentialAddress.Fre e;
FormPostalAddress.Free;
I can still clear the fault if I remove the ModuleDB.Free or if I remove
one of these guys:
FormResidentialAddress.Fre
FormPostalAddress.Free;
ASKER
Actually, I changed my code from the formevent.destroy to formever.close and it solved the problem.
Thanks for your help
Thanks for your help
Sorry, I was away to bring my daughters to the school :) Anyway, I'm happy you solved the problem: keep in mind that Destroy method doesn't must be called directly: use always Free and you'll be happy!
Cheers
Cheers
mlmcc