Solved

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

Posted on 2014-01-26
9
642 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 2
9 Comments
 
LVL 101

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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 

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

[Webinar] How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them. Thursday, July 13, 2017 10:00 A.M. PDT

Question has a verified solution.

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

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
The purpose of this article is to demonstrate how we can use conditional statements using Python.
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

630 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