用delphi编写邮件发送程序
双击开始自动滚屏 
  平时你使用的是什么工具发送电子邮件?是outlook,foxmail或是其它?无论以前是什么,现在你都可以多一种选择:使用自己编写的邮件发送程序。而且对于delphi来说,要完成它是如此的简单,那就让我们动手吧!
  构件组成
  delphi 5构件板的fastnet页中,提供了tnmsmtp构件,它的功能就在于将邮件发送到指定的邮件服务器。其最常用的属性和方法如下:

  host:smtp邮件服务器的地址,如smtp.sina.com.cn
  port:smtp邮件服务器的端口号,一般用其默认值25即可
  userid:用户名,当与邮件服务器建立连接时,需验证用户名
  postmessage.fromaddress:发件人地址
  postmessage.toaddress:收件人地址
  postmessage.p_w_uploads:邮件附件的文件列表
  postmessage.body:邮件的正文
  postmessage.subject:邮件的主题
  connect方法:建立与smtp服务器的连接,连接前需指定host和userid
  disconnect方法:断开与服务器之间的连接
  sendmail方法:将当前邮件发送到服务器
  当然,tnmsmtp构件还有很多其它的属性、方法、事件,在这里我们不作介绍,需要时大家可以查阅相关资料或delphi的帮助信息。
  实现方式
  首先在form1中加入相应的tlabel、tedit、tmemo、tbutton、tlistbox、topendialog和tnmsmtp构件。其中,tmemo用于书写邮件内容,tlistbox显示附件项目,topendialog(文件打开对话框)用于选择附件文件,按钮button1添加附件,按钮button2连接服务器并发送邮件。程序的具体代码如下:
  procedure tform1.button1click(sender: tobject);
  begin
  if opendialog1.execute then listbox1.items.add(opendialog1.filename);
  //将选择的文件项添加到listbox1中,作为附件
  end;

  procedure tform1.button2click(sender: tobject);
  begin
  nmsmtp1.host:=edit3.text; //smtp服务器地址
  nmsmtp1.userid:=edit4.text; //你在对应服务器上的注册用户名
  nmsmtp1.connect; //与服务器建立连接
  nmsmtp1.postmessage.fromaddress:=edit5.text; //发件人的邮件地址
  nmsmtp1.postmessage.subject:=edit2.text; //邮件主题
  nmsmtp1.postmessage.toaddress.add(edit1.text); //收件人的邮件地址
  nmsmtp1.postmessage.p_w_uploads.addstrings(listbox1.items); //附件
  nmsmtp1.postmessage.body.assign(memo1.lines); //邮件的正文
  nmsmtp1.sendmail; //发送邮件
  nmsmtp1.disconnect; //断开连接
  showmessage(’发送完毕!’); //发送完时显示提示信息
  end;
  以作者为例:我的一个邮箱为[email protected],利用smtp.163.com邮件服务器向《中国电脑教育报》编辑部发信时,在edit1中填入编辑部的地址[email protected],edit2中写上主题,edit3中填入smtp.163.com,edit4中填入用户名dyhuyz,edit5中填入我的地址[email protected],添加附件后,单击发送按钮,一切ok了!

  注意事项
  需要特别说明的是:目前部分网站的smtp邮件服务器要对发送邮件的用户进行身份验证,即要求给出用户名和密码信息,否则无法发送,包括163.net,263.net,sina.com,sohu.com等均在此列。而delphi 5的tnmsmtp构件本身未提供“密码”属性,采用以上简单方法,不能利用这些服务器发送邮件,否则会出现错误信息;delphi 6中用indy构件组取代了delphi 5中的fastnet构件组,其中的smtp包含有身份验证的相关信息,可以很方便地解决这个问题。另一部分网站,如china.com,163.com等,则不要求身份验证,利用本程序,尽可畅通无阻。
本文将实现如何编写简单的应用程序向邮件订阅者发送内容一样或者内容基本一样的电子邮件。

一、 软件准备
读者应该拥有一份C++ Builder3.0或者Delphi 4.0或以上版本的拷贝。另外,还要有一份支持ODBC接口的数据库拷贝,例如:Microsoft Access、Paradox、dBase、Microsoft SQL Server、IBM DB2、Oracle等等。该数据库可以是平面文件数据库,也可以是关系型数据库。在本文中使用的是Delphi 5.0和Microsoft Access。二、数据库设计该email数据库中共有三个表:User表,Subject表,link表,各个表的字段格式如下:
link表的UserID字段引自User表的ID字段,Subscribe表的SubjectID字段引自Subjectx表的ID字段。如果您使用的数据库支持Trigger,mjh yidd Insert、Update、Delete的Trigger使User表和Subject表的ID字段具有唯一的值。

三、 程序设计过程
本程序需要的组件有:TdataBaseTquery、NMSmtp、TListBox组件。其中,Tquery组件负责数据库的查询工作,TdataBase负责数据库的连接工作,TListBox组件用来显示查询结果,NMSmtp组件用来发送E-Mail信息。

本程序实现的功能有:
1. 根据用户订阅的邮件主题,从数据库中查询出要发送的用户名称,电子邮件地址;
2. 在已经查询出的用户中进行进一步的调节;
3. 载入邮件正文,保存邮件正文;
4. 发送邮件;
5. 进行邮件发送状态的记录并保存。

四、具体的设计细节
本程序的VCL组件的属性:
DataMOdule1,数据窗口。此外,还需要三个eidt文本框(分别输入发信人姓名、发信人邮件地址、邮件主题),及一个memo邮件正文输入域。
Form2窗口,用户配置界面。需要注意的是,其中的Groupbox1和Groupbox2的Visible设为false,在查询出用户信息后再显示这些组件。
1. 查询数据库
在配置用户信息的窗体的FormCreat事件中,需要将数据库中的所有的邮件主题查询出来,显示在一个ComboBox组件中。其主要代码如下:
procedure TForm2.FormCreate(Sender: TObject);
var
i:integer;
begin
Query1.Active:=false; //在对TQuery组件进行查询之前,必须要关闭TQuery组件,或者将其属性Active设为false;
Query1.Active:=true; //打开TQuery组件
for I:=0 to Query1.RecordCount-1 do
begin
ComboBox1.Items.Add(Query1.FieldValues[‘subject']); //将查询主题加入到TComboBox中
Query1.Next;
end;
Query1.First; //将TQuery的记录设为第一条
ComboBox1.Text:=ComboBox1.Items[0];
end;
然后再根据ComboBox1中的主题查询用户和用户邮件地址。
procedure TForm2.Button1Click(Sender: TObject);
var
i:integer;
begin
listbox1.Clear;
listbox2.Clear; //清除两个ListBox中的内容,进行新的一次查询
Query1.Active:=false;
Query1.SQL.Clear;//清除TQuery的SQL语句
if not checkbox1.checked then//如果选择发送给订阅相应主题的用户
begin
Query1.SQL.Add(‘SELECT user.username, user.address, subject.subject '+‘FROM (link INNER JOIN subject ON link.subjectid = subject.subjectid) INNER JOIN user ON link.userid = user.userid ' +‘WHERE (((subject.subject)=:subject))');
Query1.Params[0].AsString:=ComboBox1.Text;//设置SQL中的主题参数
end
else //如果选择发送给全部用户
Query1.SQL.Add(‘select user.username,user.address from user');
Query1.Prepare;
Query1.Open;
for i:=0 to Query1.RecordCount-1 do
beginListBox1.Items.Add(expend(vartostr(Query 1.fieldvalues[‘username']))+vartostr(Query1.FieldValues[‘address']));
//在订阅用户框中显示查询到的用户信息
Query1.Next;
end;
label4.caption:=‘共有'+inttostr(listbox1.Items.Count)+‘项';
label5.Caption:=‘共有'+inttostr(listbox2.Items.Count)+‘项';//显示查询到的用户数量
GroupBox3.Visible:=true;
GroupBox2.Visible:=true;
Form2.Height:=554;
end;
2. 调整用户信息
双击需要发送的用户框中的条目,将其去掉;同样,双击不需要发送的用户框中的条目,将其加入发送用户框中,这部分代码相当简单,在此略去。
3. 保存、载入邮件正文
本程序中邮件正文是显示在TMemo1中的,保存和载入邮件正文可以使用TMemo1.lines.loadfromfile(string filename)方法及TMemo1.lines.savetofile(filenname)方法。实现代码如下:
载入邮件正文:
procedure TForm1.Button2Click(Sender: TObject);
begin
if (datasmtp.DataModule1.OpenDialog1.Execute) then
begin
Memo1.Lines.LoadFromFile(datasmtp.DataModule 1 .OpenDialog1.FileName);
end;
end;
保存邮件正文:
procedure TForm1.Button3Click(Sender: TObject);
begin
if (datasmtp.DataModule1.SaveDialog1.Execute) then
begin
Memo1.Lines.SaveToFile(datasmtp.DataModule 1.SaveDialog1.FileName);
end;
end;
4. 发送邮件
发送邮件使用的是Delphi和C++Builder中的VCL组件TNMSMTP,在此组件中封装了发送邮件的各种属性、方法和事件,其中PostMessage包含将要发送的邮件的相关信息,PostMessage中包括Attackments(邮件附件信息)、body(邮件正文)、Date(邮件发送日期)、FromAddress(发信人邮件地址)、FromName(发信人姓名)、LocalProgram(发送邮件程序的名称)、ReplyTo(回信地址)、subject(邮件主题)、ToAddress(收信人地址)、ToBlindCarbonCopy(邮件暗送地址)、ToCarbonCopy(邮件抄送地址)。此外,TNMSMTP的重要属性和方法有:Connected(是否与邮件服务器连接上)、Host(指定邮件服务器地址)、Port(端口号,默认为25)、Connect(与Host指定的服务器连接)、sendmail(发送邮件)。发送邮件的代码如下:
procedure TForm3.Button3Click(Sender: TObject);
var
i:integer;
begin
with datasmtp.DataModule1.smtp.PostMessage do
begin
FromAddress:=form1.edit2.Text;
FromName:=form1.edit1.Text;
Subject:=form1.edit3.text;
Body.Text:=form1.memo1.Text;
end;
with datasmtp.DataModule1.smtp do
begin
for i:=0 to form2.ListBox1.Items.Count-1 do
begin
PostMessage.ToAddress.Clear;
PostMessage.ToAddress.Add(getaddress(form2.List Box1.items[i]));
Host:=gethost(getaddress(form2.ListBox1.Items[i]));
try
datasmtp.DataModule1.smtp.Connect;
except
end;
if datasmtp.datamodule1.smtp.connected then
datasmtp.DataModule1.smtp.SendMail;
end;
end;
end;
5. 记录邮件发送状态并保存日志
使用TNMSMTP的各种事件(如,OnConnect、OnConnectFailed、OnFailure、OnHostResolved等等)记录邮件发送状态。实现过程请参见下面的代码:
procedure TDataModule1.smtpConnect(Sender: TObject);
var
s:string;
begin
s:=timetostr(time)+‘正在连接'+smtp.Post Message.ToAddress.Strings[0]+‘的服务器,请稍候!';
form3.Label1.Caption:=s;
form3.Memo1.Lines.Add(s);
end;
procedure TDataModule1.smtpConnectionFailed(Sender: TObject);
var
s:string;
begin
s:=timetostr(time)+‘连接'+smtp.Post Message.ToAddress.strings[0]+‘的服务器失败!';
form3.Label1.Caption:=s;
form3.Memo1.lines.add(s);
end;
procedure TDataModule1.smtpSendStart(Sender: TObject);
var
s:string;
begin
s:=timetostr(time)+‘开始发送给'+smtp.postmessage.ToAddress.strings[0]+‘的邮件!';
form3.Label1.Caption:=s;
form3.memo1.lines.add(s);
end;
procedure TDataModule1.smtpSuccess(Sender: TObject);
var
s:string;
begin
s:=timetostr(time)+‘成功发送给'+smtp.postmessage.ToAddress.strings[0]+‘的邮件!';
form3.Label1.Caption:=s;
form3.memo1.Lines.add(s);
end;
procedure TDataModule1.smtpInvalidHost(var Handled: Boolean);
var
s:string;
begin
s:=timetostr(time)+‘:发送邮件给'+smtp.PostMessage.ToAddress.strings[0]+‘时,服务器'+smtp.Host+‘无效!';
form3.Label1.Caption:=s;
form3.memo1.lines.add(s);
end;
procedure TDataModule1.smtpFailure(Sender: TObject);
var
s:string;
begin
s:=timetostr(time)+‘:给'+smtp.PostMessage.ToAddress.strings[0]+‘的邮件发送失败!';
form3.Label1.Caption:=s;
form3.memo1.Lines.add(s);
end;
procedure TDataModule1.smtpHostResolved(Sender: TComponent);
var
s:string;
begin
s:=timetostr(time)+‘:已经解析出'+smtp.PostMessage.ToAddress.strings[0]+‘的邮件服务器地址!';
form3.Label1.Caption:=s;
form3.memo1.Lines.add(s);
end;
保存日志的代码如下:
procedure TForm3.Button1Click(Sender: TObject);
begin
datasmtp.DataModule1.SaveDialog1.FileName:=datetostr(date)+‘邮件日志.txt';
if datasmtp.DataModule1.SaveDialog1.Execute then
memo1.Lines.SaveToFile(datasmtp.DataModule1.SaveDialog1.FileName);
end;

使用TNMSMTP组件我们可以方便地实现批量发送电子邮件的功能,而如果将其组件使用在NSAPI中,加上高度的密码控制,我们就可以实现一个像在线邮局263一样的提供邮件服务的网站了。除了发送纯文本的邮件之外,TNMSMTP组件还可以通过TpostMessage中的Attachments属性粘贴附件,从而实现其他格式的文件发送。在本文的源代码中已经加上了发送附件的功能,请参考源程序 。另外,本文中的SMTP服务器是smtp.加上服务器主机名,如[email protected]的服务器就是smtp.263.net,如果您的smtp服务器不是这种规则,请修改源程序。
本程序使用Delphi5.0,在Windows98中文版下调试通过。

张小龙谈如何写E-mail软件
  编者语:鼎鼎大名的Foxmail软件制作者,你一定不会陌生吧!本刊第三期特刊还刊登过此君的生活照一张,可谓威风八面。小编此次突发奇想,“死缠烂打”,费了九牛二虎之力,终于约他写了一篇有关写E-mail软件的文章,这可是大家之作哟!各位赶快来品尝吧!

张小龙谈如何写E-mail软件

  《新潮电子》的编辑向我约稿,而我一向都是懒得写文章的,但编辑说是写关于怎样写邮件软件的,因为很多读者对开发邮件程序感兴趣。我想这倒比其他内容好写些,因为开发foxmail的缘故,毕竟积累了一些想法。但因为是面向大众的文章,技术性又不能太强,因此主要从概念上说说我自己的看法。
  首先要确定的是你要开发的邮件软件的应用对象和范围。分为三类吧,第一类,你的目标是通用的E-mail软件,就像foxmail, outlook一样。第二类,是E-mail的一些分支,如邮件监测器(POP3 Monitor,自动检测服务器上是否有邮件到来),批量邮件发送器(bulk emailer)等。第三类,是将E-mail应用到某些软件项目中,比如,你可能须要在一个MIS项目中用E-mail来传输数据,或在Web上提供发送邮件的功能(如贺卡)。
  关于三种类型,他们的实现方法和难度是不一样的。为了讲解方便,我们将上面三种类型称为A类,B类和C类。其中A类因为用户的覆盖面最广,须要考虑的因素最多,对程序的稳定性和适应性要求也最大,B类次之。
  下面的内容主要集中在邮件相关知识上。我要强调的是,一个邮件软件,更多的是非网络的处理。因为邮件软件不同于其它的网络应用,邮件软件要更贴近用户,实际上是一个日常办公应用。事实上,在Foxmail中,网络部分的处理可能只用了10%的精力。

一、了解TCP/IP网络编程方法
  对于A类和部分B类应用,要求你自己编程实现基于TCP的邮件通讯。因此对TCP/IP编程的理解是必要的。而且一旦你掌握了TCP/IP的编程方法,你可以完成更多的网络程序,比如FTP, HTTP等。这里没法深入去讨论,因为这是一本书或几本书的内容。因此只能向你推荐我自己觉得必看的书目:

1.Internet的经典教材:
书名:Internetworking With TCP/IP。作者:Douglas E. Comer。出版:Prentice Hall。
中译本:《用TCP/IP进行网际互连》。出版:电子工业出版社。
这套书共有三卷。清华大学出版社在国内发行英文版,因为价格比国外买便宜,前不久我还在广州买了一套寄给在美国念书的朋友(邮费比书还贵)。

2.一本很好的关于Winsock编程的书
书名:《Internet编程》,电子工业出版社,1996。
这是一本翻译过来的书,详细讲解了TCP/IP编程的概念和方法。其中对Unix socket和Windows socket编程的区别,以及Windows下socket的同步和异步,消息和多线程等概念讲解透砌。1996年我就是因为看了这本书,萌发了写foxmail的想法。

二、了解电子邮件相关的标准
  对A类和B类应用,有必要非常熟悉网络协议,特别是与E-mail相关的RFC协议。RFC是Request for Comments的简称,Internet的绝大部分协议都是通过RFC的方式提供与更新的,比如我们常用的HTTP协议,就是由RFC2068 定义的。与E-mail相关(通讯,邮件格式,附件编码等)的协议有很多,以下是必须要看的RFC:
RFC 821 (SMTP,简单邮件传输协议,定义了发送邮件的机制)
RFC 822 (邮件格式定义)
RFC 1725 (POP3,邮局协议版本3,定义了从POP3服务器收取邮件的机制)
RFC 1521 (MIME标准)
RFC 1522 (MIME 标准2)
  在这里,因为篇幅关系,没有办法深入讲解,因此只能由读者自己查阅这些文档。这些文档可以在Internet上很多站点找到,比如http://www.cis.ohio-state.edu/htbin/rfc/INDEX.rfc.html就列出了全部的RFC文档。
对于C类应用(和部分B类应用),可以不去关心这些协议,因为你可以采用一些现成的控件来完成邮件的功能,封装得好的控件可以完全屏蔽掉这些实现细节。

三、非网络的因素
  如前面所说的,你须要投入更大的精力到其它非网络的部分的开发中,因为作为一个应用软件,你要细致地处理每个细节。但这里说的并不是各个特性细节的堆砌。比如对于A类应用,你须要做好合理的系统设计,然后对设计中的每个大模块找到好的实现方法。比如电子邮件的存储,我称它为邮箱文件系统,事实上是个基于索引的变长记录系统。如收件箱,由两个文件组成,In.ind是索引文件,记录了每个邮件的摘要信息(发件人,主题,日期等),更重要的是,每个邮件在数据文件中的位置;数据文件是in.box,简单地记录了每个邮件的原始内容。邮箱文件系统的原理并不难理解,难的是要保证其非常高的稳定性,因为邮箱文件的设计错误将直接导致邮件的丢失。
  就像浏览器的开发一样,浏览器的HTTP网络协议处理并不复杂,但HTML的显示要复杂得多。电子邮件也一样。但是在B类和C类应用中,问题比较简单,因为是特定的功能实现就可以了。

四、Winsock的消息和多线程
  对Winsock的异步机制的理解非常重要,对多线程的理解也很重要。很多人以为多线程可以解决任何问题,我想并不全是这样。Windows下的socket编程与Unix下的一个很大差别是,你可以将socket的事件与Windows的消息机制紧密联系起来,写出高效率的Winsock程序,这有赖与对Windows和Winsock的理解。如果须要用到多线程,须要周密考虑好线程同步问题,这不仅仅是对线程的理解,可能在规划的时候,就要考虑线程同步问题来。比如你有一个线程在从发件箱中发邮件,一个线程在接收邮件,还有一个线程(主线程)在显示或删除邮件,这些线程可能同时对同一个邮箱文件进行操作,如果不同步的话,后果将是邮箱文件被破坏。
  采用Winsock的基于消息的异步机制是个很好的选择,这样只有当有网络消息到来时,Winsock才发消息通知程序处理,程序不会停顿在等待或循环中。利用异步机制,须要构造好一个“状态机”,即你要让程序清楚知道目前处理到什么阶段了,当网络(服务器)需要数据时,程序才能知道下一步要发送什么数据出去。

五、开发工具的选择
  对于A类和部分B类应用,我想Visual C++和Delphi是比较好的选择。我个人在开发Foxmail的时候决定用Delphi,是因为它可以帮我省去许多设计界面的时间。而且Delphi具备C++的绝大部分优点,如真正的面向对象,运行的效率等,同时具有比C++更容易理解的语法。而且Internet上有许多Component可以参考,我常去的一个Delphi站点是http://sunsite.icm.edu.pl/delphi。
对于部分B类和C类应用,开发工具的选择面大很多,VC++, Delphi, VB, PowerBuilder,甚至Java等都是考虑的对象,就看你的应用方向了。如果基于现成的控件来做,一般是OCX的形式,OCX可以在绝大多数开发平台上使用。
  对于开发工具,我还想说的是,开发工具并不是最重要的,因为任何一种工具,必须用好它,才能发挥它的功能,而用好一个工具的基本点,是对编程的理解和掌握,与开发工具无关。

六、要不要熟悉TCP/IP和邮件协议?
  看了上面的介绍,可能很多读者会问,如果从头去学习TCP/IP编程,并掌握关于email的协议,会用去大量的时间,有没有更快速的方法呢?我想,同样得看你的应用目标是什么。比如,你只是想在一个MIS项目中包含电子邮件功能,当然没必要从底层开发所有的东西,你可以借助一个商业(甚至免费)的OCX来完成邮件功能。比如Delphi中,就包含了POP3和SMTP的OCX,可以直接应用。有了这些OCX,为什么还要自己从头开发呢?因为商业的OCX并不能保证它是功能齐全和稳定的,特别对于email来说,有许多非标准(或准标准)的因素存在,比如汉字的编码方法,这些国外出的OCX一般都没有考虑到。而且从稳定性上来说,email软件须要适应不同的网络和服务器,只有自己开发的,才能不断地改进。Internet上甚至还提供一些免费的源代码。这些代码只能作为参考,不要指望它们能解决所有问题。

七、小结
我担心读者看了上面的介绍,可能有些失望,因为只是些指导性的内容。但没办法,因为任何一个细节的深入探讨都将超出本文的篇幅,我也不想以偏概全。另外你可能觉得工作量太大,不过,如上面说的,如果你只是将邮件功能应用到一个小范围,是不需要这样专注的,但你对TCP/IP和E-mail标准的理解是有帮助的,而且也有助于你对其它网络协议的理解,如HTTP, FTP等。

文/远望资讯



用Delphi编写邮件特快专递程序
文章发表:kehui  发表日期:2002-09-22  阅读次数:321

陈旭波  

“邮件特快专递”这个词现在已经是被广大的网民所熟知了,它就好比是网络上的EMS,能够直接把邮件送到对方的邮箱中,邮件发送完毕后,对方就可以立刻收到。Foxmail在其最新的4.0版本中也特别新增了该功能,作为一名编程爱好者,你是不是也想知道如何编程实现该功能呢?那就随本文一起揭开“邮件特快专递”的神秘面纱:
一、实现原理
在Outlook Express中,可通过查看邮件的属性得到该邮件的头部资料。我们拿一封不是特快专递的普通邮件来进行剖析,下面是笔者一封邮件的头部资料:

Received: from sm1.163.com([202.108.44.203]) by peoplemail.com.cn(JetMail 2.5.3.0)
with SMTP id jm1ac3c063eec; Thu, 29 Nov 2001 07:20:25 -0000
Received: from xubo (unknown [61.154.94.146])
by sm1.163.com (Postfix) with ESMTP id 99CF11C461061
for ; Thu, 29 Nov 2001 15:12:49 +0800 (CST)
……
从该头部资料可以看出,邮件传递的路线是由xubo→sm1.163.com→peoplemail.com.cn,其中xubo是本人所在机器,邮件是经由sm1.163.com这个中转站才送到目的地的,而我们平常所说的邮件特快专递,就是把邮件直接送到目的地而不需要中转站。
如何才能知道目的地的地址呢?我们知道电子邮件的地址由用户名(username)和邮箱域名(address.com)两部分构成的,给域名服务器发送针对“address.com”的MX指令查询即可找到目的地的地址。被查出来的目的地可能不止一个,有时这些目的地是有优先级别之分的,分别对应着邮件的优先级别,你可以根据邮件优先级的不同对应发送,也可以任意选择其中一个发送,本文的示例将选择反馈回来的第一个目的地发送,读者可根据实际需要进行改进。

二、用到的核心组件
本文将采用Indy控件集进行域名查询、邮件发送部分的编程。INDY的全名是Internet Direct(也叫Winshoes),是一套开放源代码、跨平台、优秀的Internet控件集,它支持大部分流行的Internet协议。Delphi 6已经包含了该控件集,如果你使用的是Delphi 6以前的版本,请先到Indy主页http://www.nevrona.com/Indy/Download.html下载并安装该控件集。

三、窗口设计
向窗体上放置三个TEdit组件,一个TMemo组件,四个Tlabel组件,用到的Indy组件为TIdDNSResolver、TIdAntiFreeze、TIdSMTP、TIdMessage。其中TIdDNSResolver是用来向域名服务器发出查询请求并得到结果,TIdMessage用来构建邮件,TIdSMTP用来发送邮件,TidAntiFreeze可以确保Indy在工作时程序能响应来自外界的消息(注:Indy是以同步的方式进行工作的,在读或写操作完成之前,其它代码无法执行,加入TidAntiFreeze可以在每隔一段时间通过呼叫Application.ProcessMessage方法响应来自外界的消息。)
程序的界面如下图所示(箭头所指向的文字标识了该组件的名字):

四、程序代码(代码中包含了详细的说明)

……(前面一部分省略)
type
TForm1 = class(TForm)
IdDNSResolver: TIdDNSResolver;
IdAntiFreeze1: TIdAntiFreeze;
btnSend: TButton;
IdSMTP: TIdSMTP;
IdMsgSend: TIdMessage;
mmContent: TMemo;
Label1: TLabel;
edtTo: TEdit;
Label4: TLabel;
Label5: TLabel;
edtFrom: TEdit;
Label6: TLabel;
edtSubject: TEdit;
procedure btnSendClick(Sender: TObject);
private
{ Private declarations }
procedure GetMxList(AMxList: TStringList; AQName: string);
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

{ 这个过程是用来得到邮件特快专递目的地服务器名称及优先级别数,参数AMXList是
用来接收结果值,AQName代表传递过来的域名 }
procedure TForm1.GetMxList(AMxList: TStringList; AQName: string);
var
i: Integer;
begin
with IdDNSResolver do
begin
Host := '202.101.107.55'; { Host属性用来指定域名服务器的地址,此处为笔者所在地
的主域名服务器地址,你也可以指定任一可以快速访问到的Internet上域名服务器
地址,要知道自己所在地的域名服务器地址,win98下通过winipcfg命令,win2000下
通过ipconfig /all即可查出。}
ReceiveTimeout := 10000; // 在指定的时间内得不到域名服务器的反馈,则视为失败。
ClearVars; // 清除前一次查询所反馈回来的资源记录

{ 构建此次查询的头部结构 }
with DNSHeader do
begin
Qr := False; // False 代表查询
Opcode := 0; // 0代表标准域名查询
RD := True; //域名服务器可以进行递归查询
QDCount := 1; //查询的数量
end;

{ 构建要查询的问题 }
DNSQDList.Clear;
with DNSQDList.Add do
begin
QName := AQName; //要查询的域名
QType := cMX; //QTYPE指定要查询的资源记录的种类,值为cMX代表邮件交换记录
QClass := cIN;
end;

ResolveDNS; //向域名服务器发出请求

{ 从域名服务器接收反馈的结果,将反馈回来的邮件服务器名称放在AMXList列表的Name部分,
邮件服务器的优先级别数放在Value部分。 }
for i := 0 to DNSAnList.Count - 1 do
AMxList.Add(DNSAnList.RData.MX.Exchange + '=' +
IntToStr(DNSAnList.RData.MX.Preference));
end;
end;

{ 单击"发送"按钮时发送专递邮件 }
procedure TForm1.btnSendClick(Sender: TObject);
var
MxList: TStringList;
i: Integer;
QName, ThoughAddress: string;
begin
{ 根据用户所填写的内容创建邮件 }
with IdMsgSend do
begin
Body.Assign(mmContent.Lines); //邮件正文
From.Address := Trim(edtFrom.Text); //发件人地址
Recipients.EMailAddresses := Trim(edtTo.Text); //收件人地址
Subject := edtSubject.Text; //邮件主题
end;

{ 从输入的收件人地址中取出邮箱域名,利用前面的GetMxList过程得到目的地地址 }
QName := TrimRight(copy(edtTo.Text, Pos('@', edtTo.Text) + 1, Length(edtTo.Text)));
MxList := TStringList.Create;
try
GetMxList(MxList, QName);
ThoughAddress := MxList.Names[0]; {取反馈回来的第一个服务器为目的地,读者可
根据实际需要改进,比如说考虑到信件的优先级或当你选择的服务器因繁忙而暂时
不能处理你的信件时,换用其它服务器试试 }
finally
MxList.Free;
end;

{ 发送邮件 }
with IdSMTP do
begin
Host := ThoughAddress; // 将Host赋值为目的地,这就是特快专递与普通邮件的区别
Port := 25; // smtp服务默认的端口为25
Connect; //连接到服务器
try
Send(IdMsgSend); //发送刚才创建的邮件
ShowMessage('发送完毕'); //发送完毕后提示
finally
Disconnect; //断开服务器连接
end;
end;
end;

end.