Tratamento de erros no envio de email

Delphi

02/12/2006

Oi pessoal,

Estou fazendo uma aplicação que, dentre outras características, envia emails usando o Indy 9.0.19 no Deliphi 7.

Consigo enviar alguns emails sem problemas, só que, quando algo anormal acontece, a aplicação pára e me mostra uma janela de erro. Alguns exemplos de mensagens são:

-------------------------------------------------
Quando diversos emails são enviados:
´Project Agenda.exe raised exception class EIdProtocolReplyError with message ´socom3.uol.com.br Error: too many connections from 201.44.201.83

Email não existente:
´Project Agenda.exe raised exception class EIdProtocolReplyError with message ´<rwaller12345@uol.com.br>: Recipient address rejected: User unknown in relay recipient table

Dominio não existente:
´Project Agenda.exe raised exception class EIdProtocolReplyError with message ´<rwaller@uol12345.com.br>: Recipient address rejected: Domain not found
-------------------------------------------------

Gostaria que esta janela não fosse mostrada, e sim me informar, de preferência com o erro, para que o usuário possa corrigí-lo.

Também gostaria de entender o que significa a primeira mensagem de erro e como evitá-la (too many connections from 201.44.201.83). Aqui uso Vírtua e pop3/smtp da Uol.

A função que estou utilizando é:
-------------------------------------------------
function TfrmEnviaEmail.Envia(Sender:TObject; Para:String):Boolean;
var
Mensagem: TIdMessage;

begin
Mensagem := TIdMessage.Create(Self);

if (Para <> ´´) then begin
idPOP31.UserName:=idSMTP1.UserName;
idPOP31.Password:=idSMTP1.Password;

// Define os parâmetros da mensagem
Mensagem.ContentType := ´text´;
Mensagem.Recipients.Add.Address := Para;
Mensagem.From.Text := ´rwaller@uol.com.br´ ;
Mensagem.Subject := ´[Teste] Envio pela aplicação´;
Mensagem.Body.AddStrings(mMensagem.Lines);
Mensagem.Headers.Values[´X-Library´] := ´´;

// Faz a conexão com o servidor e envia a mensagem
if not (IdSMTP1.Connected) then IdSMTP1.Connect();

idPOP31.Connect;

Envia:=False;
Try
IdSMTP1.Send(Mensagem);
Except
End;

if IdSMTP1.Connected then Begin
Envia:=True;
IdSMTP1.Disconnect;
End;

if IdPOP31.Connected then
IdPOP31.Disconnect;
End;

Mensagem.Free;
end;
-------------------------------------------------

Creio que já pesquisei os todos os tópicos deste fórum sobre este assunto. Inclusive foi por isso que instalei a última versão do Indy. Quanto à conectar no Pop antes do Smtp (dica de outro tópico), creio que não vi diferença.

Gostaria MUITO que pudéssem me ajudar, se for possível.

Obrigado :)

Abraços,
Rudolf


Rudolf Waller

Rudolf Waller

Curtidas 0

Respostas

Massuda

Massuda

02/12/2006

[quote:6231382b7d=´Rudolf Waller´]´Project Agenda.exe raised exception class EIdProtocolReplyError with message ´<rwaller12345@uol.com.br>: Recipient address rejected: User unknown in relay recipient table

´Project Agenda.exe raised exception class EIdProtocolReplyError with message ´<rwaller@uol12345.com.br>: Recipient address rejected: Domain not found[/quote:6231382b7d]Esse tipo de exceção deveria ser capturado num try..except e ser tratada adequadamente.

[quote:6231382b7d=´Rudolf Waller´]Gostaria que esta janela não fosse mostrada, e sim me informar, de preferência com o erro, para que o usuário possa corrigí-lo.[/quote:6231382b7d]Pelo código que você postou, essa janela só está aparecendo porque você está executando o programa de dentro da IDE do Delphi. se executar o programa a partir do Windows, provavelmente nada será exibido. Isso porque você pos um try..except ao redor do Send que está feito para engulir silenciosamente toda exceção. Quando você implementar o tratamento da exceção, note que a exceção possui uma propriedade ReplyErrorCode, que é um Integer, mais fácil de tratar que a string contida na propriedade Message da exceção.

[quote:6231382b7d=´Rudolf Waller´]´Project Agenda.exe raised exception class EIdProtocolReplyError with message ´socom3.uol.com.br Error: too many connections from 201.44.201.83[/quote:6231382b7d]Esse erro é estranho. Pelo seu código, isso só ocorreria se você estiver executando o envio de email dentro de um loop. É o caso?

Eu prefiro esta versão do seu código...
function  TfrmEnviaEmail.Envia(Sender:TObject; Para:String):Boolean;
var
  Mensagem: TIdMessage;
begin
  if (Para <> ´´)  then begin

    Mensagem := TIdMessage.Create(nil);
    try
      // Define os parâmetros da mensagem
      Mensagem.ContentType := ´text´;
      Mensagem.Recipients.Add.Address := Para;
      Mensagem.From.Text := ´rwaller@uol.com.br´ ;
      Mensagem.Subject   := ´[Teste] Envio pela aplicação´;
      Mensagem.Body.AddStrings(mMensagem.Lines);
      Mensagem.Headers.Values[´X-Library´] := ´´;

      idPOP31.UserName:=idSMTP1.UserName;
      idPOP31.Password:=idSMTP1.Password;

      idPOP31.Connect;
      try
        IdSMTP1.Connect;

        Envia:=False;
        Try
          IdSMTP1.Send(Mensagem);
          Envia:=True;
        Except
          on e: EIdProtocolException do begin
            // trata a exceção
          end;
        End;

        IdSMTP1.Disconnect;
      finally
        IdPOP31.Disconnect;
      end;
    finally
      Mensagem.Free;
    end;
  End;
end;
Evite ficar desnecessariamente conectado no servidores POP e SMTP.

[quote:6231382b7d=´Rudolf Waller´]Quanto à conectar no Pop antes do Smtp (dica de outro tópico), creio que não vi diferença.[/quote:6231382b7d]Muitos provedores de acesso exigem isso para poder usar o servidor SMTP.


GOSTEI 0
Rudolf Waller

Rudolf Waller

02/12/2006

Grande Massuda,

Antes de mais nada, obrigado pela força :)

[quote:6c204789a1=´Rudolf Waller´]Gostaria que esta janela não fosse mostrada, e sim me informar, de preferência com o erro, para que o usuário possa corrigí-lo.
Pelo código que você postou, essa janela só está aparecendo porque você está executando o programa de dentro da IDE do Delphi.[/quote:6c204789a1]
Sim, estou dentro da IDE. Posso estar enganado (não conheço muito Delphi), mas me parece que, mesmo mostrando a janela via raise, o programa não entra no except. É assim mesmo? Tem como sempre entrar (supondo que haja uma exceção), mesmo estando na IDE?

Relendo o que você escreveu e tentando entender o help do try...except, acho que não coloquei a exceção certa. Por isso não interpretou o que coloquei no except anteriormente. Vou checar isso melhor...
se executar o programa a partir do Windows, provavelmente nada será exibido.

Vou fazer um teste agora mesmo
Isso porque você pos um try..except ao redor do Send que está feito para engulir silenciosamente toda exceção.

Interessante! Gostei :)
Quando você implementar o tratamento da exceção, note que a exceção possui uma propriedade ReplyErrorCode, que é um Integer, mais fácil de tratar que a string contida na propriedade Message da exceção.

Na sugestão que você postou, esta propriedade seria e.ReplyErrorCode?

Existem diversas exceções no Indy. Eu teria que fazer uma análise como:
on e: EIdProtocolException do ... // Da sua sugestão
on e: EIdProtocolReplyError do ... // Uma das exceções

e assim por diante? Ou tem como, a partir do valor de uma única propriedade, fazer esta análise?
[quote:6c204789a1=´Rudolf Waller´]´Project Agenda.exe raised exception class EIdProtocolReplyError with message ´socom3.uol.com.br Error: too many connections from 201.44.201.83
Esse erro é estranho. Pelo seu código, isso só ocorreria se você estiver executando o envio de email dentro de um loop. É o caso?
[/quote:6c204789a1]
Sim, é o caso. Pelo que entendi da sua resposta, posso tratar esta exceção e re-enviar o email.
Eu prefiro esta versão do seu código...

Vou fazer um teste agora mesmo :)

Massuda, já trabalhei muito com o Turbo Pascal, mas conheço muito pouco de Delphi. Ontem preferi copiar o Indy para outra pasta para poder alterá-lo, ao invés do original. Feito isso, passei a tentar entender o que faz e alterá-lo para que não gere uma exceção. Aparentemente está funcionando bem, apesar de não ter certeza que as alterações que fiz estão 100¬ confiáveis. Como costuma dizer um amigo: ´Não é porque funciona que está certo´ :)

Isso foi bom que me permitiu entender um pouco de como funciona o SMTP. Acho que estou preferindo seguir o seu conselho e usar o código original, feito por profissionais que entendem do assunto, e que está mais do que debugado, ao contrário do que fiz.

De qualquer maneira, aquela função ficou assim:
function  TfrmEnviaEmail.Envia(Sender:TObject; Para:String):Boolean;
var
  Mensagem: TIdMessage;
  I:Integer;
  x:string;

begin
  Mensagem := TIdMessage.Create(Self);

  if (Para <> ´´)  then begin
    // Define os parâmetros da mensagem
    Mensagem.ContentType := ´text´;
    Mensagem.Recipients.Add.Address := Para;
    Mensagem.From.Text := ´rwaller@uol.com.br´ ;
    Mensagem.Subject   := ´[Teste]´;
    Mensagem.Body.AddStrings(mMensagem.Lines);
    Mensagem.Headers.Values[´X-Library´] := ´´;

    // Faz a conexão com o servidor e envia a mensagem
    Repeat
       IdSMTP1.Connect(1000);
       If not (IdSMTP1.Connected) Then Begin
         lblNumErros.Caption:=IntToStr(StrToInt(lblNumErros.Caption)+1);
         Application.ProcessMessages;
       End;
    Until IdSMTP1.Connected Or Cancela;

    If Cancela Then Begin
      Result:=False;
      Exit;
    End;

    IdSMTP1.Send(Mensagem);

    if IdSMTP1.Connected then
      IdSMTP1.Disconnect;

    Res:=idSMTP1.LastCmdResult.Text.Text;
    Num:=idSMTP1.LastCmdResult.NumericCode;
  End;

  Mensagem.Free;

  Result:=Copy(Res,1,2)=´Ok´;
end;

Nenhuma exceção nesta manga, nem na outra também :) Só não posso garantir que todas as alterações que fiz estão certas.
Evite ficar desnecessariamente conectado no servidores POP e SMTP.

Com certeza :)

Massuda, novamente, obrigado pela força :)

Abraços,
Rudolf


GOSTEI 0
Rudolf Waller

Rudolf Waller

02/12/2006

Grande Massuda,

Acabei de testar a sua sugestão com o Indy original.

Vamos aos resultados:

Pelo código que você postou, essa janela só está aparecendo porque você está executando o programa de dentro da IDE do Delphi.

Confere! A janela só aparece dentro da IDE. Tem como não aparecer lá dentro, mesmo que seja numa etapa de teste? Ficaria mais próximo da aplicação final.
[quote:11888344ed=´Rudolf Waller´]Sim, estou dentro da IDE. Posso estar enganado (não conheço muito Delphi), mas me parece que, mesmo mostrando a janela via raise, o programa não entra no except. É assim mesmo? Tem como sempre entrar (supondo que haja uma exceção), mesmo estando na IDE?
[/quote:11888344ed]
A exceção estava errada. Me baseei no que você postou e já corrigi. Ficou uma dúvida: Tive que colocar na unha a unit idException na cláusula Uses, pois senão o Delphi não acharia as exceções do Indy.

É assim mesmo? Quero dizer, tenho que localizar qual unit tem as exceções (supondo que eu tenha o fonte do componente) e digitar o nome desta unit? E se eu não tiver o fonte? Ou estou fazendo o caminho mais difícil? (novamente, (ainda) sou leigo em Delphi)
Relendo o que você escreveu e tentando entender o help do try...except, acho que não coloquei a exceção certa. Por isso não interpretou o que coloquei no except anteriormente. Vou checar isso melhor...

Corrigido
se executar o programa a partir do Windows, provavelmente nada será exibido.

Confere! Gostei deste rapaz! Cada dia que passa mais me admiro com o Delphi :)
Quando você implementar o tratamento da exceção, note que a exceção possui uma propriedade ReplyErrorCode, que é um Integer, mais fácil de tratar que a string contida na propriedade Message da exceção.

Criei diversas exceções, inclusive analisando o ReplyErrorCode. Valeu!
[quote:11888344ed=´Massuda´] Eu prefiro esta versão do seu código...

Vou fazer um teste agora mesmo :)
[/quote:11888344ed]
Funfando joinha. Agora vamos aos detalhes :)

Massuda, novamente, obrigado pela força :)

Abraços,
Rudolf


GOSTEI 0
Massuda

Massuda

02/12/2006

[quote:6a4da650c8=´Rudolf Waller´]A janela só aparece dentro da IDE. Tem como não aparecer lá dentro, mesmo que seja numa etapa de teste? Ficaria mais próximo da aplicação final.[/quote:6a4da650c8]Na maioria das versões do Delphi vá em [b:6a4da650c8]Tools|Debugger options[/b:6a4da650c8], selecione a aba [b:6a4da650c8]Language exceptions[/b:6a4da650c8] e desmarque a opção [b:6a4da650c8]Stop on Delphi exceptions[/b:6a4da650c8]

[quote:6a4da650c8=´Rudolf Waller´]Tive que colocar na unha a unit idException na cláusula Uses, pois senão o Delphi não acharia as exceções do Indy. É assim mesmo?[/quote:6a4da650c8]Sim. O Delphi inclui automaticamente apenas o mínimo de units necessárias para cada componente que você põe num form/data module; qualquer outra unit que você precise você tem que incluir manualmente.


GOSTEI 0
Rudolf Waller

Rudolf Waller

02/12/2006

Na maioria das versões do Delphi vá em [b:cd438dd7d9]Tools|Debugger options[/b:cd438dd7d9], selecione a aba [b:cd438dd7d9]Language exceptions[/b:cd438dd7d9] e desmarque a opção [b:cd438dd7d9]Stop on Delphi exceptions[/b:cd438dd7d9]

Feito

Massuda, a aplicação tá rodando belezinha, mas fiquei com uma pulga atrás da orelha: Ontem escondi as exceções do Indy e hoje as estou tratando. Ontem estava enviando mais de um email por segundo (média), hoje está demorando alguns segundos. Perguntinha bem genérica: o que pode estar acontecendo? Faz sentido isso?

Abraços,
Rudolf


GOSTEI 0
Massuda

Massuda

02/12/2006

[quote:234bed95ca=´Rudolf Waller´]... Ontem estava enviando mais de um email por segundo (média), hoje está demorando alguns segundos...[/quote:234bed95ca]Você está mandando emails em um loop? Se for, mude o código para conectar com os servidores, mandar os emails e desconectar dos servidores. Os códigos postados aqui conectam/desconectam dos servidores a cada envio de mensagem.


GOSTEI 0
Rudolf Waller

Rudolf Waller

02/12/2006

[quote:98841361fe=´Rudolf Waller´]... Ontem estava enviando mais de um email por segundo (média), hoje está demorando alguns segundos...
Você está mandando emails em um loop? Se for, mude o código para conectar com os servidores, mandar os emails e desconectar dos servidores. Os códigos postados aqui conectam/desconectam dos servidores a cada envio de mensagem.[/quote:98841361fe]

Grande Massuda,

Tentei enviar 10 emails por conexão, e não fez quase diferença. Coloquei um Tmemo para mostrar os enventos de status do idSMTP. A aplicação demora muito em ´encoding text´, praticamente os 5s do envio estão aqui.

Tem alguma coisa que posso fazer para otimizar isso? Como informação extra, o email tem uns 12Kb em modo texto (não é HTML).

Valeu novamente :)

Abraços,
Rudolf


GOSTEI 0
Rudolf Waller

Rudolf Waller

02/12/2006

Grande Massuda,

Achei o erro, e já está corrigido!

Valeu pela força :)

Abraços,
Rudolf


GOSTEI 0
Massuda

Massuda

02/12/2006

Apenas curiosidade... se não for segredo, o que era?


GOSTEI 0
Rudolf Waller

Rudolf Waller

02/12/2006

Apenas curiosidade... se não for segredo, o que era?

Grande Massuda,

Na versão original, criei o TIdMessage cada vez que entrava na rotina. Para facilitar, coloquei um componente idMessage no form.

Como cada vez que entrava na rotina ele anexava mais um destinatário, a lista ia aumentando cada vez mais. Num teste que fiz enviando para meu próprio email, ao invés de receber 100 emails recebi mais de 5000 :O

Nestas hora me lembro de uma frase de humor que rolava nas conversas do BBS: ´Definitivamente este era o último bug´ :D

Abraços,
Rudolf


GOSTEI 0
POSTAR