Stef Merlijn
asked on
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?
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?
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;
ASKER
Thank you. Could you explain how this works?
Suppose I send 100 Emails. Then I start the thread by:
with TEmailThread.Create(True) do
begin
FreeOnTerminate:= True;
Resume;
end;
Can the user from that moment on do anything within the application?
What if an error occurs. Will the user still be able to get this?
Is it still possible to inform the enduser that sending has finished, show some result?
Some issues:
f.e: The Memo containing the content of the Email-body is on the send-form, which will be freed, when user starts working in the app. Therefore I need to pass that content to the thread somehow.
Suppose I send 100 Emails. Then I start the thread by:
with TEmailThread.Create(True) do
begin
FreeOnTerminate:= True;
Resume;
end;
Can the user from that moment on do anything within the application?
What if an error occurs. Will the user still be able to get this?
Is it still possible to inform the enduser that sending has finished, show some result?
Some issues:
f.e: The Memo containing the content of the Email-body is on the send-form, which will be freed, when user starts working in the app. Therefore I need to pass that content to the thread somehow.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
2. don't change query1, edit1, edit2 because the thread uses them
it would be even better if everything is inside the query
and this has it's own connection within the thread
--
sql.Text := 'select :fix1 subject, a.* from table a';
paramByname('SUBJECT').AsS tring := 'subject of email';
--
you can set fixed values inside a query too to accomplish
or pass parameter to the thread
this would make your thread totally independant of any form
and fwiw
don't use edit1.text or edit2.text inside a thread
it would be even better if everything is inside the query
and this has it's own connection within the thread
--
sql.Text := 'select :fix1 subject, a.* from table a';
paramByname('SUBJECT').AsS
--
you can set fixed values inside a query too to accomplish
or pass parameter to the thread
this would make your thread totally independant of any form
and fwiw
don't use edit1.text or edit2.text inside a thread
ASKER
Oke guys, give me some time to do some testing, but it looks very promising.
Thank you for now.
Thank you for now.
ASKER
Suppose the enduser will enter the mailing form again, but before the thread has finished. Is there a way to determine if it is still running?
use you own boolean variable like this:
IsThreadRunning: Boolean;
with TEmailThread.Create(True) do
begin
OnTerminate:= ThreadOnTerminate;
FreeOnTerminate:= True;
IsThreadRunning:= True;
Resume;
end;
procedure TMainForm.ThreadOnTerminat e(ASender: TObject);
begin
IsThreadRunning:= False;
ShowMessage('Job is done');
end;
IsThreadRunning: Boolean;
with TEmailThread.Create(True) do
begin
OnTerminate:= ThreadOnTerminate;
FreeOnTerminate:= True;
IsThreadRunning:= True;
Resume;
end;
procedure TMainForm.ThreadOnTerminat
begin
IsThreadRunning:= False;
ShowMessage('Job is done');
end;
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Did some testing, but I have no experience with contructors and threads, so I get a bit confused...
From My Mail-form I try to start up the thread from within the mainform (as that form will not be destroyed).
// added
Uses MailingPerEmailThread;
procedure TFHoofdscherm.StartMailing PerEmailTh read;
begin
with TMailingPerEmailThread.Cre ate(True) do
begin
OnTerminate:= MailingPerEmailThreadOnTer minate;
FreeOnTerminate:= True;
Resume;
end;
end;
procedure TFHoofdscherm.MailingPerEm ailThreadO nTerminate (ASender: TObject);
begin
MyMessageDlg(Format(blcMes sageInform atie
, [ConstantP])
, blcMailingEmailIsVerzonden
, dkInformatie, NMV);
end;
Error: Undeclared identifier: 'TMailingPerEmailThread'
Also the code below isn't quite correct.
From My Mail-form I try to start up the thread from within the mainform (as that form will not be destroyed).
// added
Uses MailingPerEmailThread;
procedure TFHoofdscherm.StartMailing
begin
with TMailingPerEmailThread.Cre
begin
OnTerminate:= MailingPerEmailThreadOnTer
FreeOnTerminate:= True;
Resume;
end;
end;
procedure TFHoofdscherm.MailingPerEm
begin
MyMessageDlg(Format(blcMes
, [ConstantP])
, blcMailingEmailIsVerzonden
, dkInformatie, NMV);
end;
Error: Undeclared identifier: 'TMailingPerEmailThread'
Also the code below isn't quite correct.
unit MailingPerEmailThread;
interface
uses Classes, IdSMTP, IdMessage;
type
TMailingPerEmailThread = class(TThread)
private
FOnderwerp : String
FVan : String
FBody : String
protected
procedure Execute; override;
public
constructor Create(Const FOnderwerp : String ); overload;
constructor Create(Const FVan : String ); overload;
constructor Create(Const FBody : String ); overload;
end;
implementation
constructor TMailingPerEmailThread.Create(Const AVan: String );
begin
inherited Create(True);
FVan := AVan;
end;
constructor TMailingPerEmailThread.Create(Const ABody: String );
begin
inherited Create(True);
FBody := ABody;
end;
constructor TMailingPerEmailThread.Create(Const AOnderwerp: String );
begin
inherited Create(True);
FOnderwerp := AOnderwerp;
end;
procedure TMailingPerEmailThread.Execute;
begin
// Do my stuff
end;
end.
ASKER
The first part had to do with added the unit to the uses clause of the mainform.
In unit MailingPerEmailThread I have still some problems with constructors.
Error: Method 'Create' with identical parameters already exists
How do I get all variables assigned and available in this unit?
In unit MailingPerEmailThread I have still some problems with constructors.
Error: Method 'Create' with identical parameters already exists
How do I get all variables assigned and available in this unit?
ASKER
Got that solved too. They can be combined into one constructor....
unit MailingPerEmailThread;
interface
uses Classes, IdSMTP, IdMessage;
type
TMailingPerEmailThread = class(TThread)
private
FIsMailingPerEmailBezig : Boolean;
FOnderwerp : String;
FVan : String;
FBody : String;
protected
procedure Execute; override;
public
property IsThreadRunning: Boolean read FIsMailingPerEmailBezig;
constructor Create(Const AOnderwerp, AVan, ABody : String ); overload;
end;
implementation
constructor TMailingPerEmailThread.Create(Const AOnderwerp, AVan, ABody: String );
begin
inherited Create(True);
FOnderwerp := AOnderwerp;
FVan := AVan;
FBody := ABody;
end;
procedure TMailingPerEmailThread.Execute;
begin
FIsMailingPerEmailBezig := True;
// Code
FIsMailingPerEmailBezig := False;
end;
end.
>> Error: Undeclared identifier: 'TMailingPerEmailThread'
It seems, your code is correct. Did you add MailingPerEmailThread to the project? In what line does the error occur?
Your improved unit:
procedure TFHoofdscherm.StartMailing PerEmailTh read;
begin
with TMailingPerEmailThread.Cre ate('Onder werp', 'Van', 'Body') do
begin
OnTerminate:= MailingPerEmailThreadOnTer minate;
FreeOnTerminate:= True;
Resume;
end;
end;
It seems, your code is correct. Did you add MailingPerEmailThread to the project? In what line does the error occur?
Your improved unit:
procedure TFHoofdscherm.StartMailing
begin
with TMailingPerEmailThread.Cre
begin
OnTerminate:= MailingPerEmailThreadOnTer
FreeOnTerminate:= True;
Resume;
end;
end;
unit MailingPerEmailThread;
interface
uses Classes, IdSMTP, IdMessage;
type
TMailingPerEmailThread = class(TThread)
private
FOnderwerp : String;
FVan : String;
FBody : String;
protected
procedure Execute; override;
public
constructor Create(Const AOnderwerp, AVan, ABody: String ); overload;
end;
implementation
constructor TMailingPerEmailThread.Create(Const AOnderwerp, AVan, ABody: String );
begin
inherited Create(True);
FVan := AVan;
FBody := ABody;
FOnderwerp := AOnderwerp;
end;
procedure TMailingPerEmailThread.Execute;
begin
// Do my stuff
end;
end.
Well done, you was faster :)
ASKER
But you were very quick too my friend...
Is it correct to asume that if place this code in the mainform, and pass the content from the editfields as parameter. Like:
Is it correct to asume that if place this code in the mainform, and pass the content from the editfields as parameter. Like:
procedure TFHoofdscherm.StartMailingPerEmailThread(vVan, vOnderwerp, vBody);
begin
with TMailingPerEmailThread.Create(vVan, vOnderwerp, vBody) do
begin
OnTerminate:= MailingPerEmailThreadOnTerminate;
FreeOnTerminate:= True;
Resume;
end;
end;
From the Send-button the the mailingUnit (where the mail is composed).
procedure TFMailingUnit.btnSendMyEmailClick(Sender: TObject);
begin
TFHoofdscherm.StartMailingPerEmailThread(EditVan.Text, EditOnderwerp.Text, MemoBody.Lines.Text);
end;
yes, that's ok
but
procedure TFHoofdscherm.StartMailing PerEmailTh read(vVan, vOnderwerp, vBody: string);
but
procedure TFHoofdscherm.StartMailing
ASKER
Oke, whole thing is compiling now, but I haven't figured out how and where I pass the variables to the thread.
Somewhere I need to assign content to vVan, vOnderwerp, vBody
Somewhere I need to assign content to vVan, vOnderwerp, vBody
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Oke it works now.
Remains one little thing that I asked at the beginning:
"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."
So the user has pressed the sendbutton and decides not wanting to wait anymore. Duration of sending an E-mail might differ a lot, when f.e. larger attachments are added.
Remains one little thing that I asked at the beginning:
"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."
So the user has pressed the sendbutton and decides not wanting to wait anymore. Duration of sending an E-mail might differ a lot, when f.e. larger attachments are added.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I believe we have a winner...Thank you very much for all your time and effort. I'm very happy with the result.
You're welcome
Using:
uses
EmailThread;
procedure TForm1.Button2Click(Sender
begin
with TEmailThread.Create(True) do
begin
FreeOnTerminate:= True;
Resume;
end;
end;
Open in new window