【问题标题】:Can't send email using Indy, freezes program无法使用 Indy 发送电子邮件,冻结程序
【发布时间】:2014-07-08 08:41:04
【问题描述】:

当我尝试使用 indy 发送带有附件的电子邮件时,我的程序冻结了,我不知道为什么。 这是我用于发送电子邮件的表单的完整代码。

unit Dok_sutisana;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdMessage, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, IdExplicitTLSClientServerBase, IdMessageClient, IdSMTPBase,
IdSMTP, StdCtrls, Buttons, ComCtrls, IdAttachmentFile;

type
TForm14 = class(TForm)
  Edit1: TEdit;
  Edit2: TEdit;
  Label1: TLabel;
  Label2: TLabel;
  BitBtn1: TBitBtn;
FontDialog1: TFontDialog;
RichEdit1: TRichEdit;
IdSMTP1: TIdSMTP;
IdMessage1: TIdMessage;
BitBtn2: TBitBtn;
procedure BitBtn2Click(Sender: TObject);
procedure BitBtn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
 end;

var
 Form14: TForm14;

 implementation

 uses Autentif, EDGA;

{$R *.dfm}

procedure TForm14.BitBtn1Click(Sender: TObject);
begin
if FontDialog1.Execute() then
RichEdit1.Font:=FontDialog1.Font;
end;

procedure TForm14.BitBtn2Click(Sender: TObject);
var s:string;
begin
form3.ADOTable1.Active:=true;
//setup SMTP
IdSMTP1.Host := form3.adotable1['smtp'];
IdSMTP1.Port := form3.adotable1['ports'];
IdSMTP1.Username:= '******@gmail.com';
IdSMTP1.Password:='******';

//setup mail message
IdMessage1.From.Address := form3.adotable1['e-pasts'];
IdMessage1.From.Name:= form3.adotable1['Vards']+' '+ form3.adotable1['Uzvards'];
IdMessage1.Recipients.EMailAddresses := edit1.Text;;

IdMessage1.Subject := edit2.Text;
IdMessage1.Body.Add(RichEdit1.Text + form3.ADOTable1['paraksts']);
s:= GetCurrentDir + form1.ADOTable1['Dok_adr'];
TIdAttachmentFile.Create(IdMessage1.MessageParts, s ) ;

//send mail

IdSMTP1.Connect() ;
IdSMTP1.Send(IdMessage1) ;
IdSMTP1.Disconnect;
IdMessage1.Free;
IdSMTP1.Free;

form3.ADOTable1.Active:=false;
Form14.Close;
end;

end.

我希望这能帮助我解决我的问题。

【问题讨论】:

  • 您是否尝试过使用调试器单步执行您的代码?可能有助于准确了解锁定的步骤。您还需要尝试...最后围绕连接/断开连接和内存创建/释放。可能是您第一次连接但不断开连接,然后您将不得不等待连接超时。
  • @DavidG 锁定发生在 IdSMTP 中的 TIdSMTP.Connect 函数中
  • 当我使用这个库时,我有额外的行:“IdSMTP1.NeedAuthentication := false;”和“IdSMTP1.UseTLS := emNOTLSSupport”。我怀疑你可以在连接之前尝试第一行,看看会发生什么。
  • 在“IdSMTP1.Connect”行之前添加“IdSMTP1.NeedAuthentication := false;”。请注意,您的代码无法使用“Connect()”编译,因为这不是有效的 delphi pascal。您需要提供实际代码。可能是问题被搁置的原因。
  • @DavidG 我到底需要在哪里添加它们? (我对这一切知之甚少)

标签: delphi delphi-2010 email-attachments indy


【解决方案1】:

Indy 使用阻塞操作,并且您在主 UI 线程的上下文中使用 Indy。因此,当TIdSMTP 很忙时,您的主线程会被阻止处理新消息,从而出现应用程序冻结的现象,直到TIdSMTP 完成。

为避免冻结,您可以:

  1. (首选解决方案)将您的 TIdSMTP 代码移动到单独的工作线程中。

  2. TIdAntiFreeze 组件拖放到您的表单上。这将允许主消息队列在其他 Indy 组件在主线程中运行时继续处理新消息。

为了更好地衡量,您还应该设置TIdSMTP.ConnectTimeoutTIdSMTP.ReadTimeout 属性,以便Connect()Send() 不会长时间阻塞。如果发生超时,将引发适当的异常以中止操作。

【讨论】:

  • 我添加了TIdAntiFreeze 并尝试再次发送电子邮件。该程序仍然无法运行,但我可以按下发送按钮并引发异常,即EIdAlready connected,并且程序无法继续。
  • EIdAlreadyConnected 在您调用 Connect() 时引发,而 Connected 为真。请记住,如果IOHandler.InputBuffer 中有未读数据,即使底层套接字关闭,Connected 也可能为真。因此,如果您需要在活动连接期间关闭套接字然后重新连接,请确保在再次调用Connect() 之前清除InputBuffer
  • 如果连接成功,为什么没有发送消息?
  • 鉴于您迄今为止提供的详细信息有限,无法回答。实际冻结发生在哪里?在Connect()Send() 上?您要连接的实际 Port 是什么?
  • 问题可能出在安全性上,我使用来自this question的代码运行我的程序
【解决方案2】:

我从您的代码中删除了对 ADO 的使用,并为我的电子邮件帐户使用了硬编码值(出于安全原因,此处已删除 - 您可以将占位符替换为工作帐户)。

此代码有效 - 它发送附件。

我也:

  • 在连接周围添加了一个try / finally,以确保在发送抛出异常时释放它;
  • 添加了 Lemy 提到的 ConnectTimeout 选项;
  • 已清除电子邮件正文。在添加新内容之前(否则您最终会得到多个测试副本);
  • 还需要删除以前的附件,否则您的第二封电子邮件最终会带有第一封电子邮件的附件

    IdSMTP1.Host := '******';
    IdSMTP1.Port := 25;
    IdSMTP1.Username:= '******@gmail.com';
    IdSMTP1.Password:='******';
    IdMessage1.From.Address := '******';
    IdMessage1.From.Name:= '******';
    IdMessage1.Recipients.EMailAddresses := '******';
    IdMessage1.Subject := '******';
    IdMessage1.Body.Clear; // Clear the email body
    IdMessage1.Body.Add('******');
    s:= GetCurrentDir + '\******';
    IdMessage1.MessageParts.Clear;
    TIdAttachmentFile.Create(IdMessage1.MessageParts, s ) ;
    
    IdSMTP1.ConnectTimeout := 2400 ;
    IdSMTP1.Connect();
    try
        IdSMTP1.Send(IdMessage1) ;
    finally
        IdSMTP1.Disconnect;
    end; 
    

由于这个简单的示例有效,您的问题可能是:

  • 表格中的电子邮件名称/密码/值不正确;
  • 防病毒包阻止您的程序访问 TCP;
  • 尝试使用不是您的 ISP 提供的电子邮件帐户

当我更换 ISP 时,旧的不起作用(访问需要 SMTPS),但是当我切换到使用我的宽带供应商提供的电子邮件帐户时,这不需要 SMTPS,但可以在普通 SMTP 上工作,因为它在他们的网络中因此值得信赖

【讨论】:

  • 虽然很高兴您经历了所有这些努力,但它并没有回答问题。正确的答案是 Indy 正在阻塞,因此您的代码将停止,直到对 IdSMTP1.Send 的调用返回。雷米的答案有正确的信息,要么是一个单独的线程,要么是使用TIdAntiFreeze(按优先顺序)来防止阻塞主(UI)线程。 (发帖人的代码也发送了一个附件;问题是关于如何防止它在这样做时冻结用户界面。)
猜你喜欢
  • 1970-01-01
  • 2012-04-08
  • 2018-02-25
  • 2015-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多