0
votes

I'm creating a new app in XE3 but using some units created in D2007.

I"m getting an error when freeing a TStringList data item. Here's the code that creates the data item FSQL:

procedure TPayorDM.DataModuleCreate(Sender: TObject);
begin
  FPayorDRM := TDRM.Create;
  FSQL := TStringList.Create;
end;

Here's the code that is getting the error:

procedure TPayorDM.DataModuleDestroy(Sender: TObject);
begin
  FreeAndNil(FSQL);
  if T_Payor.Active then T_Payor.Close;
  FreeAndNil(FPayorDRM);
end;

The error occurs on 'FreeAndNil(FSQL);'. I have tried 'FSQL.Free' and I get the same result.

Here's the error I'm getting:

Project: PayorUpdate.exe raised exception class EInvalidPointer with message 'Invalid pointer operation.

When I break blue arrows (debug mode) is pointing to _FreeMem(Pointer(Self)); in the procedure TObject.FreeInstance in System unit as follows:

procedure TObject.FreeInstance;
begin
  CleanupInstance;
  _FreeMem(Pointer(Self));
end;

If I don't free the TStringList data item I would have a memory leak in the app.

Is there possibly a configuration option I need to set? I've search with google and have not found anything that explains what I am doing wrong other than one of the three possibilities:

  • It was allocated by some other memory manager.
  • It had already been freed once before.
  • It had never been allocated by anything.

If I put a try ... except... I'm able to get around the issue, but I prefer not to do this.

BTW, I have another TStringList in different unit and I create and FreeAndNil and I do not get any error.

Here is entire source:


    unit PayorDataMgr;

    interface

    uses
      SysUtils,
      Classes,
      Dialogs,
      NativeXML,
      adscnnct,
      DB,
      adsdata,
      adsfunc,
      adstable,
      ace,
      cbs.drm,
      cbs.utils,
      cbs.LogFiles;

    const
      POLICY_TYPES: array[1..3] of string = ('Primary','Secondary','Tertiary');

    type
      TPayorRecord = Record
        ASSIGNBENEFITS: Boolean;
        AUTHORIZE: Boolean;
        BATCHBILL: Boolean;
        CLAIMMAX: Integer;
        DISCONTINUED: TDateTime;
        DISPENSEUPDATE: Boolean;
        EHRSIGNOFF: Boolean;
        EMCDEST: String;
        FORM: String;
        GOVASSIGN: Boolean;
        HIDE: Boolean;
        IGRPUNIQUE: Integer;
        LEGACYPLAN: String;
        LEGACYTYPE: String;
        LOCALATTN: String;
        LOCALCITY: String;
        LOCALNAME: String;
        LOCALPHONE: String;
        LOCALSTATE: String;
        LOCALSTREET: String;
        LOCALZIP: String;
        MASTERATTN: String;
        MASTERCITY: String;
        MASTERNAME: String;
        MASTERPHONE: String;
        MASTERSTATE: String;
        MASTERSTREET: String;
        MASTERZIP: String;
        MEDIGAPCODE: String;
        MEDIGAPPAYOR: Boolean;
        MEDPLANGUID: String;
        MODIFIED: TDateTime;
        NEICCODE: String;
        NEICTYPESTDC: Integer;
        OWNER: String;
        PAYORGUID: String;
        PAYORSUBTYPESTDC: Integer;
        PAYORTYPESTDC: Integer;
        PAYORUNIQUE: Integer;
        PAYPERCENT: Integer;
        RTCODE: String;
        SRXPLANGUID: String;
        STATEFILTER: String;
        procedure Clear;
      End;

      TPayors = Record
      private
        function _pGetCount: Integer;
      public
        Items: Array of TPayorRecord;
        procedure Add(const aItem:TPayorRecord);
        function CarriersList:TStrings;
        procedure Free;
        function GetPayorGuid(const aPAYORUNIQUE:Integer):String;
        function IndexOfIgrpUnique(Const aIGRPUNIQUE:Integer):Integer;
        function IndexOfPayorUnique(Const aPAYORUNIQUE:Integer):Integer;
        procedure SortByName;
        property Count:Integer Read _pGetCount;
      End;

      TPayorDM = class(TDataModule)
        CommonConnection: TAdsConnection;
        T_Payor: TAdsTable;
        Q_Payor: TAdsQuery;
        procedure DataModuleDestroy(Sender: TObject);
        procedure DataModuleCreate(Sender: TObject);
      private
        FPayorDRM: TDRM;
        FSQL: TStringList;
        function _LoadRecordFromTable:TPayorRecord;
        function _newIDSTRING(const aFormat:String='F'):String;
        { Private declarations }
        procedure _pSetConnectionHandle(const Value: Integer);
        procedure _pSetErrorMessage(const Value: String);
        procedure _psetSQL(const Value: TStringList);

        { Private properties }
        property ErrorMessage:String Write _pSetErrorMessage;
      public
        function AddPayor(var aPAYORRECORD:TPAYORRECORD):Boolean;
        function ExecuteScript(const aTo,aFrom:string):Boolean;
        function FindPayor(const aPAYORGUID:String):Boolean;overload;
        function FindPayor(const aPAYORUNIQUE:Integer):Boolean;overload;
        function GetPayorData:TDRM;
        function GetRecordCount(const aData:String):Integer;
        function LoadCarriers(const aHide:boolean = False):TPayors;
        function LoadPayor:TPayorRecord;
        function OpenTable:Boolean;
        function UpdateFromXML(const aPayorNode:TXMLNode):boolean;
        { Public declarations }
        property ConnectionHandle:Integer Write _pSetConnectionHandle;
        property DynamicPayorFields:TDRM Read FPayorDRM;
        property SQL:TStringList Read FSQL Write _psetSQL;
      end;

    var
      PayorDM: TPayorDM;

    implementation

    {$R *.dfm}

    function TPayorDM.AddPayor(var aPAYORRECORD: TPAYORRECORD): Boolean;
    begin
      Result := False;
      if IsNull(aPAYORRECORD.LOCALNAME) then Exit;
      { Create uniques }

      { Add Record }
      if not T_Payor.Active then
        if not OpenTable  then Exit;
      with T_Payor do
      try
        Insert;
        FieldByName('PAYORGUID').AsString := _newIDSTRING;
        FieldByName('MASTERNAME').AsString := aPAYORRECORD.MASTERNAME;
        FieldByName('MASTERSTREET').AsString := aPAYORRECORD.MASTERSTREET;
        FieldByName('MASTERCITY').AsString := aPAYORRECORD.MASTERCITY;
        FieldByName('MASTERSTATE').AsString := aPAYORRECORD.MASTERSTATE;
        FieldByName('PAYORTYPESTDC').AsInteger := aPAYORRECORD.PAYORTYPESTDC;
        FieldByName('MASTERZIP').AsString := aPAYORRECORD.MASTERZIP;
        FieldByName('MASTERATTN').AsString := aPAYORRECORD.MASTERATTN;
        FieldByName('MASTERPHONE').AsString := aPAYORRECORD.MASTERPHONE;
        FieldByName('NEICCODE').AsString := aPAYORRECORD.NEICCODE;
        FieldByName('RTCODE').AsString := aPAYORRECORD.RTCODE;
        FieldByName('STATEFILTER').AsString := aPAYORRECORD.STATEFILTER;
        FieldByName('NEICTYPESTDC').AsInteger := aPAYORRECORD.NEICTYPESTDC;
        FieldByName('PAYORSUBTYPESTDC').AsInteger := aPAYORRECORD.PAYORSUBTYPESTDC;
        FieldByName('OWNER').AsString := aPAYORRECORD.OWNER;
        FieldByName('HIDE').AsBoolean := aPAYORRECORD.HIDE;
        FieldByName('IGRPUNIQUE').AsInteger := aPAYORRECORD.IGRPUNIQUE;
        FieldByName('FORM').AsString := aPAYORRECORD.FORM;
        FieldByName('GOVASSIGN').AsBoolean := aPAYORRECORD.GOVASSIGN;
        FieldByName('CLAIMMAX').AsInteger := aPAYORRECORD.CLAIMMAX;
        FieldByName('MEDIGAPCODE').AsString := aPAYORRECORD.MEDIGAPCODE;
        FieldByName('EMCDEST').AsString := aPAYORRECORD.EMCDEST;
        FieldByName('ASSIGNBENEFITS').AsBoolean := aPAYORRECORD.ASSIGNBENEFITS;
        FieldByName('BATCHBILL').AsBoolean := aPAYORRECORD.BATCHBILL;
        FieldByName('MEDIGAPPAYOR').AsBoolean := aPAYORRECORD.MEDIGAPPAYOR;
        FieldByName('MEDPLANGUID').AsString := aPAYORRECORD.MEDPLANGUID;
        FieldByName('SRXPLANGUID').AsString := aPAYORRECORD.SRXPLANGUID;
        FieldByName('PAYPERCENT').AsInteger := aPAYORRECORD.PAYPERCENT;
        FieldByName('LOCALNAME').AsString := aPAYORRECORD.LOCALNAME;
        FieldByName('LOCALSTREET').AsString := aPAYORRECORD.LOCALSTREET;
        FieldByName('LOCALCITY').AsString := aPAYORRECORD.LOCALCITY;
        FieldByName('LOCALSTATE').AsString := aPAYORRECORD.LOCALSTATE;
        FieldByName('LOCALZIP').AsString := aPAYORRECORD.LOCALZIP;
        FieldByName('LOCALATTN').AsString := aPAYORRECORD.LOCALATTN;
        FieldByName('LOCALPHONE').AsString := aPAYORRECORD.LOCALPHONE;
        FieldByName('EHRSIGNOFF').AsBoolean := aPAYORRECORD.EHRSIGNOFF;
        FieldByName('DISCONTINUED').AsDateTime := aPAYORRECORD.DISCONTINUED;
        FieldByName('MODIFIED').AsDateTime := Now;
        FieldByName('LEGACYPLAN').AsString := aPAYORRECORD.LEGACYPLAN;
        FieldByName('LEGACYTYPE').AsString := aPAYORRECORD.LEGACYTYPE;
        FieldByName('AUTHORIZE').AsBoolean := aPAYORRECORD.AUTHORIZE;
        FieldByName('DISPENSEUPDATE').AsBoolean := aPAYORRECORD.DISPENSEUPDATE;
        Post;
        aPAYORRECORD.PAYORUNIQUE := FieldByName('PAYORUNIQUE').AsInteger;
        aPAYORRECORD.PAYORGUID := FieldByName('PAYORGUID').AsString;
        Close;
        Result := True;
      except on E: EADSDatabaseError do
      begin
        ErrorMessage := 'AddPayor: ERRORCODE: ' + IntToStr(e.ACEErrorCode) +
          ' ERROR: ' + e.Message;
      end;
      end;
    end;

    procedure TPayorDM.DataModuleCreate(Sender: TObject);
    begin
      FPayorDRM := TDRM.Create;
      FSQL := TStringList.Create; { FSQL Created }
   end;

    procedure TPayorDM.DataModuleDestroy(Sender: TObject);
    begin
      try
        FSQL.Free; { FSQL destroyed - work around to get unit to run without error}
      except

      end;
      if T_Payor.Active then T_Payor.Close;
      FreeAndNil(FPayorDRM);
    end;

    function TPayorDM.ExecuteScript(const aTo, aFrom: string):Boolean;
    begin
      Result := False;
      if FSQL.Count = 0 then exit;
      with Q_Payor do
      try
        if Active then Close;
        SQL := FSQL;
        ParamByName('to').Text := aTo;
        ParambyName('from').Text := aFrom;
        ExecSQL;
        if Active then Close;
        Result := True;
      except on E: EADSDatabaseError do
      begin
        ErrorMessage := 'ExecuteScript: ERRORCODE: ' + IntToStr(e.ACEErrorCode) +
          ' ERROR: ' + e.Message + ' SQL: ' + Q_Payor.SQL.Text;
      end;
      end;
    end;

    function TPayorDM.FindPayor(const aPAYORUNIQUE: Integer): Boolean;
    begin
      T_Payor.IndexName := 'PAYORUNIQUE';
      Result := T_Payor.FindKey([aPAYORUNIQUE]);
    end;

    function TPayorDM.FindPayor(const aPAYORGUID: String): Boolean;
    begin
      T_Payor.IndexName := 'PAYORGUID';
      Result := T_Payor.FindKey([aPAYORGUID]);
    end;

    function TPayorDM.GetPayorData: TDRM;
    begin
      if FPayorDRM.Count = 0 then
        FPayorDRM.BuildDRMList(T_Payor);
      Result := FPayorDRM;
    end;


    function TPayorDM.GetRecordCount(const aData:string): Integer;
    begin
      Result := 0;
      if FSQL.Count = 0 then exit;
      with Q_Payor do
      try
        if Active then Close;
        SQL := FSQL;
        ParamByName('data').AsString := aData;
        Open;
        Result := RecordCount;
        Close;
      except on E: EADSDatabaseError do
      begin
        ErrorMessage := 'GetRecordCount: ERRORCODE: ' + IntToStr(e.ACEErrorCode) +
          ' ERROR: ' + e.Message;
      end;
      end;

    end;

    function TPayorDM.LoadCarriers(const aHide: boolean): TPayors;
    begin
      OpenTable;
      Result.Free;
      with T_Payor do
      begin
        First;
        while not EOF do
        begin
          if T_Payor.FieldByName('HIDE').AsBoolean = aHide then
            Result.Add(_LoadRecordFromTable);
          Next;
        end;
        First;
        Result.SortByName;
      end;
    end;

    function TPayorDM.LoadPayor: TPayorRecord;
    begin
      Result.Clear;
      try
        if not T_Payor.active then exit;
        if T_Payor.RecNo = 0 then exit;
        Result := _LoadRecordFromTable;
      except on E: EADSDatabaseError do
      begin
        ErrorMessage := 'LoadPayor: ERRORCODE: ' + IntToStr(e.ACEErrorCode) +
          ' ERROR: ' + e.Message;
      end;
      end;
    end;

    function TPayorDM.OpenTable: Boolean;
    begin
      Result := False;
      with T_Payor do
      try
        if not Active then Open;
        FPayorDRM.BuildDRMList(T_Payor);
        FPayorDRM.LoadValues(T_Payor); { test }
        FPayorDRM.ExportDRMList; { test }
        Result := True;
      except on E: EADSDatabaseError do
      begin
        ErrorMessage := 'OpenTable: ERRORCODE: ' + IntToStr(e.ACEErrorCode) +
          ' ERROR: ' + e.Message;
      end;
      end;
    end;

    function TPayorDM.UpdateFromXML(const aPayorNode: TXMLNode): boolean;
    var
      fKeyData:TXMLNode;
      Idx,fPAYORUNIQUE:Integer;
    begin
      Result := False;
      if not Assigned(aPayorNode) then Exit;
      try
        if FPayorDRM.Count = 0 then
          FPayorDRM.BuildDRMList(T_Payor);
        FPayorDRM.ClearValues;
        fKeyData := aPayorNode.FindNode('KeyData');
        FPayorDRM.FindRecordFromKeyData(fKeyData,T_Payor);
        fPAYORUNIQUE := FPayorDRM.FieldByName('PAYORUNIQUE').AsInteger;
        FPayorDRM.LoadValues(aPayorNode);
        if fPAYORUNIQUE = 0 then
        begin
          FPayorDRM.FieldByName('PAYORUNIQUE').AsInteger := 0;
          FPayorDRM.FieldByName('PAYORGUID').AsString := _newIDSTRING;
          FPayorDRM.FieldByName('MODIFIED').AsDate := Now;
          FPayorDRM.AddRecord(T_Payor)
        end
        else
        begin
          FPayorDRM.FieldByName('MODIFIED').AsDate := Now;
          FPayorDRM.UpdateRecord(T_Payor);
        end;
      except on e:exception do
      begin
        ErrorMessage := 'UpdateFromXML: ERROR: ' + e.Message;
      end;
      end;
    end;

    function TPayorDM._LoadRecordFromTable: TPayorRecord;
    begin
      with T_Payor do
      begin
        Result.PAYORUNIQUE := FieldByName('PAYORUNIQUE').AsInteger;
        Result.PAYORGUID := FieldByName('PAYORGUID').AsString;
        Result.MASTERNAME := FieldByName('MASTERNAME').AsString;
        Result.MASTERSTREET := FieldByName('MASTERSTREET').AsString;
        Result.MASTERCITY := FieldByName('MASTERCITY').AsString;
        Result.MASTERSTATE := FieldByName('MASTERSTATE').AsString;
        Result.PAYORTYPESTDC := FieldByName('PAYORTYPESTDC').AsInteger;
        Result.MASTERZIP := FieldByName('MASTERZIP').AsString;
        Result.MASTERATTN := FieldByName('MASTERATTN').AsString;
        Result.MASTERPHONE := FieldByName('MASTERPHONE').AsString;
        Result.NEICCODE := FieldByName('NEICCODE').AsString;
        Result.RTCODE := FieldByName('RTCODE').AsString;
        Result.STATEFILTER := FieldByName('STATEFILTER').AsString;
        Result.NEICTYPESTDC := FieldByName('NEICTYPESTDC').AsInteger;
        Result.PAYORSUBTYPESTDC := FieldByName('PAYORSUBTYPESTDC').AsInteger;
        Result.OWNER := FieldByName('OWNER').AsString;
        Result.HIDE := FieldByName('HIDE').AsBoolean;
        Result.IGRPUNIQUE := FieldByName('IGRPUNIQUE').AsInteger;
        Result.FORM := FieldByName('FORM').AsString;
        Result.GOVASSIGN := FieldByName('GOVASSIGN').AsBoolean;
        Result.CLAIMMAX := FieldByName('CLAIMMAX').AsInteger;
        Result.MEDIGAPCODE := FieldByName('MEDIGAPCODE').AsString;
        Result.EMCDEST := FieldByName('EMCDEST').AsString;
        Result.ASSIGNBENEFITS := FieldByName('ASSIGNBENEFITS').AsBoolean;
        Result.BATCHBILL := FieldByName('BATCHBILL').AsBoolean;
        Result.MEDIGAPPAYOR := FieldByName('MEDIGAPPAYOR').AsBoolean;
        Result.MEDPLANGUID := FieldByName('MEDPLANGUID').AsString;
        Result.SRXPLANGUID := FieldByName('SRXPLANGUID').AsString;
        Result.PAYPERCENT := FieldByName('PAYPERCENT').AsInteger;
        Result.LOCALNAME := FieldByName('LOCALNAME').AsString;
        Result.LOCALSTREET := FieldByName('LOCALSTREET').AsString;
        Result.LOCALCITY := FieldByName('LOCALCITY').AsString;
        Result.LOCALSTATE := FieldByName('LOCALSTATE').AsString;
        Result.LOCALZIP := FieldByName('LOCALZIP').AsString;
        Result.LOCALATTN := FieldByName('LOCALATTN').AsString;
        Result.LOCALPHONE := FieldByName('LOCALPHONE').AsString;
        Result.EHRSIGNOFF := FieldByName('EHRSIGNOFF').AsBoolean;
        Result.DISCONTINUED := FieldByName('DISCONTINUED').AsDateTime;
        Result.MODIFIED := FieldByName('MODIFIED').AsDateTime;
        Result.LEGACYPLAN := FieldByName('LEGACYPLAN').AsString;
        Result.LEGACYTYPE := FieldByName('LEGACYTYPE').AsString;
        Result.AUTHORIZE := FieldByName('AUTHORIZE').AsBoolean;
        Result.DISPENSEUPDATE := FieldByName('DISPENSEUPDATE').AsBoolean;
      end;
    end;

    function TPayorDM._newIDSTRING(const aFormat: String): String;
    begin
      Result := '';
      try
        with Q_Payor do
        try
          SQL.Clear;
          SQL.Add('SELECT NEWIDSTRING( "' + aFormat + '" ) AS GUID FROM system.iota');
          Open;
          Result := FieldByName('GUID').AsString;
          Close;
        except on E: EADSDatabaseError do
        begin
          ErrorMessage := '_newIDSTRING: ERRORCODE: ' + IntToStr(e.ACEErrorCode) +
            ' ERROR: ' + e.Message;
        end;
        end;
      finally

      end;
    end;

    procedure TPayorDM._pSetConnectionHandle(const Value: Integer);
    begin
      if T_Payor.Active then T_Payor.Close;
      CommonConnection.SetHandle(Value);
      OpenTable;
    end;

    procedure TPayorDM._pSetErrorMessage(const Value: String);
    begin
      WriteError('[TPayorDM]' + Value,LogFilename);
    end;

    procedure TPayorDM._psetSQL(const Value: TStringList);
    begin
      FSQL := Value;
    end;

    { TPayorRecord }

    procedure TPayorRecord.Clear;
    begin
      PAYORUNIQUE := 0;
      PAYORGUID := '';
      MASTERNAME := '';
      MASTERSTREET := '';
      MASTERCITY := '';
      MASTERSTATE := '';
      PAYORTYPESTDC := 0;
      MASTERZIP := '';
      MASTERATTN := '';
      MASTERPHONE := '';
      NEICCODE := '';
      RTCODE := '';
      STATEFILTER := '';
      NEICTYPESTDC := 0;
      PAYORSUBTYPESTDC := 0;
      OWNER := '';
      HIDE := False;
      IGRPUNIQUE := 0;
      FORM := '';
      GOVASSIGN := False;
      CLAIMMAX := 0;
      MEDIGAPCODE := '';
      EMCDEST := '';
      ASSIGNBENEFITS := False;
      BATCHBILL := False;
      MEDIGAPPAYOR := False;
      MEDPLANGUID := '';
      SRXPLANGUID := '';
      PAYPERCENT := 0;
      LOCALNAME := '';
      LOCALSTREET := '';
      LOCALCITY := '';
      LOCALSTATE := '';
      LOCALZIP := '';
      LOCALATTN := '';
      LOCALPHONE := '';
      EHRSIGNOFF := False;
      DISCONTINUED := 0;
      MODIFIED := 0;
      LEGACYPLAN := '';
      LEGACYTYPE := '';
      AUTHORIZE := False;
      DISPENSEUPDATE := False;
    end;

    { TPayors }

    procedure TPayors.Add(const aItem: TPayorRecord);
    begin
      SetLength(Items,Count + 1);
      Items[Count - 1] := aItem;
    end;

    function TPayors.CarriersList: TStrings;
    var
      I: Integer;
    begin
      Result := TStringList.Create;
      Result.Clear;
      SortbyName;
      try
      for I := 0 to Count - 1 do
        Result.Add(Items[I].LOCALNAME);
      finally

      end;
    end;

    procedure TPayors.Free;
    begin
      Items := Nil;
    end;

    function TPayors.GetPayorGuid(const aPAYORUNIQUE: Integer): String;
    var
      Idx:Integer;
    begin
      Result := '';
      Idx := IndexOfPayorUnique(aPAYORUNIQUE);
      if not (Idx = -1) then
        Result := Items[Idx].PAYORGUID;
    end;

    function TPayors.IndexOfIgrpUnique(const aIGRPUNIQUE: Integer): Integer;
    var
      I: Integer;
    begin
      Result := -1;
      for I := 0 to Count - 1 do
        if Items[I].IGRPUNIQUE = aIGRPUNIQUE then
        begin
          Result := I;
          Break;
        end;
    end;

    function TPayors.IndexOfPayorUnique(const aPAYORUNIQUE: Integer): Integer;
    var
      I: Integer;
    begin
      Result := -1;
      for I := 0 to Count - 1 do
        if Items[I].PAYORUNIQUE = aPAYORUNIQUE then
        begin
          Result := I;
          Break;
        end;
    end;

    procedure TPayors.SortByName;
    var
      fSort:TStringList;
      fParse:TStrings;
      I,Idx: Integer;
      fTempPayor:TPayors;
    begin
      fSort := TStringList.Create;
      fParse := TStringList.Create;
      fTempPayor.Items := Self.Items;
      fSort.Sorted := True;
      try
        for I := 0 to Count - 1 do
          fSort.Add(Items[I].LOCALNAME + #9 + IntToStr(I));
        Items := Nil;
        for I := 0 to fSort.Count - 1 do
          begin
            cbs.utils.ParseDelimited(fParse,fSort[I],#9);
            Idx := StrToInt(fParse[1]);
            Add(fTempPayor.Items[Idx]);
          end;
      finally
        fTempPayor.Free;
        fParse.Free;
        fSort.Free;
      end;
    end;

    function TPayors._pGetCount: Integer;
    begin
      Result := Length(Items);
    end;

    end.

1
The error is not in the code you have shownDavid Heffernan
@Mason As well as another question related to a keyboard hook...Jerry Dodge
@JerryDodge Keyboard hooks are not necessarily evil. In that specific case Rich was mistaken in believing that a keyboard hook was needed to handle input events in his own app. There's no sign of malware here, and I think that the comments in response to Rich's question have been way out of line.David Heffernan
@Rich I believe your problem to be valid, but your question is currently not answerable. As I said in my very first comment, the error is not in the code that you show. You need to give us enough detail to be able to diagnose the problem. Clearly it is normal to create an object and then subsequently destroy it. Something else must be interfering.David Heffernan
I guess I should not mention the issue of using FreeAndNil. <g>Rudy Velthuis

1 Answers

6
votes

You are (most probably) double freeing a stringlist (and never freeing at least one). The problem is in the setter of your 'SQL' property (which is backed by the 'FSQL' field):

procedure TPayorDM._psetSQL(const Value: TStringList);
begin
  FSQL := Value;
end;

Here you are loosing the reference to the already existing stringlist (LHS). Consider the below scenario:

You call

PayorDM.SQL := AStringList;

and the reference to the private field you created in the constructor is gone, instead you keep a reference to 'AStringList'. After that at some point you destroy 'AStringList', now the 'FSQL' field is a stale pointer. When in the destructor you call

FSQL.Free;

you get an invalid pointer operation.

Change your setter to:

procedure TPayorDM._psetSQL(const Value: TStringList);
begin
  FSQL.Assign(Value);
end;