vc编写电子邮件程序

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

VC++编写电子程序

.diybl.时间:2008-08-27作者:佚名编辑:本站点击:635 [评论]

VC++编写电子程序

一、概述

---- 本文主要讲述如何使用Visual C++用MAPI编写程序。MAPI是包含在Windows之中的,因此不需要安装其他额外的部件。MAPI有以下三种形式:

SMAPI,Simple MAPI,简单的MAPI

CMC,mon Messaging Calls,一般通讯调用

完整的MAPI

---- SMAPI和CMC都包含在完整的MAPI中,当用户想执行一些高级操作,比如编写自己的服务器的时候,

必须使用完整的MAPI。本文主要阐述如何编写能够收发电子的程序,因此使用SMAPI就足够了。

二、编写电子程序

3-1 初始化MAPI

---- 要使用MAPI,必须首先对它进行初始化。初始化包括以下三个步骤:

装载MAPI32.DLL动态库

找到想要调用的MAPI函数地址

登录到电子对象

3-1-1 装载MAPI32.DLL

---- 要装载MAPI,用户必须程序运行时动态的装载一个动态库。LoadLibrary函数提供了此功能,

它定位一个动态库,并返回HINSTANCE局柄(需要保存该句柄)。

LoadLibrary的语法如下:

LoadLibrary ( lpLibFileName );

其中lpLibFileName为LPCTSTR结构变量,

是所要调用的库的路径和名称。

程序示例:

// 调用MAPI32.DLL并计算函数地址

HINSTANCE hInstMail;

hInstMail = ::LoadLibrary ( “MAPI32.DLL” );

if ( hInstMail == NULL )

{

// 错误处理

// 受篇幅限制,下面的错误处理部分省略

}

3-1-2 确定函数地址

---- 由于MAPI32.DLL是被动态装载的,因此不知道所要调用的函数地址,也就不能一开始就调用它们,

而要通过函数名获得函数的地址,并在动态库中查找每一个函数并核实。因此首先必须为这

些函数声明指针

程序示例:

// 为MAPI32.DLL中的函数声明函数指针

ULONG (PASCAL *lpfnMAPISendMail) (LHANDLE lhSession,

ULONG ulUIParam, lpMapiMessage lpMessage,

FLAGS flFlags, ULONG ulReserved);

ULONG (PASCAL *lpfnMAPIResolveName) (LHANDLE lhSession, ULONG ulUIParam, LPTSTR lpszName,

FLAGS ulFlags, ULONG ulReserved,

lpMapiRecipDesc FAR *lppRecip);

ULONG (FAR PASCAL *lpfnMAPILogon)(ULONG ulUIParam,

LPSTR lpszProfileName, LPSTR lpszPassword,

FLAGS flFlags, ULONG ulReserved,

LPLHANDLE lplhSession);

ULONG (FAR PASCAL *lpfnMAPILogoff)(LHANDLE lhSession,

ULONG ulUIParam, FLAGS flFlags,

ULONG ulReserved);

ULONG (FAR PASCAL *lpfnMAPIFreeBuffer)(LPVOID lpBuffer);

ULONG (FAR PASCAL *lpfnMAPIAddress)(LHANDLE lhSession,

ULONG ulUIParam, LPSTR lpszCaption,

ULONG nEditFields, LPSTR lpszLabels,

ULONG nRecips, lpMapiRecipDesc lpRecips,

FLAGS flFlags, ULONG ulReserved,

LPULONG lpnNewRecips,

lpMapiRecipDesc FAR *lppNewRecips);

ULONG (FAR PASCAL *lpfnMAPIFindNext)(LHANDLE lhSession, ULONG ulUIParam, LPSTR lpszMessageType,

LPSTR lpszSeedMessageID, FLAGS flFlags,

ULONG ulReserved, LPSTR lpszMessageID);

ULONG (FAR PASCAL *lpfnMAPIReadMail)(LHANDLE lhSession, ULONG ulUIParam, LPSTR lpszMessageID,

FLAGS flFlags, ULONG ulReserved,

lpMapiMessage FAR *lppMessage);

---- 为了决定每一个函数的地址,必须为每一个函数调用GetProcAddress。GetProcAddress的语法为:

GetProcAddress (hModule, lpProame);

其中,hModule为HMODULE结构,是所调用DLL模块的句柄;lpProame为LPCSTR结构,是函数名称。

程序示例:

// 找到MAPI32.DLL函数的地址,并将它们保存在函数指针变量里(FARPROC&) lpfnMAPISendMail = GetProcAddress(hInstMail, “MAPISendMail”);

(FARPROC&) lpfnMAPIResolveName = GetProcAddress(

hInstMail, “MAPIResolveName”);

(FARPROC&) lpfnMAPILogon = GetProcAddress(hInstMail,

“MAPILogon”);

(FARPROC&) lpfnMAPILogoff = GetProcAddress(hInstMail,

“MAPILogoff”);

(FARPROC&) lpfnMAPIFreeBuffer = GetProcAddress(

hInstMail, “MAPIFreeBuffer”);

(FARPROC&) lpfnMAPIAddress = GetProcAddress(hInstMail,

“MAPIAddress”);

(FARPROC&) lpfnMAPIFindNext = GetProcAddress(hInstMail,

“MAPIFindNext”);

(FARPROC&) lpfnMAPIReadMail = GetProcAddress(hInstMail,

“MAPIReadMail”);

3-1-3 登录到电子对象

---- 用户必须在电子系统中登录,才能实现MAPI的各种功能。MAPI提供了登录的三种选择:

登录到一个已经存在的对象。

登录到一个新对象,用编程的方法确定解释新信息。

使用对话框提示用户登录。

---- 我们通常选择登录到一个已经存在的电子对象,因为网络合作用户通常会保持自己的电子程序处于激活状态。登录通常使用MAPI提供的函数lpfnMAPILogon。lpfnMAPILogon的语法为:

lpfnMAPILogon (lpszProfileName, lpszPassword, flFlags,

ulReserved, lplhSession );

---- 其中,lpszProfileName指向一个256字符以内的登录名称,lpszPassword指向密码,它们均

为LPTSTR结构。flFlags为FLAGS结构,其值详见表1。ulReserved必须为0。lplhSession 为输出SMAPI的句柄。

表1:lpfnMAPILogon函数中flFlags的值

值意义

MAPI_FORCE_DOWNLOAD

在函数调用返回之前下载用户的所有。

如果MAPI_FORCE_DOWNLOAD没有被设置,

那么信件能够在函数调用返回后在后台被下载。

MAPI_NEW_SESSION 建立一个新会话,

而不是获得环境的共享会话。如果MAPI_NEW_SESSION没有被设置,

MAPILogon使用现有的共享会话。

MAPI_LOGON_UI 显示一个登录对话框来提示用户输入登录信息。

例如Outlook检查用户电子时便是如此。

MAPI_PASSWORD_UI MAPILogon只允许用户输入电子的密码,

而不许改动账号。

程序示例:

LHANDLE lhSession;

ULONG lResult = lpfnMAPILogon(0, NULL, NULL, 0, 0,

&lhSession);

if (lResult != SUCCESS_SUCCESS)

//SUCCESS_SUCCESS在MAPI.H中被定义

{

// 错误处理

}

3-2 阅读电子

---- MAPIFindNext和MAPIReadMail使用与阅读的两个基本函数。MAPIFindNext用于定位第一

封或下一封电子并返回标识号,MAPIReadMail返回以该标识号为基础的电子的内容。另外,一个常用的函数是MAPIFreeBuffer,用于释放内存。

3-2-1 定位到第一封信

---- 要找到第一封信,需要使用MAPIFindNext函数,其函数声明如下:

ULONG FAR PASCAL MAPIFindNext(LHANDLE lhSession,

ULONG ulUIParam, LPTSTR lpszMessageType,

LPTSTR lpszSeedMessageID, FLAGS flFlags,

ULONG ulReserved, LPTSTR lpszMessageID )

---- 其中,lhSession为提交SMAPI的会话句柄;ulUIParam为父窗体的句柄;lpszMessageType

指向一个字符串,用来鉴别类型,并加以查找;lpszSeedMessageID为指向起始信息ID的指针,

其值为0时,MAPIFindNext获得第一封电子;flFlags的值见表2;ulReserved必须为0;lpszMessageID为输出值,它是指向信息ID地址的指针。

---- 表2:MAPIFindNext函数中flFlags的值

值意义

MAPI_GUARANTEE_FIFO 按发送的时间顺序接受电子。

MAPI_LONG_MSGID 返回信件标识符可达512字符。

MAPI_UNREAD_ONL Y 只列举没有阅读过的电子。

程序示例:

// 找到第一条没有阅读的电子

char pMessageID [513];

ULONG lResult = lpfnMAPIFindNext(lhSession, NULL, NULL,

NULL, MAPI_LONG_MSGID | MAPI_UNREAD_ONLY,

0, pMessageID);

3-2-2 阅读信息

当信件ID被获取后,就可以调用MAPIReadMail

阅读实际的信息了。MAPIReadMail的函数声明如下:

ULONG FAR PASCAL MAPIReadMail(LHANDLE lhSession,

ULONG ulUIParam, LPTSTR lpszMessageID,

FLAGS flFlags, ULONG ulReserved,

lpMapiMessage FAR * lppMessage);

其中,lppMessage为指向MapiMessage的指针;

除flFlags外的其他参数与lpfnFindNext函数的同名参数意义相同,flFlags参数的值见表3:

表3:MAPIReadMail函数中flFlags的值:

值意义

MAPI_BODY_AS_FILE 将信息写到一个临时文件中,

并且将它作为第一个附件添加到附件列表中。

MAPI_ENVELOPE_ONL Y 只读取标题。

MAPI_PEEK 读完之后不把它标记为“已读”。

MAPI_SUPPRESS_ATTACH MAPIReadMail函数不拷贝附件,

但是将文本写入MapiMessage结构中。

程序示例:

// 读取电子

long nFlags = MAPI_SUPPRESS_ATTACH;

if (!bMarkAsRead)

nFlags = nFlags | MAPI_PEEK;

lResult = lpfnMAPIReadMail(lhSession, NULL, pMessageID,

nFlags, 0, &pMessage);

if (lResult != SUCCESS_SUCCESS);

return false;

如果调用成功,就可以访问MapiMessage结构了(使用pMessage):pMessage- >ulReserved:0

pMessage- >lpszSubject:标题

pMessage- >lpszNoteText:信息

pMessage- >lpszMessageType:类型

pMessage- >DateReceived:接收时间

pMessage- >lpszConversationID:所属的会话线程ID

pMessage- >flFlags:其值见表4

表4:MapiMessage结构中的flFlags

值意义

MAPI_RECEIPT_REQUESTED 接收通知被申请。

客户端应用程序在发送消息时设置该项。

MAPI_SENT 已被发送。

MAPI_UNREAD 是“未读”状态。

pMessage- >lpOriginator:指向MapiRecipDesc结构,包含发件人信息。pMessage- >nRecipCount:信件者数目。

pMessage- >lpRecips:指向MapiRecipDesc结构数组,包含接收者信息。pMessage- >nFileCount:附件数量。

pMessage- >lpFiles:指向MapiFileDesc结构数组,

每一个结构包含一个文件附件。

3-2-3 释放内存

---- 在访问另一条信件以前应当释放内存,否则会出现内存泄漏。

程序示例:

// 释放内存

lpfnMAPIFreeBuffer(pMessage);

3-2-4 定位到下一条信件

定位到下一条信件依然使用MAPIFindNext函数,

该函数声明及参数意义详见3-2-1节。下面示X如何定位到下一条信件。程序示例:

// 定位到下一条没有阅读的信件

ULONG lResult = lpfnMAPIFindNext(lhSession, NULL, NULL,

pMessageID, MAPI_LONG_MSGID|MAPI_UNREAD_ONL Y,

0, pMessageID);

3-3 发送电子

---- 发送电子的一般步骤:

---- 1.建立MapiMessage结构对象

---- 2.调用MAPIResolveName使发送者名称合法

---- 3.添加附件

---- 4.调用MAPISendMail发送电子

---- 5.调用MAPIFreeBuffer释放内存

---- 下面详细分别详细阐述。

3-3-1 建立MapiMessage结构对象

---- 对于MapiMessage结构,3-2-2节已经做过介绍,下面一步步介绍如何设置其中的值:---- 1.为MapiMessage对象分配内存:

MapiMessage message;

Memset(&message, 0, sizeof(message));

---- 2.将ulReserved设置为0:

message.ulReserved = 0;

---- 3.设置信息类型指针lpszMessageType,可以为NULL:

message.lpszMessageType = NULL;

---- 4.设置信件标题(lpszSubject):

char subject[512];

strcpy(subject, sSubject);

message.lpszSubject = subject;

---- 5.设置信件内容:

char text[5000];

strcpy(text, sMessage);

message.lpszNoteText = text;

---- 6.设置flFlags标识,详见3-2-2节中表4:

message.flFlags = MAPI_SENT;

---- 7.用一个指向MapiRecipDesc结构的指针设置发送者信息(lpOriginator),或将其设置为NULL:

message.lpOriginator = N

文章出处:DIY部落(.diybl./course/3_program/vc/vc_js/2008827/137716.html)

++ SMTP协议电子传送剖析

2002-11-19 18:05作者:信息产业部电子第二十二研究所青出处:yesky责任编辑:方舟

摘要:本文介绍了一种采用SMTP协议规X并通过直接使用SMTP协议命令而在程序中实现电子传送的方法。并在VC++开发环境下给出了部分关键的实现代码。

前言

电子服务作为Internet上应用最多和最广的服务项目得到了非常广泛的应用,在网络应用中也起到非常重要的作用。如同其他的网络服务,电子系统也有其使用的传输协议,包括SMTP(Simple Mail Transfer Protocol,简单传输协议)、POP(Post Office Protocol,邮局协议)和IMAP(Internet Message Access Protocal,消息访问协议)等,这些协议应用于电子的发送和接收。一些处理软件如OutLook Express和FoxMail等就是按照SMTP和POP3 协议结合Windows Sockets套接字进行设计来收发的。本文以SMTP 协议为研究对象,在Visual C++ 6.0编程环境下按照SMTP协议通过套接字发送SMTP命令,接收并处理服务器的反馈信息,从而实现对电子的发送。

SMTP协议的通讯模型和会话流程SMTP协议通讯模型

SMTP协议是TCP/IP协议族中的一员,主要对如何将电子从发送方地址传送到接收方地址,也即是对传输的规则做了规定。SMTP协议的通信模型并不复杂,主要工作集中在发送SMTP和接收SMTP上:首先针对用户发出的请求,由发送SMTP建立一条连接到接收SMTP的双工通讯链路,这里的接收SMTP 是相对于发送SMTP而言的,实际上它既可以是最终的接收者也可以是中间传送者。发送SMTP负责向接收SMTP发送SMTP命令,而接收SMTP则负责接收并反馈应答。可大致用下面的通讯模型示意图来表示:

SMTP协议的命令和应答

从前面的通讯模型可以看出SMTP协议在发送SMTP和接收SMTP之间的会话是靠发送SMTP的SMTP命令和接收SMTP反馈的应答来完成的。在通讯链路建立后,发送SMTP发送MAIL命令指令发送者,若接收SMTP此时可以接收则作出OK的应答,然后发送SMTP继续发出RCPT命令以确认是否收到,如果接收到就作出OK的应答,否则就发出拒绝接收应答,但这并不会对整个操作造成影响。双方如此反复多次,直至处理完毕。SMTP协议共包含10个SMTP命令,列表如下:

SMTP命令命令说明

HELLO <domain><CRLF>识别发送方到接收SMTP的一个HELLO命令

MAIL FROM:<reverse-path><CRLF><reverse-path>为发送者地址。此命令告诉接收方一个新发送的

注:<CRLF>为回车、换行,ASCII码分别为13、10(十进制)。

SMTP协议的每一个命令都会返回一个应答码,应答码的每一个数字都是有特定含义的,如第一位数字为2时表示命令成功;为5表失败;3表没有完成。一些较复杂的程序利用该特点,首先检查应答码的首数字,并根据其值来决定下一步的动作。下面将SMTP的应答码列表如下:

VC++ SMTP协议电子传送剖析

2002-11-19 18:05作者:信息产业部电子第二十二研究所青出处:yesky责任编辑:方舟

在应用程序中使用SMTP协议SMTP协议的会话流程

在进行程序设计之前有必要弄清SMTP协议的会话流程,其实前面介绍的内容已经可以大致勾勒出用SMTP发送的框架了,对于一次普通的发送,其过程大致为:先建立TCP连接,随后客户端发出HELLO 命令以标识发件人自己的身份,并继续由客户端发送MAIL命令,如服务器应答为"OK",可继续发送RCPT 命令来标识电子的收件人,在这里可以有多个RCPT行,而服务器端则表示是否愿意为收件人接受该。在双方协商结束后,用命令DATA将发送出去,其中对表示结束的"."也一并发送出去。随后结束本次发送过程,以QUIT命令退出。下面通过一个实例,从langruisohu.发送到renpingsina.来更详细直观地描述此会话流程:

R:220 sina. Simple Mail Transfer Service Ready

S:HELLO sohu.

R:250 sina.

S:MAIL FROM:<langruisohu.>

R:250 OK

S:RCPT TO:<renpingsina.>

R:250 OK

S:DATA

R:354 Start mail input;end with "<CRLF>.<CRLF>"

S:……

R:250 OK

S:QUIT

R:221 sina. Service closing transmission channel

的格式化

由于电子结构上的特殊性,在传输时是不能当作简单的文本来直接处理的,而必须按照一定的格式对头和体进行格式化处理之后才可以被发送。需要进行格式化的部分主要有:发件人地址、收件人地址、主题和发送日期等。在RFC文档的RFC 822里对的格式化有详尽的说明,有关详情请参阅该文档。下面通过VC++6.0按照RFC 822文档规定将格式化的部分编写如下(部分代码):

//头准备

strTemp = _T( "From: " ) + m_strFrom; file://发件人地址

add_header_line( (LPCTSTR)strTemp );

strTemp = _T( "To: " ) + m_strT o; file://收件人地址

add_header_line( (LPCTSTR)strTemp );

m_tDateTime = m_tDateTime.GetCurrentTime();//发送时间

strTemp = _T( "Data: " );

strTemp += m_tDateTime.Format( "%a, %d %b %y %H:%M:%S %Z" );

add_header_line( (LPCTSTR)strTemp );

其中add_header_line(LPCTSTR szHeaderLine)函数用于把szHeaderLine指向的字串追加到m_strHeader后面。其中,格式化后的头保存在m_strHeader里,格式化后的体保存在m_strBody中。

由Socket套接字为SMTP提供网络通讯基础

许多网络程序都是采用Socket套接字实现的,对于一些标准的网络协议如HTTP、FTP和SMTP等协议的编程也是基于套接字程序的,只是端口号不再是随意设定而要由协议来指定,比如HTTP端口在80、FTP是21,而SMTP则是25。Socket只是提供在指定的端口上同指定的服务器从事网络上的通讯能力,至于客户和服务器之间是如何通讯的则由网络协议来规定,这对于套接字是完全透明的。因此可以使用Socket套接字为程序提供网络通讯的能力,而对于网络通讯连路建立好之后采取什么样的通讯应答则要按SMTP协议的规定去执行了。Socket套接字网络编程方面的文章资料非常丰富,限于本文篇幅,在此不再赘述,有关详情请参阅相关文档。为简便起见,没有采用编写较复杂的Windows Sockets API进行编程,而是使用经过较好封装的MFC 的CSocket类。在正式使用套接字之前,也要先用AfxSocketInit()函数对套接字进行初始化,然后用Create()创建套接字对象,并由该套接字通过Connect()建立同服务器的连接。如果一切正常,再后续的工作中就是遵循SMTP协议的约定来使用Send()、Receive()函数来发送SMTP命令和接收服务器发来的应答码以完成对的传送。

SMTP会话应答的实现

在同服务器建立好链路连接后就可以按前面介绍过的会话流程进行程序设计了,对于SMTP命令的发送,可按命令格式将其组帧完毕后用CSocket类的Send()函数将其发送到服务器,并通过CSocket类的Receive()函数接收从服务器发来的应答码,并根据SMTP协议的应答码表对其做出响应的处理。下面是用于接收应答码的函数get_response()的部分实现代码:

BOOL CSMTP::get_response( UINT response_expected )//输入参数为希望的应答码

{

……

// m_wsSMTPServer为CSocket的类对象,调用Receive()将应答码接收到缓存

// response_buf中

m_wsSMTPServer.Receive( response_buf, RESPONSE_BUFFER_SIZE )

sResponse = response_buf;

sscanf( (LPCTSTR)sResponse.Left( 3 ), _T( "%d" ), &response );

pResp = &response_table[ response_expected ];

file://检验收到的应答码是否是所希望得到的

if( response != pResp->nResponse )

{

会话的各个部分比较类似,都是命令--应答方式,而且均成对出现,下面是本文的重点也是实现的关键部分--在程序控制下完成对SMTP命令的格式化以及对命令的发送和对服务器应答码的检验处理:

//格式化并发送HELLO命令,并接收、验证服务器应答码

gethostname( local_host, 80 );

sHello.Format( _T( "HELO %s\r\n" ), local_host );

m_wsSMTPServer.Send( (LPCTSTR)sHello, sHello.GetLength() );

if( !get_response( GENERIC_SUCCESS ) ) file://检验应答码是否为250

{

……

return FALSE;

}

file://格式化并发送MAIL命令,并接收、验证服务器应答码

sFrom.Format( _T( "MAIL From: <%s>\r\n" ), (LPCTSTR)msg->m_strFrom );

m_wsSMTPServer.Send( (LPCTSTR)sFrom, sFrom.GetLength() );

if( !get_response( GENERIC_SUCCESS ) ) file://检验应答码是否为250

return FALSE;

file://格式化并发送RCPT命令,并接收、验证服务器应答码

sEmail=(LPCTSTR)msg->m_strTo;

sTo.Format( _T( "RCPT TO: <%s>\r\n" ), (LPCTSTR)sEmail );

m_wsSMTPServer.Send( (LPCTSTR)sTo, sTo.GetLength() );

if(!get_response( GENERIC_SUCCESS )) file://检验应答码是否为250

return FALSE;

file://格式化并发送DATA命令,并接收、验证服务器应答码

sTemp = _T( "DATA\r\n" );

m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );

if( !get_response( DATA_SUCCESS ) ) file://检验应答码是否为354

return FALSE;

file://发送根据RFC 822文档规定格式化过的头

m_wsSMTPServer.Send( (LPCTSTR)msg->m_strHeader, msg->m_strHeader.GetLength() );

……

file://发送根据RFC 822文档规定格式化过的体

sTemp = msg->m_strBody;

if( sTemp.Left( 3 ) == _T( ".\r\n" ) )

sTemp = _T( "." ) + sTemp;

while( (nPos = sTemp.Find( szBad )) >-1 )

{

sCooked = sTemp.Mid( nStart, nPos );

sCooked += szGood;

sTemp = sCooked + sTemp.Right( sTemp.GetLength() - (nPos + nBadLength) );

}

m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );

file://发送内容数据结束标志"<CRLF>.<CRLF>",并检验返回应答码

sTemp = _T( "\r\n.\r\n" );

m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );

if( !get_response( GENERIC_SUCCESS ) )// 检验应答码是否为250

return FALSE;

到此为止,已基本在程序中体现出了SMTP协议的会话流程,能在Socket套接字所提供的网络通讯能力基础之上实现以SMTP命令和SMTP应答码为基本会话内容的通讯交互过程,从而最终实现SMTP 协议对电子的发送。

结论

电子类软件作为Internet上的应用软件,其设计开发必须符合Internet上成熟的技术规X(如RFC文档系列规X)和相关协议(如POP、SMTP、IMAP以及LDAP等)。只有在遵循了上述规X和协议的基础上进行编程才能真正实现类软件产品和服务的开放性和标准化。本文着重对SMTP协议及其在VC++编程中的应用做了介绍,并按照SMTP协议对电子的发送进行了开放性和标准性较好的程序设计。本文所述程序在Windows 98下,由Microsoft Visual C++ 6.0编译通过。

相关文档
最新文档