Delphi7 send email in different thread and unit

I want to send email in other unit with different thread with indy10.0.52 I have source code

unit ThreadEmail;

interface

uses Classes, SysUtils, IdGlobal, IdMessage, IdIOHandler, IdIOHandlerSocket,
  IdSSLOpenSSL, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdMessageClient, IdSMTP, IdExplicitTLSClientServerBase, IdSMTPBase,
  IdIOHandlerStack, IdSSL, ExtCtrls;

type
  TThreadEmail = class(TThread)
  private
    run      : boolean;
    counter  : Integer;
    target   : Integer;
    IdSMTP: TIdSMTP;
    IdSSLIOHandlerSocketOpenSSL: TIdSSLIOHandlerSocketOpenSSL;
    Messages : Array [0..10] of TIdMessage;
    procedure checkRun();
  protected
    procedure Execute; override;
  public
    constructor Create(timerInS:Integer;host:string;port:integer;username,password:String;readTimeout : integer = 0);reintroduce;
    function expressSend(recipients,subject,body:string;from:String='';replayTo:String='') :boolean;
    function makeEmail(recipients,subject,body:string;from:String='';replayTo:String=''): boolean;
    procedure SendAllEmail();
  end;

implementation

constructor TThreadEmail.Create(timerInS:Integer;host:string;port:integer;username,password:String;readTimeout : integer = 0);
var b: byte;
begin
  inherited Create(False);
  Priority:= tpNormal;
  FreeOnTerminate:= True;

  IdSMTP := TIdSMTP.Create;
  IdSSLIOHandlerSocketOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create();
  for b:=low(Messages) to high(messages) do Messages[b] := nil;

  IdSMTP.IOHandler := IdSSLIOHandlerSocketOpenSSL;
  IdSMTP.UseTLS    := utUseImplicitTLS;
  IdSMTP.Host      := host;
  IdSMTP.Port      := port;
  IdSMTP.Username  := username;
  IdSMTP.Password  := password;

  IdSSLIOHandlerSocketOpenSSL.DefaultPort   := 0;
  IdSSLIOHandlerSocketOpenSSL.Destination   := host+':'+inttostr(port);
  IdSSLIOHandlerSocketOpenSSL.Host          := host;
  IdSSLIOHandlerSocketOpenSSL.MaxLineAction := maException;
  IdSSLIOHandlerSocketOpenSSL.Port          := port;
  IdSSLIOHandlerSocketOpenSSL.ReadTimeout   := readTimeout;
  IdSSLIOHandlerSocketOpenSSL.SSLOptions.Method  := sslvSSLv3;
  IdSSLIOHandlerSocketOpenSSL.SSLOptions.Mode    := sslmClient;

  run:=true;
  //target := timerInS*10;
end;

function TThreadEmail.expressSend(recipients,subject,body:string;from:String='';replayTo:String='') : boolean;
var IdMessage: TIdMessage;
begin
  Result := false;
  IdMessage := TIdMessage.Create();
  IdMessage.Recipients.EMailAddresses := recipients;
  IdMessage.Subject := subject;
  IdMessage.Body.Text := body;
  if from <> '' then IdMessage.From.Address := from;
  if replayTo <> '' then IdMessage.ReplyTo.EMailAddresses := from;
  try
    IdSMTP.Connect();
    IdSMTP.Send(IdMessage);
    Result := true;
  finally
    IdSMTP.Disconnect();
  end;
end;

function TThreadEmail.makeEmail(recipients,subject,body:string;from:String='';replayTo:String='') : boolean;
var b: byte;
begin
  Result := false;
  for b:=low(Messages) to high(messages) do
    if Messages[b] = nil then
    begin
      Result := true;
      Messages[b]:= TIdMessage.Create();
      Messages[b].Recipients.EMailAddresses := recipients;
      Messages[b].Subject := subject;
      Messages[b].Body.Text := body;
      if from <> '' then Messages[b].From.Address := from;
      if replayTo <> '' then Messages[b].ReplyTo.EMailAddresses := from;
    end;
  if not(result) then
  begin
    SendAllEmail();
    makeEmail(recipients,subject,body,from,replayTo);
  end;
end;

procedure TThreadEmail.SendAllEmail();
var b: byte;
begin
  try
    IdSMTP.Connect();
    for b:=low(Messages) to high(messages) do
      if run and (Messages[b] <> nil) then
      begin
        try
          IdSMTP.Send(Messages[b]);
        finally
          Messages[b].Free;
          Messages[b] := nil;
        end
      end;
    finally
      IdSMTP.Disconnect();
    end;
end;

procedure TThreadEmail.checkRun();
begin
  Dec(counter);
  if counter <= 0 then SendAllEmail();
end;

procedure TThreadEmail.Execute;
var b: byte;
begin
  while run do
  begin
    sleep(100);
    checkRun();
  end;
  IdSMTP.Free;
  IdSSLIOHandlerSocketOpenSSL.Free;
  for b:=low(Messages) to high(messages) do
    if Messages[b] <> nil then Messages[b].Free;
end;

end.

and in mainfrom that i create

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,  ThreadEmail;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
implementation

{$R *.dfm}


procedure TForm1.Button1Click(Sender: TObject);
var   ThreadEmail : TThreadEmail;
begin
  ThreadEmail := ThreadEmail.Create(10,'smtp.gmail.com',465,'xxx.gmail.com','xxx',2000);
  ThreadEmail.expressSend('[email protected]','TES','TES');
end;

When button1 clicked, it always "create access violation error", Why it happend? can anyone help me? as a info, i sucesed send a email before, but i want to make a singgle unit that can run alone. thanks

Upvotes: 0

Views: 1091

Answers (1)

J...
J...

Reputation: 31453

ThreadEmail := ThreadEmail.Create(10,'s....

this should be :

ThreadEmail := TThreadEmail.Create(10,'s....

Not sure if that's just a typo? It will definitely cause an AV if not.


In any case, ThreadEmail.expressSend will not run in your TThread's thread the way you are calling it. When you run a TThread the code in its Execute method will run in a separate thread. Any of the public member methods, however, can be called on an instance just like public methods of any class and they are executed on the thread that calls them.

To get this to work you need to have the Execute method performing the calls to send the email message. The UI thread needs to trigger action in the Execute method and not perform the action itself; this can be done by any number of means (having Execute synchronize with WaitForSingleObject, via message passing, etc).

The rest of your code looks rather broken. Your Execute code is not really going to work as it is - this loop :

while run do
begin
  sleep(100);
  checkRun();
end;

will never terminate as it seems you don't set run to false anywhere. Furthermore, counter does not seem to get set anywhere (nor do I really understand its purpose) so this will just SendAllEmail() every 100ms or so.

The makeEmail function will never terminate (stack overflow) since it calls itself recursively with the original arguments and the logic guarantees re-entry on each pass. It also looks like it will send whatever message eleven times on each recursion (since all 11 elements of Messages will be nil after initialization and after each call to SendAllEmail().

Even if you fix this - if you are calling makeEmail externally (ie: from the UI or another thread) then this will likely end up with all sorts of cross-thread errors since both Execute and the calling thread will be trying to call SendAllEmail at the same time. This code will need some work.

Upvotes: 5

Related Questions