怎样发送邮件

翻译|其它|编辑:郝浩|2004-07-30 09:28:00.000|阅读 2988 次

概述:

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>


怎样发送邮件

Doug Hennig 提出了一个可以用来从你的应用程序发送 e-mail 的类。他结合两个伴随 VFP 发行 MAPI ActiveX 控件创造出一个容易使用的类。

这些日子以来,越来越多的用户希望他们的应用程序是可以使用 e-mail 的。例如,一个常见的商业应用程序的自定义表单会包含一个用于储存客户的 e-mail 地址的字段。不使用一个快速的途径(例如在表单或者工具栏上的一个按扭、或者一个菜单项),而要切换到其它的如 outlook Express 中去并拷贝和粘贴它们的email地址是一个耻辱。如果认识到给vfp的应用程序添加这个功能是这样的简单那更是双倍的耻辱。这个月的文章讲述了一个可以从你的应用程序中被用来发送email的类。

MAPI ActiveX 控件

实现一个可以发送email的应用程序最简单的途径是使用随着vfp发行的 MAPI ActiveX 控件。事实上有两个相关的控件,都包含在 MSMAPI32.OCX 文件中: MAPIMessages 和 MAPISession。这些控件一起工作- MAPIMessages 控件负责管理邮件对话,MAPISession 被用来发送和接收信息。这两个控件都是非可视的。你可以把它们拖放到一个表单上,或者在代码中使用 CREATEOBJECT 来实例化它们(这些控件的 ProgIDs 是 MSMAPI.MAPIMessages 和 MSMAPI.MAPISession)。

这些控件的属性、事件和方法(PEMs)详细描述在帮助文件MAPI98.CHM (在我的系统中是在 \WINDOWS\HELP 目录下)中。我们不准备详细讲述怎么直接使用这些控件,而是要讲讲我已经综合了这两个控件建立的一个更容易使用的容器类。当我们查看类中的代码的时候,我们将看到这些 ActiveX 控件的 PEMs 是怎样被使用的。另外,尽管这些控件发送和接收 e-mail都可以,这篇文章将仅关注于发送信息。

SFMAPI

为了隐藏使用这两个控件的复杂性,我建立了SFMAPI 类 (在 SFMAPI.VCX 文件中)。SFMAPI 基于我们定义在 SFCTRL.VCX 文件中的基础容器类。它包含一个 MAPIMessages 控件(在 SFMAPI 中的名字是 oMAPIMessage)和一个 MAPISession 控件(oMAPISession)。我把容器的 Visible 属性设置为 .F.(因为它在运行时是一个非可视控件),并添加了几个属性:
aAttachments—— 一个被保护的数组属性,用于储存一个邮件的附件的文件名;
aRecipients—— 一个被保护的数组属性,用于储存一封邮件的收件人;
cMessage———— 要发送的邮件的正文;
cSubject———— 邮件的主题
lLoggedIn————一个被保护的属性,如果我们成功的登录到MAPI则它包含一个.T.
lSignoffAfterSend——如果它为.T.,则意味着将在发送完一封邮件后退出 MAPI 对话登录
要使用 SFMAPI 发送一封邮件,就从调用 NEWMessage 方法开始。这个信息简单的初始化 aRecipents 和 aAttachments 做一个空的输入;这其实只是为了可能你过去用这个对象发送过一封邮件的原因。下一步,为这封邮件的每个收件人调用一次 AddRecipient 方法,传递e-mail地址以及可选的姓名和收件人的类型(主收件人为1,复件收件人为2,3是为一个匿名收件人使用的)。这个方法在确定信息有效后把它传递到 aRecipients 数组中去:
lparameters tcAddress, ;
tcName, ;
tnType
local lcName, ;
lnType, ;
lnCount
assert vartype(tcAddress) = 'C' and ;
not empty(tcAddress) ;
message '指定了无效的e-mail地址'
lcName = iif(type('tcName') <> 'C' or empty(tcName), ;
tcAddress, tcName)
lnType = iif(type('tnType') <> 'N' or ;
not between(tnType, 1, 3), 1, tnType)
with This
lnCount = iif(empty(.aRecipients[1, 1]), 1, ;
alen(.aRecipients, 1) + 1)
dimension .aRecipients[lnCount, alen(.aRecipients, 2)]
.aRecipients[lnCount, 1] = lcName
.aRecipients[lnCount, 2] = tcAddress
.aRecipients[lnCount, 3] = lnType
End With
如果你的这封邮件有任何附件,就调用 AddAttachment 方法,这个方法类似于AddRecipient 方法,但它给 aTTachments 数组添加的是指定的文件名。

最后,为这封邮件的主题设置 cSubject 属性,为邮件的正文设置 cMessage 属性,并调用 Send 方法。如果你想要显示一个对话框则传递一个 .T.。注意,在VFP5中你必须这么做以避免得到一个“无效调用”的错误(在VFP6中它工作正常)。

我们将分步来讲述 Send 方法。它从保证我们已有至少一个收件人开始,然后,如果我们还没有登录进MAPI,则调用 SignOn 方法(不久我们会讨论)。如果它失败了,Send 将返回 .F.。
lparameters tlDisplayDialog
local lnI
with This

* 确保我们至少有一个收件人

assert not empty(.aRecipients[1, 1]) ;
message 'SFMAPI.Send: no recipients'

* If we're not already logged in, try to do so. If we
* can't, return .F.

if not .lLoggedIn and not .SignOn()
return .F.
endif not .lLoggedIn ...
下一步,它设置 MAPIMessages 对象的 SessionID 属性为 MAPISession 对象的相应属性的值,这样 MAPIMessagex对象就能够与 MAPI 进行通讯了;这一点类似于为一个用 FOPEN() 函数打开的文件使用一个文件句柄。它调用 MAPIMessages 的 Compose 方法来开始一个新的邮件,并设置 MsgNoteText 和 MsgSubject属性为它的 cMessage 和 cSubject 的值。
.oMAPIMessage.SessionID = .oMAPISession.SessionID
.oMAPIMessage.Compose()
.oMAPIMessage.MsgNoteText = .cMessage
.oMAPIMessage.MsgSubject = .cSubject
下一步是告诉 MAPIMessage 对象关于收件人的事。MAPIMessage 对象有一个用某种奇怪的方法显露的内部收件人集合:收件人相关的属性被绑定到当前内部收件人,这个收件人用 RecipIndex 属性来表示。把RecipIndex 设置为你想要对之工作的收件人的索引(这个索引是从0开始的,所以第一个收件人的的所以是0),从相对应的 MAPIMessages 对象的属性读出并写入到选中的收件人中去。要增加一个新的收件人,,设置 RecipIndex 为一个比当前收件人编号大的值(事实上,由于这个索引是从0开始的,所以这个值就等于编号),这个值保存在 RecipCount 属性中;这么做 RecipCount 会自动增加。我们感兴趣的收件人相关的属性是 RecipDisplayName (收件人的姓名,就象用户看到的那样),RecipAddress(e-mail地址),和 RecipType(收件人类别,它的范围我前面提到过)。在设置了这些属性后,如果重要的话调用 ResolveName 方法从地址簿中决定收件人姓名。
写收件人信息到 MAPIMessages 对象的代码遍历 aRecipients数组,设置收件人索引来增加另一个收件人并为新的收件人更新相应的属性。
for lnI = 1 to alen(.aRecipients, 1)
.oMAPIMessage.RecipIndex = ;
.oMAPIMessage.RecipCount
.oMAPIMessage.RecipDisplayName = .aRecipients[lnI, 1]
.oMAPIMessage.RecipAddress = .aRecipients[lnI, 2]
.oMAPIMessage.RecipType = .aRecipients[lnI, 3]
.oMAPIMessage.ResolveName()
next lnI
文件附件的待遇非常类似:Attachmentindex 属性表示被提到的是哪个内部附件,AttachmentPosition 、AttachmentPathName 和 AttachmentName 被绑定到当前附件。AttachmentPathName(附件完整的路径和文件名)和 AttachmentName (用户看到的附件名)简单,不过 AttachmentPosition(又一次)很奇怪:它表示附件应该放在邮件正文的什么地方。我不知道附件跟邮件的正文有什么关系,但既然没有两个附件可以出现在同一个位置并且没有一个可以放在邮件末尾的后面,我决定把它们放在邮件的前面(位置是从0开始的;因此在下面的代码中-1)
for lnI = 1 to alen(.aAttachments)
if not empty(.aAttachments[lnI])
.oMAPIMessage.AttachmentIndex = ;
.oMAPIMessage.AttachmentCount
.oMAPIMessage.AttachmentPosition = lnI - 1
.oMAPIMessage.AttachmentPathName = .aAttachments[lnI]
.oMAPIMessage.AttachmentName = ;
justfname(.aAttachments[lnI])
endif not empty(.aAttachments[lnI])
next lnI
最后,我们需要调用 MAPIMessages 对象的 Send 方法来发送邮件。如果我们假定要显示一个对话框(象我前面所提到的,那是VFP5所必需的),值1被传递来发送。然后,如果我们假定发送一个邮件后要退出登录,就调用 Signoff 方法。你也许会以为返回 MAPIMessages 的 MsgSent 属性会有意义(这个属性表示邮件是否被成功的发送到邮件服务器;它不表示邮件是否将成功的被邮件服务器发送出去),但由于某些原因,我的这个属性总是 .F.。
if tlDisplayDialog
.oMAPIMessage.Send(1)
else
.oMAPIMessage.Send()
endif tlDisplayDialog
if .lSignOffAfterSend
.SignOff()
endif .lSignOffAfterSend
End With
如果我们还没有登录进 MAPI,被保护的方法 SignOn 被调用来登录到 MAPI。它首先确证 MAPI32.DLL 能够在 Windows 的系统路径中被找到(GetSystemDirectory API函数被用来找到那个路径),然后设置 MAPISession 对象的 DownloadMail 属性为 .F.(在这里我们只想发送邮件)、LogonUI 属性为.F.(如果你需要显示一个对话框让用户输入他们的姓名和密码则把它设置为.T.)它调用 MAPISession 的 SignOn 方法来执行登录到 MAPI,然后,如果 MAPISession 的 SessionId 属性大于0 则设置 SFMAPI 的 lLoggedIn 属性为 .T.。这个方法如果登录成功则返回 .T.。
local lcDirectory, ;
lnLen
with This

* 除非我们能够找到这个DLL,否则不要试图登录到 MAPI

declare integer GetSystemDirectory in Win32API ;
string @, integer
lcDirectory = replicate(chr(0), 80)
lnLen = GetSystemDirectory(@lcDirectory, 80)
lcDirectory = addbs(left(lcDirectory, lnLen))

* 我们能够找到,那么设置一些属性并试图登录进去

if file(lcDirectory + 'MAPI32.DLL')
.oMAPISession.DownloadMail = .F.
.oMAPISession.LogonUI = .F.
.oMAPISession.SignOn()
.lLoggedIn = .oMAPISession.SessionID > 0
else
messagebox('Cannot find MAPI32.DLL')
.lLoggedIn = .F.
endif not file(lcDirectory + 'MAPI32.DLL')
End With
return This.lLoggedIn
被保护的 Signoff方法,当 lSignoffAfterSend 属性被设置为.T.时会从 Send 方法被调用,还会被 Destroy方法、Error方法调用,它从MAPI的登录中退出,并设置 lLoggedIn 属性为.F.。
with This.oMAPISession
if .SessionID <> 0
.SignOff()
endif .SessionID <> 0
End With
This.lLoggedIn = .F.
示例表单
TESTMAPI.SCX 是一个演示了使用 SFMAPI 的表单。它看起来象是一个常见的客户数据输入表单,有姓名、地址等等的文本框。它还有一个客户的 e-mail 地址的文本框。在这个文本框前面的标签是粗体的(以告诉用户它是特殊的)并在它的双击方法中有以下代码:
with Thisform.oMAPI
.AddRecipient(alltrim(Thisform.txtEmail.Value))
.Send(.T.)
End With
当你双击这个标签,这段代码作为客户添加e-mail文本框的值,并调用 SFMAPI 控件的 Send 方法,传递 .T. 以显示对话框。注意 cSubject 和 cMessage 属性还没有设置;毕竟,我们不知道用户想要为这些项目使用什么内容,这样,他们能够在显示的对话框中输入详细的信息。

你能够为用户提供其它的途径来表示他们想要发送一封 e-mail:在 e-mail 文本框边上的一个命令按扭,在表单或工具栏上的一个按扭,一个菜单项等等。著名的 VFP 精神领袖 Markus Egger建立了一个天才的机制,把它作为 Visual LandPro 的一个部分。两年前他帮助建立的一个应用程序连续的在VFP优秀奖中成为最终优胜者。他建立了一个作为超级链接的显示一个 e-mail 地址的文本框(就是说,它在白色的背景上显示为蓝色,并有一条下划线)。用户可以向使用一个超级链接一样使用它,来给那个地址发送一封邮件。关于他的类的更棒的是你甚至不需要对大多数用户解释它是怎么工作的。
结论
既然给一个应用程序添加使用 e-mail 的能力就象拖放一个 SFMAPI 对象到表单上、设置它的属性、并在用户表示他们要发送一个 e-mail 时调用一些方法那么简单,没有理由不把这些能力添加到即使一个最简单的应用程序中去。


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP