Solved

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

Posted on 2014-01-26
9
639 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

Suggested Solutions

There is an easy way, in .NET, to centralize the treatment of all unexpected errors. First of all, instead of launching the application directly in a Form, you need first to write a Sub called Main, in a module. Then, set the Startup Object to th…
Whether you’re a college noob or a soon-to-be pro, these tips are sure to help you in your journey to becoming a programming ninja and stand out from the crowd.
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.
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.

713 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