Question : Send E-mails in background process (and continue working)

Hi,

As I personilize the E-mails per customer, I need to send them one-by-one.
When 1000 E-mail are send this way, the enduser of my app will have to wait until the all have finished. If attachments are added it will even take much longer.

What I'm looking for is a way to send the E-mail in an optional backgroudprocess.
During sending of the E-mails, the enduser can decide to wait for it all to happen, or to send it to the background and continue other work with the app.

Can anybody supply me with some codesample on how to accomplish this?
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
procedure SendEmails;
var  IdSMTP: TIdSMTP;
     IdMsg: TIdMessage;
     I : Integer;
begin
  IdSMTP:=TIdSMTP.Create(nil);
  IdSMTP.Host:='my.domain.nl'; // replace by your outgoing server
  IdSMTP.Port:=25;
  IdSMTP.Username := 'MyUserName';
  IdSMTP.Password := 'MyPassword';
  try
    if NOT IdSMTP.Connected then
      IdSMTP.Connect;

    For I := 0 to Query1.Recordcount -1 do
    begin
      try
        IdMsg:=TIdMessage.Create(nil);
        IdMsg.From.Address:= edit1.Text;
        IdMsg.Subject:= edit2.text;
        IdMsg.Recipients.EMailAddresses:= Query1.FieldByName('Email').Text;

        // Start some function to personalize body per customer
        IdMsg.Body.Text := PersonalizeEmailForCustomer;
        try
          IdSMTP.Connect;
          IdSMTP.Send(IdMsg);
        except
          Showmessage('Error when sending E-mail');
        end;
      finally
        IdMsg.Free;
      end;
      Query1.Next;
    end;
  finally
    if IdSMTP.Connected then
      IdSMTP.Disconnect;
    IdSMTP.Free;
  end;
end;

Answer : Send E-mails in background process (and continue working)

it creates a new thread for sending emails.

>> Can the user from that moment on do anything within the application?

sure. but there are two things that you have to control:

1. user want to quit from your application but the emails are still sending. In this case you can terminate the thread or wait for its finishing

2. don't change query1, edit1, edit2 because the thread uses them

>> What if an error occurs. Will the user still be able to get this?

Yes, your try/except/showmessage still work:

        try
          IdSMTP.Connect;
          IdSMTP.Send(IdMsg);
        except
          Showmessage('Error when sending E-mail');
        end;

It would be better to wrap all execute method into try/except too like this:

procedure TEmailThread.Execute;
var  IdSMTP: TIdSMTP;
     IdMsg: TIdMessage;
     I : Integer;
begin
  try
    IdSMTP:=TIdSMTP.Create(nil);
    IdSMTP.Host:='my.domain.nl'; // replace by your outgoing server
    IdSMTP.Port:=25;
    IdSMTP.Username := 'MyUserName';
    IdSMTP.Password := 'MyPassword';
    try
      if NOT IdSMTP.Connected then
        IdSMTP.Connect;

      For I := 0 to Query1.Recordcount -1 do
      begin
        if Terminated then Exit;
 
        try
          IdMsg:=TIdMessage.Create(nil);
          IdMsg.From.Address:= edit1.Text;
          IdMsg.Subject:= edit2.text;
          IdMsg.Recipients.EMailAddresses:= Query1.FieldByName('Email').Text;
 
          // Start some function to personalize body per customer
          IdMsg.Body.Text := PersonalizeEmailForCustomer;
         try
            IdSMTP.Connect;
            IdSMTP.Send(IdMsg);
          except
             on E: Exception do
               Showmessage('Error when sending E-mail: ' + E.Message);
          end;
        finally
          IdMsg.Free;
        end;
        Query1.Next;
      end;
    finally
      if IdSMTP.Connected then
        IdSMTP.Disconnect;
      IdSMTP.Free;
    end;
  except
    on E: Exception do
      Showmessage('Error when sending E-mail: ' + E.Message);
  end;
end;

>> Is it still possible to inform the enduser that sending has finished, show some result?

You can use OnTerminate message or

 with TEmailThread.Create(True) do
 begin
   OnTerminate:= ThreadOnTerminate;
   FreeOnTerminate:= True;
   Resume;
 end;

procedure TMainForm.ThreadOnTerminate(ASender: TObject);
begin
  ShowMessage('Job is done');
end;

>> Therefore I need to pass that content to the thread somehow

you can pass to the thread any parameters you like via its constructor. For example:

  TEmailThread = class(TThread)
  private
    FSubject: string;
  protected
    procedure Execute; override;
  public
    constructor Create( const ASubject: string );
  end;

constructor TEmailThread.Create( const ASubject: string );
begin
  inherited Create(True);
  FSubject:= ASubject;
end;

procedure TEmailThread.Execute;
...
        // using local var
        IdMsg.Subject:= FSubject; // was IdMsg.Subject:= edit2.text;
Random Solutions  
 
programming4us programming4us