Solved

creating 2 instances of a form getting "invalid pointer error" on Free

Posted on 2014-01-26
9
636 Views
Last Modified: 2014-01-26
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.Free;
  FormPostalAddress.Free;
end;

I am getting an "invalid pointer error" , but if I remove either: FormResidentialAddress.Free; or  FormPostalAddress.Free; it solves the problem.

What atrocity am I committing?
0
Comment
Question by:etiennedemers
  • 6
  • 2
9 Comments
 
LVL 100

Expert Comment

by:mlmcc
ID: 39811004
Need to see the declaration of TFormAddress.

mlmcc
0
 

Author Comment

by:etiennedemers
ID: 39811050
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.

Open in new window

0
 

Author Comment

by:etiennedemers
ID: 39811059
Problem goes away if I remove "ModuleDB.Destroy"

procedure TFormAddress.FormDestroy(Sender: 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.
0
Netscaler Common Configuration How To guides

If you use NetScaler you will want to see these guides. The NetScaler How To Guides show administrators how to get NetScaler up and configured by providing instructions for common scenarios and some not so common ones.

 

Author Comment

by:etiennedemers
ID: 39811061
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.

Open in new window

0
 
LVL 31

Accepted Solution

by:
Marco Gasi earned 500 total points
ID: 39811147
You have to use ModuleDB.Free instead of ModuleDB.Destroy. Usually, you have never to use Destroy method directly: it is used internally when you call Free method.
0
 

Author Comment

by:etiennedemers
ID: 39811165
oops sorry yeah, this is me fiddling around with code trying different things.

the right code is:
procedure TFormAddress.FormDestroy(Sender: 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.Free;
  End;
0
 

Author Comment

by:etiennedemers
ID: 39811180
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.Free;
  FormPostalAddress.Free;
0
 

Author Closing Comment

by:etiennedemers
ID: 39811182
Actually, I changed my code from the formevent.destroy to formever.close and it solved the problem.

Thanks for your help
0
 
LVL 31

Expert Comment

by:Marco Gasi
ID: 39811218
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
0

Featured Post

PRTG Network Monitor: Intuitive Network Monitoring

Network Monitoring is essential to ensure that computer systems and network devices are running. Use PRTG to monitor LANs, servers, websites, applications and devices, bandwidth, virtual environments, remote systems, IoT, and many more. PRTG is easy to set up & use.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
This article is meant to give a basic understanding of how to use R Sweave as a way to merge LaTeX and R code seamlessly into one presentable document.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

825 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question