使用 MAPI 实现邮件发送
原作:deltacat,修改中
最后更新:2004.09.16
版权声明:随意转载,敬请保持文档及说明完整性
关键字: VC 邮件发送 MAPI
一、简述
实际上,本文的设计,所使用的只是 Simple MAPI,可以把它看作是MAPI(Messaging Application Programming Interface)的一个子集。不过大多数时候,Simple MAPI提供的功能已经足够使用。本文阐述了一个简单的,用于发送一个带附件的邮件的功能实现。如果需要更复杂的功能,我认为还是老老实实封装SMTP和POP3比较好
二、Simple MAPI 介绍
Simple MAPI 包括一组很容易使用的函数以及相关的数据结构,可以在C/C++、VB等多种语言中使用。本文是基于VC设计的。
实现一个完整的邮件发送过程,只需要一个函数“MAPISendMail()”,完整函数表及相关介绍参查阅MSDN Library->Platform SDK->Messaging and Collaboration Services。
MAPI 与邮件系统关系密切,要能够使用MAPI的功能,系统必须安装有支持 MAPI 的邮件系统,比如Outlook、Outlook Express、Eudora、Netscape等. 在这里不得不提一下,使用非常广泛的 FoxMail (5.0版本)似乎并不支持 MAPI,不过并没有深入研究,如果有哪位高手发现实际上是支持的,麻烦告知我如何做。
Windows提供了一个文件 MAPI.H,包含所有的相关数据类型的定义。
三、功能的设计
我需要实现如下功能:可定制一封邮件,包括至少一个收件人,可选项目有标题、正文、若干抄送人、密件抄送人、附件。能够根据需要选择自动发送(无用户干预),或弹出邮件编写窗口。
为了便于使用和扩展,我用了一个class来实现。
1、MAPI库的初始化
包括两个步骤:装载MAPI库、得到函数入口地址。
//-------------------------------------------------------------------------------------
m_hLibrary = ::LoadLibrary(_T("MAPI32.DLL"));
if(NULL == m_hLibrary)
{
return ::GetLastError();
}
// LPMAPISENDMAIL 等均定义在 <MAPI.H>
m_lpfnMAPISendMail = (LPMAPISENDMAIL)::GetProcAddress(m_hLibrary, _T("MAPISendMail"));
// 可以根据需要添加其他函数入口。我的做法是用了一个 InitMapi() 函数,一次性将所有函数入口得出,作为类的成员变量保存。随时可以使用
//-------------------------------------------------------------------------------------
2、发送邮件 MAPISendMail()
发送邮件功能就是对MAPISendMail()的封装。下面解释这个API函数的参数定义。
ULONG FAR PASCAL MAPISendMail(LHANDLE lhSession, ULONG ulUIParam, lpMapiMessage lpMessage, FLAGS flFlags, ULONG ulReserved);
lhSession、ulUIParam和ulReserved这三个参数,简单地设置为0就可以了。
flFlags有三个有效位,分别是 MAPI_DIALOG,MAPI_LOGON_UI,MAPI_NEW_SESSION。
重点是 lpMessage 这个参数,它指向一个MapiMessage类型的结构,详细地定义了一个邮件的全部信息。
3、结构 MapiMessage
使用时首先定义一个MapiMessage类型的变量,将其清零。现在我们只要简单地设定lpszSubject(标题)、lpszNoteText(正文)、lpOriginator(发件人)、nRecipCount(收件人计数,包含TO、CC、GCC),lpRecips(一个包含全部收件人的数组),nFileCount(附件计数)、lpFiles(包含每个附件信息的数组)。
nRecipCount 和 nFileCount 的值一定要与实际的收件人和附件数目相符。
4、结构 MapiRecipDesc
必须设置 ulRecipClass、lpszName、lpszAddress
5、结构 MapiFileDesc
必须设置的有lpszPathName、nPosiotion两个参数
这里有个地方需要特别注意。我在实际编码过程中,有几次 Outlook 不能正确弹出窗口(自动发送也不行),但是同样的过程,将邮件客户设为 OutlookExpress 就没有问题。但是十分头痛。经过发付查找,发现 nPosiotion 这个参数十分重要。它指示附件在邮件中的位置。
对于一些邮件客户端,比如OutlookExpress,也许忽略了这个参数,由客户端自动安排。所以没有问题。对于另一些客户端,比如Outlook,总是按照这个值的指示来安排的。如果附件数多于一个,这个值如果相同,那么就会造成错误。但是自行计算挺麻烦而且没什么意义。解决的方法是,将其设为 -1,指示客户软件自行安排。:)
四、如何工作?
MAPISendMail() 会调用系统默认的邮件客户程序来发送邮件。对于弹出编辑窗的方式,它的行为和另一个函数MAPISendMail()差不多,只是可以定制标题、收件人等等,而MAPISendDocuments()只是简单地准备一个空白的邮件(包含附件),有关MAPISendDocuments()的介绍参见MSDN。
对于自动发送。需要在Outlook Express的安全设置中,取消“当有其他应用程序试图发送邮件时警告”这个选项。对于Outlook,还没有找到方法。
我设计的类包括三个接口函数,Send(), AddFiles(), AddRecips(),其中只有Send()是必须的。使用时声明一个对象,然后就直接调用Send()函数发送邮件。两个Add函数只要根据需要在Send()之前调用即可。
五、遗留问题
我的开发环境是 Windows Xp Sp2 CHS,有如下几个问题,希望有高手可以解决。
1、自动发送时的警告问题。
2、默认Outlook Express为系统邮件客户端,可以立即发送,如果是Outlook,是先放到Outlook的发送队列了,这时如果Outlook未运行,就一直不会发送。怎样可以保证无论邮件客户软件是否在运行,我的程序都可以立即将邮件发送出去呢?
3、对于默认是“Hotmail”的情况,只有以 @hotmail.com 结尾的帐号可以正常发送,而 @msn.com 的则不行。同时,在发送时,hotmail会将硬盘上的附件改名(末尾添加“^”符号)并将文件属性改为只读。
六、结束语
本文是在给应用程序中添加邮件发送功能的心得。过程中遇到了很多问题,将他写出来,主要是给自己一个记录,也是我第一次将自己的编程过程整理成文档。
若本文还能帮到有同样需要的朋友,会令我很开心。也希望有这方面经验的朋友可以解决我的遗留问题。
欢迎发送邮件至 catking@163.com 共同探讨编程中的各种问题,分享乐趣。