本人用VFP写了一个文件备份的小程序,现在还想它能把备份时的状况自动发送邮件返回。

时间:2022-06-01 19:12:03
就是说想在这个程序上继续增加一个功能,可以将备份的信息通过邮件自动返回到我的帐号里。最好是能把备份的信息写在邮件的正文里。

有哪位有办法解决的,可以给满分哟。

18 个解决方案

#1


http://www.vfptop.com/download/mapi.zip
MAPI ActiveX controls 
要使用应用程序可以发送 e-mail 的最简单的方法是使用 VFP 自带的MAPI ActiveX 控件. 有两个相关控件, 都在文件 MSMAPI32.OCX 中: MAPIMessages 和 MAPISession. 这些控件一起工作-MAPISession 控件负责管理由件会话期, MAPIMessages 控件用于发送和接收信息. 两个控件都是非可视的. 你可以将它们拖放到表单中或用代码 CREATEOBJECT 来实例它们(这些控件的ProgIDs 是 MSMAPI.MAPIMessages 和 MSMAPI.MAPISession).
这些控件的属性, 事件和方法 (PEM) 在帮助文件 MAPI98.CHM 中描述(位于我的系统中的 \WINDOWS\HELP 目录). 代替描述如何使用这些控件, 我们将探讨一个我创建的组合两个控件到一个易于使用的对象的容器类. 我们将明白ActiveX 的 PEM 是如何用于我们正在探讨的类的代码中的. 另外, 虽然这些控件可以发送和接收邮件, 本文将只着重于发送信息. 

SFMAPI 
为了隐藏两个控件的复杂的处理, 我创建了 SFMAPI 类 (在 SFMAPI.VCX 中). SFMAPI 是基于 SFContainer 容器类的, 我们的容器基类在 SFCTRLS.VCX 中定义. 它包括一个 MAPIMessages 控件 (名为 oMAPIMessage 在 SFMAPI 中) 和一个 MAPISession 控件 (oMAPISession). 我设置容器的 Visible 属性为 .F. (由于它是一个在运行时非可视的控件) 并添加了一系列的属性: aAttachments, 一个用于保存与信息相关的文件名的受保护的数组; aRecipients, 一个收件人信息的受保护的数组; cMessage, 要发送的信息体; cSubject, 信息主题; lLoggedIn, 一个受保护的属性,如果成功登录到 MAPI 则它的值为.T.; 以及 lSignoffAfterSend, 若为 .T. 意味着 SFMAPI 将在发送完信息后中止联接 MAPI 会话期.
要用 SFMAPI 发送一条信息, 用调用 NewMessage 方法开始. 该信息简单地初始化 aRecipents 和 aAttachments 为单纯的空的入口; 它只在你以前用该对象发送过信息才真正地需要. 其次, 为每一个接受者调用 AddRecipient 方法一次, 传递 e-mail 地址和可选的接受者名字和类别 (1是主要接受者, 2是复制接受者, 3是 blind 复制接受者). 该方法在确信 aRecipients 数组是可用的后, 添加传递的信息到 aRecipients 数组:


lparameters tcAddress, ;
tcName, ;
tnType

local lcName, ;
lnType, ; 
lnCount
assert vartype(tcAddress) = 'C' and ;
not empty(tcAddress) ;
message 'Invalid address specified'
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
endwith

如果信息有任何附件, 调用 AddAttachment 方法, 它与 AddRecipient 类似但添加指定的文件名到 aAttachments 数组.
最后, 设置 cSubject 属性为信息主题, 设置 cMessage 为信息体, 并调用 Send 方法. 如果想显示对话框传递 .T.. 注意当从 VFP 5 中调用该方法时,你必须这样做,以避免一个 "不可用的调用" 错 (在 VFP 6 中不会出现该错误).
我们将探讨一下 Send 方法的代码. 它开始时确保至少有一个接收者, 然后,如果还没有登录到 MAPI则调用 SignOn 方法 (我们马上就要看到). 如果失败, Send 返回 .F.

lparameters tlDisplayDialog
local lnI
with This
    * 确信至少有一个接收者.
    assert not empty(.aRecipients[1, 1]) ;
    message 'SFMAPI.Send: no recipients'
    * 如果还没有登录, 试着登录. 如果不能登录, 返回 .F.
    if not .lLoggedIn and not .SignOn()
        return .F.
    endif
接着, 它设置 MAPIMessages 对象的 SessionID 属性为MAPISession 对象的属性值, 这样 MAPIMessages 对象可以和 MAPI 通信; 这与用 FOPEN() 函数打开文件时使用文件句柄相似. 它调用 MAPIMessage 的 Compose 方法来启始一个新的信息, 并设置MsgNoteText 和 MsgSubject 属性为它的 cMessage 和 cSubject 值.

    .oMAPIMessage.SessionID = .oMAPISession.SessionID
    .oMAPIMessage.Compose()
    .oMAPIMessage.MsgNoteText = .cMessage
    .oMAPIMessage.MsgSubject = .cSubject

下一步是告诉 MAPIMessages 对象关于收件人. MAPIMessages 对象的一个内部的、以离奇的方式表述的收件人集合: 由 RecipIndex 属性指明的、与收件人相关的属性绑定到当前的内部收件人. 你设置 RecipIndex 为你想处理的收件人索引 (索引是以0为基数的, 所以第一个收件人的索引是 0), 和适当的 MAPIMessages 属性以便读取和写到选定的收件人. 要添加一个新的收件人, 设置 RecipIndex 为一个大于当前收件人数的值 (事实上, 等于收件人人数, 因为索引是以0为基数的), 它是保存在 RecipCount 属性中; 这样做自动增加 RecipCount. 我们感兴趣的、与收件人相关的属性是 RecipDisplayName (显示给用户的收件人名字), RecipAddress ( e-mail 地址), 以及 RecipType (收件人类型, 我在前面讨论过的范围值). 设置了这些属性后, 调用 ResolveName 方法来从地址薄中分解名字同样是必须的.
写收件人信息到 MAPIMessages 对象的代码遍历 aRecipients 数组, 设置收件人索引来添加另一个收件人并新收件人的适当的属性.

#2


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 方法来发送信息. 如果我们假定要显示一个对话框 (正如我在先前提及, 在 VFP 5 中必须这样做), 传递值 1 到 Send. 然后我们调用 SignOff 方法 (假定发送信息后挂断). 你可能认为返回MAPIMessage 的 MsgSent 属性值是有意义的(它指明邮件是否成功的发送到了邮件服务器; 但它不能指明邮件是否会成功地从邮件服务器发送出去) 但出于一些理由, 对于我来说,该属性总是返回 .F..

    if tlDisplayDialog
        .oMAPIMessage.Send(1)
    else
    .oMAPIMessage.Send()
    endif tlDisplayDialog
    if .lSignOffAfterSend
        .SignOff()
    endif .lSignOffAfterSend
endwith
受保护的 SignOn 方法, 如果我们还没有登录到 MAPI 就从 Send 方法中调用它来登录到 MAPI. 它首先确保 MAPI32.DLL 可以在 Windows System 目录中找到 (GetSystemDirectory API 函数用于定位该目录), 然后设置 MAPISession 对象的 DownloadMail 属性为 .F. (此时我们只想发送邮件) 和 LogonUI 属性为 .F. (如果我们想显示一个登录对话框让用户输入他们的名字和口令, 设置该属性为 .T.). 它调用 MAPISession 的 SignOn 方法来执行登录到 MAPI, 然后设置 SFMAPI 的 lLoggedIn 属性为 .T.(如果 MAPISession 的 SessionID 属性大于 0). 如果登录成功, 方法返回 .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')
endwith
return This.lLoggedIn
受保护的 SignOff 方法, 在 lSignOffAfterSend 属性为 .T. 时从 Send , Destroy 方法和 Error 方法中调用, 用于挂断 MAPI 和设置 lLoggedIn 属性为 .F.

with This.oMAPISession
    if .SessionID <> 0
        .SignOff()
    endif .SessionID <> 0
endwith
This.lLoggedIn = .F. 
示例表单 
TESTMAPI.SCX 是一个演示 SFMAPI 的表单. 它看起来象一个典型的客户数据输入表单, 带有名字, 地址等文本框等. 它还有一个输入客户的 e-mail 地址的文本框. 该文本框前面的标签是黑体字的 (向用户显示它是特别的) 且在其 DblClick 方法中有以下代码:


with Thisform.oMAPI
    .AddRecipient(alltrim(Thisform.txtEmail.Value))
    .Send(.T.)
endwith
当你双击该标签时, 这些代码添加 e-mail 文本框中的值作为收件人并调用 SFMAPI 控件的 Send 方法, 并传递参数 .T. 因此显示一个对话框. 注意 cSubject 和 cMessage 属性没有设置; 毕竟, 我们不知道用户想如何设置这些值, 因此它们可以在显示的对话框中输入需要的信息.
你可以为用户提供其它方法来发送一个 e-mail: 一个在 e-mail 文本框旁边的命令按钮, 一个表单上或工具条上的按钮, 一个菜单项等等. 众所周知的 VFP 头子 Markus Egger 创建了一个聪明的机制作为 Visual LandPro 的一部分, ( Visual LandPro 是一个帮助开发的应用程序, 曾被选为两年一度的 VFP Excellence Awards(VFP 杰出成就奖)决赛程序). 它创建一个显示 e-mail 地址为一个热连接的文本框 (就是说, 它在白色的背景中显示一个兰色的下划线文本). 用户单击它, 就象它们是一个热连接一样, 要发送一个信息到该地址. 关于他的类中的伟大的东西是你甚至不必向大数用户解释它是如何工作的. 
结论 
由于添加 e-mail 能力到一个应用程序简单到拖动一个 SFMAPI 对象到表单中, 设置它的属性, 并在用户指出他们想发送一个 e-mail 时调用一些方法, 没有理由不添加这种能力到那怕是最简单的应用程序中.
下个月, 我们将探讨一些发送邮件列表到一组人的可重用工具: 它们提供大多数邮件列表基础(不, 我不是午餐肉拥护者; 有很多合法的理由来发送相同的邮件到一组人). 到那时, 我希望你喜欢那些类.
  例子下载 http://www.vfptop.com/download/mapi.zip

#3


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 方法来发送信息. 如果我们假定要显示一个对话框 (正如我在先前提及, 在 VFP 5 中必须这样做), 传递值 1 到 Send. 然后我们调用 SignOff 方法 (假定发送信息后挂断). 你可能认为返回MAPIMessage 的 MsgSent 属性值是有意义的(它指明邮件是否成功的发送到了邮件服务器; 但它不能指明邮件是否会成功地从邮件服务器发送出去) 但出于一些理由, 对于我来说,该属性总是返回 .F..

    if tlDisplayDialog
        .oMAPIMessage.Send(1)
    else
    .oMAPIMessage.Send()
    endif tlDisplayDialog
    if .lSignOffAfterSend
        .SignOff()
    endif .lSignOffAfterSend
endwith
受保护的 SignOn 方法, 如果我们还没有登录到 MAPI 就从 Send 方法中调用它来登录到 MAPI. 它首先确保 MAPI32.DLL 可以在 Windows System 目录中找到 (GetSystemDirectory API 函数用于定位该目录), 然后设置 MAPISession 对象的 DownloadMail 属性为 .F. (此时我们只想发送邮件) 和 LogonUI 属性为 .F. (如果我们想显示一个登录对话框让用户输入他们的名字和口令, 设置该属性为 .T.). 它调用 MAPISession 的 SignOn 方法来执行登录到 MAPI, 然后设置 SFMAPI 的 lLoggedIn 属性为 .T.(如果 MAPISession 的 SessionID 属性大于 0). 如果登录成功, 方法返回 .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')
endwith
return This.lLoggedIn
受保护的 SignOff 方法, 在 lSignOffAfterSend 属性为 .T. 时从 Send , Destroy 方法和 Error 方法中调用, 用于挂断 MAPI 和设置 lLoggedIn 属性为 .F.

with This.oMAPISession
    if .SessionID <> 0
        .SignOff()
    endif .SessionID <> 0
endwith
This.lLoggedIn = .F. 
示例表单 
TESTMAPI.SCX 是一个演示 SFMAPI 的表单. 它看起来象一个典型的客户数据输入表单, 带有名字, 地址等文本框等. 它还有一个输入客户的 e-mail 地址的文本框. 该文本框前面的标签是黑体字的 (向用户显示它是特别的) 且在其 DblClick 方法中有以下代码:


with Thisform.oMAPI
    .AddRecipient(alltrim(Thisform.txtEmail.Value))
    .Send(.T.)
endwith
当你双击该标签时, 这些代码添加 e-mail 文本框中的值作为收件人并调用 SFMAPI 控件的 Send 方法, 并传递参数 .T. 因此显示一个对话框. 注意 cSubject 和 cMessage 属性没有设置; 毕竟, 我们不知道用户想如何设置这些值, 因此它们可以在显示的对话框中输入需要的信息.
你可以为用户提供其它方法来发送一个 e-mail: 一个在 e-mail 文本框旁边的命令按钮, 一个表单上或工具条上的按钮, 一个菜单项等等. 众所周知的 VFP 头子 Markus Egger 创建了一个聪明的机制作为 Visual LandPro 的一部分, ( Visual LandPro 是一个帮助开发的应用程序, 曾被选为两年一度的 VFP Excellence Awards(VFP 杰出成就奖)决赛程序). 它创建一个显示 e-mail 地址为一个热连接的文本框 (就是说, 它在白色的背景中显示一个兰色的下划线文本). 用户单击它, 就象它们是一个热连接一样, 要发送一个信息到该地址. 关于他的类中的伟大的东西是你甚至不必向大数用户解释它是如何工作的. 
结论 
由于添加 e-mail 能力到一个应用程序简单到拖动一个 SFMAPI 对象到表单中, 设置它的属性, 并在用户指出他们想发送一个 e-mail 时调用一些方法, 没有理由不添加这种能力到那怕是最简单的应用程序中.
.
  例子下载 http://www.vfptop.com/download/mapi.zip

#4


另一種方法,直接調用outlook,就像調用excel、word一樣:
lol_outlook=createobject('outlook.application')
lol_email=lol_outlook.createitem(0)
with lol_email
.recipients.add('aaa@yahoo.com.cn')
.subject='subject'
.body='body'
.send
endwith
release lol_outlook
release lol_email

#5


*另一種方法,直接調用outlook,就像調用excel、word一樣

lol_outlook=createobject('outlook.application')
lol_email=lol_outlook.createitem(0)
with lol_email
.recipients.add('aaa@yahoo.com.cn')
.subject='subject'
.body='body'
.send
endwith
release lol_outlook
release lol_email

#6


楼上的用法是简单,但一定要先打开OUTLOOK的。这不太合我的要求,
还有其它隐性发布邮件的的方法吗,同时还要不用打开OUTLOOK的,所有信息只要在程序上写就可以的?

#7


楼上的用法是简单,但一定要先打开OUTLOOK的。这不太合我的要求,
还有其它隐性发布邮件的的方法吗,同时还要不用打开OUTLOOK的,所有信息只要在程序上写就可以的?

---------------
不打開OUTLOOK也可以呀,否則用: mgan(很笨 ,很笨的鸟! 想先飞) 的方法最好
所有信息只要在程序上写就可以的?  以上均是呀!

#8


老兄,我不太明白,有例程吗,写给我行吗?

#9


把我也搞糊涂了,你關掉outlook,運行以下代碼不就明白了:

lol_outlook=createobject('outlook.application')
lol_email=lol_outlook.createitem(0)
with lol_email
.recipients.add('aaa@yahoo.com.cn')
.subject='subject'
.body='body'
.send
endwith
release lol_outlook
release lol_email

#10


老兄,这个我试过了,但运行后系统好象在等待运行OUTLOOK一样,不会运行完毕,这里一定有问题存在。
同时我强行关闭VFP后,打开OUTLOOK发现根本不存在程序发出的邮件。

但是我在打开OUTLOOK的情况下,程序很快就运行完毕,而且还发出了邮件。

这会是什么原因呢??

#11


我關閉outlook,运行上述代碼后,系统是好象在等待运行OUTLOOK一样(因為要創建ole),
但是可以運行完畢,再开OUTLOOK可以发现程序发出的邮件,
你强行关闭VFP后,程序沒運行完,打开OUTLOOK,當然发现不了程序发出的邮件。
如果覺得慢,用mgan(很笨 ,很笨的鸟! 想先飞) 的方法試一試

#12


关注

#13


关注

#14


这个问题,放了这么久,还没有一个好的答案。难道就这样结了贴吗?

#15


把你的备份程序发给我一份好吗?
huhuyuan@163.com
谢谢!

#16


我也想要。。。。因为我的程序里,可能需要写到备份!  谢谢!

 lovemm-2002@163.com

#17


MAPI
是要用Outlook的
要不用Outlook直接用stmp,和pop协议

#18


我也要想要一份备分的,如果有EAMIL的更好,能发一份给我吗?
我的邮箱地址 jinxianjaxi2001@sina.com.cn

#1


http://www.vfptop.com/download/mapi.zip
MAPI ActiveX controls 
要使用应用程序可以发送 e-mail 的最简单的方法是使用 VFP 自带的MAPI ActiveX 控件. 有两个相关控件, 都在文件 MSMAPI32.OCX 中: MAPIMessages 和 MAPISession. 这些控件一起工作-MAPISession 控件负责管理由件会话期, MAPIMessages 控件用于发送和接收信息. 两个控件都是非可视的. 你可以将它们拖放到表单中或用代码 CREATEOBJECT 来实例它们(这些控件的ProgIDs 是 MSMAPI.MAPIMessages 和 MSMAPI.MAPISession).
这些控件的属性, 事件和方法 (PEM) 在帮助文件 MAPI98.CHM 中描述(位于我的系统中的 \WINDOWS\HELP 目录). 代替描述如何使用这些控件, 我们将探讨一个我创建的组合两个控件到一个易于使用的对象的容器类. 我们将明白ActiveX 的 PEM 是如何用于我们正在探讨的类的代码中的. 另外, 虽然这些控件可以发送和接收邮件, 本文将只着重于发送信息. 

SFMAPI 
为了隐藏两个控件的复杂的处理, 我创建了 SFMAPI 类 (在 SFMAPI.VCX 中). SFMAPI 是基于 SFContainer 容器类的, 我们的容器基类在 SFCTRLS.VCX 中定义. 它包括一个 MAPIMessages 控件 (名为 oMAPIMessage 在 SFMAPI 中) 和一个 MAPISession 控件 (oMAPISession). 我设置容器的 Visible 属性为 .F. (由于它是一个在运行时非可视的控件) 并添加了一系列的属性: aAttachments, 一个用于保存与信息相关的文件名的受保护的数组; aRecipients, 一个收件人信息的受保护的数组; cMessage, 要发送的信息体; cSubject, 信息主题; lLoggedIn, 一个受保护的属性,如果成功登录到 MAPI 则它的值为.T.; 以及 lSignoffAfterSend, 若为 .T. 意味着 SFMAPI 将在发送完信息后中止联接 MAPI 会话期.
要用 SFMAPI 发送一条信息, 用调用 NewMessage 方法开始. 该信息简单地初始化 aRecipents 和 aAttachments 为单纯的空的入口; 它只在你以前用该对象发送过信息才真正地需要. 其次, 为每一个接受者调用 AddRecipient 方法一次, 传递 e-mail 地址和可选的接受者名字和类别 (1是主要接受者, 2是复制接受者, 3是 blind 复制接受者). 该方法在确信 aRecipients 数组是可用的后, 添加传递的信息到 aRecipients 数组:


lparameters tcAddress, ;
tcName, ;
tnType

local lcName, ;
lnType, ; 
lnCount
assert vartype(tcAddress) = 'C' and ;
not empty(tcAddress) ;
message 'Invalid address specified'
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
endwith

如果信息有任何附件, 调用 AddAttachment 方法, 它与 AddRecipient 类似但添加指定的文件名到 aAttachments 数组.
最后, 设置 cSubject 属性为信息主题, 设置 cMessage 为信息体, 并调用 Send 方法. 如果想显示对话框传递 .T.. 注意当从 VFP 5 中调用该方法时,你必须这样做,以避免一个 "不可用的调用" 错 (在 VFP 6 中不会出现该错误).
我们将探讨一下 Send 方法的代码. 它开始时确保至少有一个接收者, 然后,如果还没有登录到 MAPI则调用 SignOn 方法 (我们马上就要看到). 如果失败, Send 返回 .F.

lparameters tlDisplayDialog
local lnI
with This
    * 确信至少有一个接收者.
    assert not empty(.aRecipients[1, 1]) ;
    message 'SFMAPI.Send: no recipients'
    * 如果还没有登录, 试着登录. 如果不能登录, 返回 .F.
    if not .lLoggedIn and not .SignOn()
        return .F.
    endif
接着, 它设置 MAPIMessages 对象的 SessionID 属性为MAPISession 对象的属性值, 这样 MAPIMessages 对象可以和 MAPI 通信; 这与用 FOPEN() 函数打开文件时使用文件句柄相似. 它调用 MAPIMessage 的 Compose 方法来启始一个新的信息, 并设置MsgNoteText 和 MsgSubject 属性为它的 cMessage 和 cSubject 值.

    .oMAPIMessage.SessionID = .oMAPISession.SessionID
    .oMAPIMessage.Compose()
    .oMAPIMessage.MsgNoteText = .cMessage
    .oMAPIMessage.MsgSubject = .cSubject

下一步是告诉 MAPIMessages 对象关于收件人. MAPIMessages 对象的一个内部的、以离奇的方式表述的收件人集合: 由 RecipIndex 属性指明的、与收件人相关的属性绑定到当前的内部收件人. 你设置 RecipIndex 为你想处理的收件人索引 (索引是以0为基数的, 所以第一个收件人的索引是 0), 和适当的 MAPIMessages 属性以便读取和写到选定的收件人. 要添加一个新的收件人, 设置 RecipIndex 为一个大于当前收件人数的值 (事实上, 等于收件人人数, 因为索引是以0为基数的), 它是保存在 RecipCount 属性中; 这样做自动增加 RecipCount. 我们感兴趣的、与收件人相关的属性是 RecipDisplayName (显示给用户的收件人名字), RecipAddress ( e-mail 地址), 以及 RecipType (收件人类型, 我在前面讨论过的范围值). 设置了这些属性后, 调用 ResolveName 方法来从地址薄中分解名字同样是必须的.
写收件人信息到 MAPIMessages 对象的代码遍历 aRecipients 数组, 设置收件人索引来添加另一个收件人并新收件人的适当的属性.

#2


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 方法来发送信息. 如果我们假定要显示一个对话框 (正如我在先前提及, 在 VFP 5 中必须这样做), 传递值 1 到 Send. 然后我们调用 SignOff 方法 (假定发送信息后挂断). 你可能认为返回MAPIMessage 的 MsgSent 属性值是有意义的(它指明邮件是否成功的发送到了邮件服务器; 但它不能指明邮件是否会成功地从邮件服务器发送出去) 但出于一些理由, 对于我来说,该属性总是返回 .F..

    if tlDisplayDialog
        .oMAPIMessage.Send(1)
    else
    .oMAPIMessage.Send()
    endif tlDisplayDialog
    if .lSignOffAfterSend
        .SignOff()
    endif .lSignOffAfterSend
endwith
受保护的 SignOn 方法, 如果我们还没有登录到 MAPI 就从 Send 方法中调用它来登录到 MAPI. 它首先确保 MAPI32.DLL 可以在 Windows System 目录中找到 (GetSystemDirectory API 函数用于定位该目录), 然后设置 MAPISession 对象的 DownloadMail 属性为 .F. (此时我们只想发送邮件) 和 LogonUI 属性为 .F. (如果我们想显示一个登录对话框让用户输入他们的名字和口令, 设置该属性为 .T.). 它调用 MAPISession 的 SignOn 方法来执行登录到 MAPI, 然后设置 SFMAPI 的 lLoggedIn 属性为 .T.(如果 MAPISession 的 SessionID 属性大于 0). 如果登录成功, 方法返回 .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')
endwith
return This.lLoggedIn
受保护的 SignOff 方法, 在 lSignOffAfterSend 属性为 .T. 时从 Send , Destroy 方法和 Error 方法中调用, 用于挂断 MAPI 和设置 lLoggedIn 属性为 .F.

with This.oMAPISession
    if .SessionID <> 0
        .SignOff()
    endif .SessionID <> 0
endwith
This.lLoggedIn = .F. 
示例表单 
TESTMAPI.SCX 是一个演示 SFMAPI 的表单. 它看起来象一个典型的客户数据输入表单, 带有名字, 地址等文本框等. 它还有一个输入客户的 e-mail 地址的文本框. 该文本框前面的标签是黑体字的 (向用户显示它是特别的) 且在其 DblClick 方法中有以下代码:


with Thisform.oMAPI
    .AddRecipient(alltrim(Thisform.txtEmail.Value))
    .Send(.T.)
endwith
当你双击该标签时, 这些代码添加 e-mail 文本框中的值作为收件人并调用 SFMAPI 控件的 Send 方法, 并传递参数 .T. 因此显示一个对话框. 注意 cSubject 和 cMessage 属性没有设置; 毕竟, 我们不知道用户想如何设置这些值, 因此它们可以在显示的对话框中输入需要的信息.
你可以为用户提供其它方法来发送一个 e-mail: 一个在 e-mail 文本框旁边的命令按钮, 一个表单上或工具条上的按钮, 一个菜单项等等. 众所周知的 VFP 头子 Markus Egger 创建了一个聪明的机制作为 Visual LandPro 的一部分, ( Visual LandPro 是一个帮助开发的应用程序, 曾被选为两年一度的 VFP Excellence Awards(VFP 杰出成就奖)决赛程序). 它创建一个显示 e-mail 地址为一个热连接的文本框 (就是说, 它在白色的背景中显示一个兰色的下划线文本). 用户单击它, 就象它们是一个热连接一样, 要发送一个信息到该地址. 关于他的类中的伟大的东西是你甚至不必向大数用户解释它是如何工作的. 
结论 
由于添加 e-mail 能力到一个应用程序简单到拖动一个 SFMAPI 对象到表单中, 设置它的属性, 并在用户指出他们想发送一个 e-mail 时调用一些方法, 没有理由不添加这种能力到那怕是最简单的应用程序中.
下个月, 我们将探讨一些发送邮件列表到一组人的可重用工具: 它们提供大多数邮件列表基础(不, 我不是午餐肉拥护者; 有很多合法的理由来发送相同的邮件到一组人). 到那时, 我希望你喜欢那些类.
  例子下载 http://www.vfptop.com/download/mapi.zip

#3


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 方法来发送信息. 如果我们假定要显示一个对话框 (正如我在先前提及, 在 VFP 5 中必须这样做), 传递值 1 到 Send. 然后我们调用 SignOff 方法 (假定发送信息后挂断). 你可能认为返回MAPIMessage 的 MsgSent 属性值是有意义的(它指明邮件是否成功的发送到了邮件服务器; 但它不能指明邮件是否会成功地从邮件服务器发送出去) 但出于一些理由, 对于我来说,该属性总是返回 .F..

    if tlDisplayDialog
        .oMAPIMessage.Send(1)
    else
    .oMAPIMessage.Send()
    endif tlDisplayDialog
    if .lSignOffAfterSend
        .SignOff()
    endif .lSignOffAfterSend
endwith
受保护的 SignOn 方法, 如果我们还没有登录到 MAPI 就从 Send 方法中调用它来登录到 MAPI. 它首先确保 MAPI32.DLL 可以在 Windows System 目录中找到 (GetSystemDirectory API 函数用于定位该目录), 然后设置 MAPISession 对象的 DownloadMail 属性为 .F. (此时我们只想发送邮件) 和 LogonUI 属性为 .F. (如果我们想显示一个登录对话框让用户输入他们的名字和口令, 设置该属性为 .T.). 它调用 MAPISession 的 SignOn 方法来执行登录到 MAPI, 然后设置 SFMAPI 的 lLoggedIn 属性为 .T.(如果 MAPISession 的 SessionID 属性大于 0). 如果登录成功, 方法返回 .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')
endwith
return This.lLoggedIn
受保护的 SignOff 方法, 在 lSignOffAfterSend 属性为 .T. 时从 Send , Destroy 方法和 Error 方法中调用, 用于挂断 MAPI 和设置 lLoggedIn 属性为 .F.

with This.oMAPISession
    if .SessionID <> 0
        .SignOff()
    endif .SessionID <> 0
endwith
This.lLoggedIn = .F. 
示例表单 
TESTMAPI.SCX 是一个演示 SFMAPI 的表单. 它看起来象一个典型的客户数据输入表单, 带有名字, 地址等文本框等. 它还有一个输入客户的 e-mail 地址的文本框. 该文本框前面的标签是黑体字的 (向用户显示它是特别的) 且在其 DblClick 方法中有以下代码:


with Thisform.oMAPI
    .AddRecipient(alltrim(Thisform.txtEmail.Value))
    .Send(.T.)
endwith
当你双击该标签时, 这些代码添加 e-mail 文本框中的值作为收件人并调用 SFMAPI 控件的 Send 方法, 并传递参数 .T. 因此显示一个对话框. 注意 cSubject 和 cMessage 属性没有设置; 毕竟, 我们不知道用户想如何设置这些值, 因此它们可以在显示的对话框中输入需要的信息.
你可以为用户提供其它方法来发送一个 e-mail: 一个在 e-mail 文本框旁边的命令按钮, 一个表单上或工具条上的按钮, 一个菜单项等等. 众所周知的 VFP 头子 Markus Egger 创建了一个聪明的机制作为 Visual LandPro 的一部分, ( Visual LandPro 是一个帮助开发的应用程序, 曾被选为两年一度的 VFP Excellence Awards(VFP 杰出成就奖)决赛程序). 它创建一个显示 e-mail 地址为一个热连接的文本框 (就是说, 它在白色的背景中显示一个兰色的下划线文本). 用户单击它, 就象它们是一个热连接一样, 要发送一个信息到该地址. 关于他的类中的伟大的东西是你甚至不必向大数用户解释它是如何工作的. 
结论 
由于添加 e-mail 能力到一个应用程序简单到拖动一个 SFMAPI 对象到表单中, 设置它的属性, 并在用户指出他们想发送一个 e-mail 时调用一些方法, 没有理由不添加这种能力到那怕是最简单的应用程序中.
.
  例子下载 http://www.vfptop.com/download/mapi.zip

#4


另一種方法,直接調用outlook,就像調用excel、word一樣:
lol_outlook=createobject('outlook.application')
lol_email=lol_outlook.createitem(0)
with lol_email
.recipients.add('aaa@yahoo.com.cn')
.subject='subject'
.body='body'
.send
endwith
release lol_outlook
release lol_email

#5


*另一種方法,直接調用outlook,就像調用excel、word一樣

lol_outlook=createobject('outlook.application')
lol_email=lol_outlook.createitem(0)
with lol_email
.recipients.add('aaa@yahoo.com.cn')
.subject='subject'
.body='body'
.send
endwith
release lol_outlook
release lol_email

#6


楼上的用法是简单,但一定要先打开OUTLOOK的。这不太合我的要求,
还有其它隐性发布邮件的的方法吗,同时还要不用打开OUTLOOK的,所有信息只要在程序上写就可以的?

#7


楼上的用法是简单,但一定要先打开OUTLOOK的。这不太合我的要求,
还有其它隐性发布邮件的的方法吗,同时还要不用打开OUTLOOK的,所有信息只要在程序上写就可以的?

---------------
不打開OUTLOOK也可以呀,否則用: mgan(很笨 ,很笨的鸟! 想先飞) 的方法最好
所有信息只要在程序上写就可以的?  以上均是呀!

#8


老兄,我不太明白,有例程吗,写给我行吗?

#9


把我也搞糊涂了,你關掉outlook,運行以下代碼不就明白了:

lol_outlook=createobject('outlook.application')
lol_email=lol_outlook.createitem(0)
with lol_email
.recipients.add('aaa@yahoo.com.cn')
.subject='subject'
.body='body'
.send
endwith
release lol_outlook
release lol_email

#10


老兄,这个我试过了,但运行后系统好象在等待运行OUTLOOK一样,不会运行完毕,这里一定有问题存在。
同时我强行关闭VFP后,打开OUTLOOK发现根本不存在程序发出的邮件。

但是我在打开OUTLOOK的情况下,程序很快就运行完毕,而且还发出了邮件。

这会是什么原因呢??

#11


我關閉outlook,运行上述代碼后,系统是好象在等待运行OUTLOOK一样(因為要創建ole),
但是可以運行完畢,再开OUTLOOK可以发现程序发出的邮件,
你强行关闭VFP后,程序沒運行完,打开OUTLOOK,當然发现不了程序发出的邮件。
如果覺得慢,用mgan(很笨 ,很笨的鸟! 想先飞) 的方法試一試

#12


关注

#13


关注

#14


这个问题,放了这么久,还没有一个好的答案。难道就这样结了贴吗?

#15


把你的备份程序发给我一份好吗?
huhuyuan@163.com
谢谢!

#16


我也想要。。。。因为我的程序里,可能需要写到备份!  谢谢!

 lovemm-2002@163.com

#17


MAPI
是要用Outlook的
要不用Outlook直接用stmp,和pop协议

#18


我也要想要一份备分的,如果有EAMIL的更好,能发一份给我吗?
我的邮箱地址 jinxianjaxi2001@sina.com.cn