Solved

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

Posted on 2014-01-26
9
633 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
 

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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 30

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 30

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

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

If you haven’t already, I encourage you to read the first article (http://www.experts-exchange.com/articles/18680/An-Introduction-to-R-Programming-and-R-Studio.html) in my series to gain a basic foundation of R and R Studio.  You will also find the …
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

760 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

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now