Search code examples
delphipointersqueuerecord

What is the correct way to store record pointers in a Tqueue in Delphi


I'm trying to store a pointer to a record in a Tqueue, then dequeue the pointer later and extract the data but am getting in a muddle with the pointers and keep getting a 'Abstract Error'

Can anyone please see what I am doing wrong and advise me on the correct solution?

(BTW, Initially I had it all without the ^ but then realised my mistake but was surprised that it still gave an error)

The record holds email data that gets sent to a smtp server, It uses a TstringList to hold each line of the body and another one to hold each attachment filename

This is the record structure used to store the email data

TPtrEmailData  = ^TEmailDataRec;
TEmailDataRec = record
                ToAddr      : string; //one email address
                CcAddr      : string; //addresses delimitated by semicolons
                BccAddr     : string; //addresses delimitated by semicolons
                Subject     : String; 
                Body : TStrings; //each string contains one line of the body
                attachments: TStrings;//each string contains a filename 
                end;

To create the records I use

function TFrmSendEmail.CreateNewEmailRec: TPtrEmailData;
var
   EmailRecPtr : TPtrEmailData;
begin
 new(EmailRecPtr);   //make a new record
 EmailRecPtr^.Body := Tstrings.Create ;
 EmailRecPtr^.attachments := Tstrings.create;
 result := EmailRecPtr ;
end;

and to free them after dequeing I use

procedure TFrmSendSllSmtptEmail.DestroyEmailRec(EmailRecPtr : TPtrEmailData);
 //frees memory for the Tstrings and then frees the record
begin
 freeandnil(EmailRecPtr^.Body); //free one Tstringlist
 FreeAndNil(EmailRecPtr^.attachments); //and the other
 FreeAndNil(EmailRecPtr ); //now free the precord pointer
end;

CreateNewEmailRec is called when I enqueue a new record pointer in the queue using the following, passing in the memo and list box containig th ebody and attachments. This is where I get the error.

procedure TFrmSendEmail.AddToEmailQueue(ToAddr, CCAddr,
                            BccAddr,Subject:String;  
                            Body: Tmemo; Attachments: TListBox);
 var
 i : integer;
 s : string;
 EmailRecPtr : TPtrEmailData;
 begin
 EmailRecPtr := CreateNewEmailRec; //allocate memory 
                                   //deallocated in RemoveFromEmailQueue
 EmailRecPtr^.ToAddr := ToAddr;
 EmailRecPtr^.CCAddr := CCAddr;
 EmailRecPtr^.BccAddr := BccAddr;
 for I := 0 to Attachments.Count - 1 do
     begin
     s := Attachments.Items[i];
     EmailRecPtr^.attachments.add(s );  <---- !!! get abstract error here
     end;
 for I := 0 to Body.lines.Count - 1 do
     begin
     s := Body.lines[i];
     EmailRecPtr^.Body.Add(s) ;
     end;
 EmailQueue.Enqueue(EmailRecPtr );
end;

and DestroyEmailRec is called when I dequeue a pointer to use the data in

procedure TFrmSendEmail.RemoveFromEmailQueue(var ToAddr,
                                                     CCAddr,
                                                     BccAddr,
                                                     Subject: String;
                                                     var Body,
                                                     Attachments: TStringlist);
var
  EmailRecPtr :TPtrEmailData;
  i : integer;
  s : string;
begin
if  EmailQueue.Count > 0 then
   begin
   Body.Clear;
   Attachments.Clear;

   EmailRecPtr := EmailQueue.Dequeue; //get pointer to next record
   ToAddr := EmailRecPtr^.ToAddr;  //populate procedure parameters
   CCAddr := EmailRecPtr^.CCAddr;
   BccAddr := EmailRecPtr^.BccAddr;
   for EmailRecPtr^.attachments.Count - 1 do
       begin
       s := EmailRec^.attachments[i];
       Attachments.Add(s) ;
       end;
   for I := 0 to EmailRecPtr ^.Body.Count - 1 do
       begin
       s := EmailRecPtr ^.Body[i];
       Body.Add(s);
       end;

   DestroyEmailRec(EmailRecPtr);  //release memory
end;

The call to RemoveFromEmailQueue passes in a couple of created TStringLists

TheBody := Tstringlist.Create ;
TheAttachments := Tstringlist.create;
try
   RemoveFromEmailQueue(ToAddr, CCAddr, BccAddr, Subject,TheBody,TheAttachments);
// do stuff with the data;
finally
   TheBody.Free;
   TheAttachments.Free;
end;

Oh, and the queue is declared as

var
     EmailQueue : Tqueue<TPtrEmailData>;

Solution

  • You get the "Abstract Error" because you use an astract object (TStrings)! In the TFrmSendEmail.CreateNewEmailRec method replace TStrings with TStringList:

    function TFrmSendEmail.CreateNewEmailRec: TPtrEmailData;
    begin
     new(result);   //make a new record
     Result^.Body := TStringList.Create ;
     Result^.attachments := TStringList.create;
    end;
    

    Also, you can't free records using FreeAndNil! So your method to free the record should be like

    procedure TFrmSendSllSmtptEmail.DestroyEmailRec(EmailRecPtr : TPtrEmailData);
     //frees memory for the Tstrings and then frees the record
    begin
     EmailRecPtr^.Body.Free; //free one Tstringlist
     EmailRecPtr^.attachments.Free; //and the other
     Dispose(EmailRecPtr); //now free the precord pointer
    end;