使用 MAPI 实现邮件发送

作者在 2009-02-15 20:58:49 发布以下内容

一、简述

实际上,本文的设计,所使用的只是 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会将硬盘上的附件改名(末尾添加“^”符号)并将文件属性改为只读。

vc | 阅读 5123 次
文章评论,共0条
游客请输入验证码