第8章邮件接收和发送客户端•邮件接收和发送客户端的作用是在本地计算机和远程计算机之间传送电子信件以及接收电子信件。用户平时所用的Foxmail就是一种邮件接收和发送客户端。通常情况下,Foxmail由发送者将电子信件发送到邮件服务器(SMTP)中,再由SMTP服务器将该邮件发送到POP3(接收邮件)服务器中,邮件接收者通过账户和口令再从POP3服务器中获取信件。在本章中,将向用户介绍邮件接收和发送客户端的原理以及开发过程。8.1调用Windows自带的邮件发送程序•一般情况下,用户所使用的Windows操作系统中都带有默认的邮件发送程序。通过该邮件发送程序,用户可以将邮件发送到任何目的地址。这种方法比较简单适用,所以很受大部分用户欢迎。用户可以在操作系统中,使用操作系统命令打开邮件程序。如果用户需要在自己的程序中调用系统自带的邮件程序,那么需要使用函数CreateProcess()或者ShellExecute()进行调用。下面将分别介绍这两种方法。8.1.1调用Windows自带程序•在Windows操作系统中,所有的程序都是以进程为单位运行。本节中所讲述的调用邮件发送程序就是通过调用相应的Windows进程实现的。调用该Windows进程所使用的命令是“mailto:+string”,其中,string表示邮件发送的目的地址。例如,用户需要将邮件发送到邮件地址为lymlrl@163.com的邮箱中,使用的命令是“mailto:lymlrl@163.com”。•首先,在Windows系统界面下选择“开始”|“运行”命令,弹出“运行”对话框,如图8.1所示。图8.1“运行”对话框•然后,在运行对话框中输入命令“mailto:lymlrl@163.com”,可以打开Windows自带的邮件发送程序进行邮件发送,如图8.2所示。图8.2Windows邮件收发器•以上过程是用户通过Windows命令调用邮件收发器必须做的。实际上,除了这种方法,用户还可以在程序中通过函数调用Windows邮件收发器。此种方法将在8.1.2节中进行讲解。8.1.2CreateProcess()函数•在VC中编程,MFC类库已经提供了几个库函数用于调用Windows的外部程序,包括邮件收发程序。在本节中,将向用户介绍其中的两个函数CreateProcess()和ShellExecute()。1.使用CreateProcess()函数•CreateProcess()函数可以创建Windows进程,同时也可以调用已经存在的进程。该函数的原型如下:•BOOLCreateProcess(•LPCTSTRlpApplicationName,•LPTSTRlpCommandLine,•LPSECURITY_ATTRIBUTESlpProcessAttributes,•LPSECURITY_ATTRIBUTESlpThreadAttributes,•BOOLbInheritHandles,•DWORDdwCreationFlags,•LPVOIDlpEnvironment,•LPCTSTRlpCurrentDirectory,•LPSTARTUPINFOlpStartupInfo,•LPPROCESS_INFORMATIONlpProcessInformation•);•该函数创建进程成功则返回true,否则返回false。其参数意义如下:•参数lpApplicationName表示可执行文件的名字。用户指定该参数后,该函数会在当前路径下搜索可执行文件,但不会按照系统的搜索路径进行搜索。•注意:使用该参数时,需要加上扩展名,因为系统不会自动为其添加“.exe”后缀名。•参数lpCommandLine表示将要传递到新进程的命令行字符串。使用该参数时,该函数会自动为其添加后缀名“.exe”。如果参数字符串没有指定所在路径,那么该函数则会按照系统的搜索路径进行搜索文件。•参数bInheritHandles表示该进程创建的子进程是否能继承父进程的对象句柄。•参数lpStartupInfo指向结构体STARTUPINFO的指针变量。该结构体的声明如下:•typedefstruct_STARTUPINFO{•DWORDcb;//表示该结构体的大小•LPTSTRlpReserved;//保留,必须将该参数初始化为NULL•LPTSTRlpDesktop;•LPTSTRlpTitle;//设置控制台程序的名称•DWORDdwX;//设置应用程序窗口的X坐标•DWORDdwY;//设置应用程序窗口的Y坐标•DWORDdwXSize;//设置应用程序窗口的横向大小•DWORDdwYSize;//设置应用程序窗口的纵向大小•DWORDdwXCountChars;//以字符为单位设置应用程序窗口的X坐标•DWORDdwYCountChars;//以字符为单位设置应用程序窗口的Y坐标•DWORDdwFillAttribute;//设置应用程序窗口所使用的背景色等•DWORDdwFlags;//表示创建窗口的标志•WORDwShowWindow;//是否显示应用程序窗口•WORDcbReserved2;//保留,将该参数必须设置为0•LPBYTElpReserved2;//保留,将该参数必须设置为0•HANDLEhStdInput;//设置控制台程序的输入输出缓存句柄•HANDLEhStdOutput;•HANDLEhStdError;//错误输出句柄•}STARTUPINFO,*LPSTARTUPINFO;•该结构体主要用于保存新创建进程的窗口信息,如窗口的大小或窗口的显示方式等。其中,参数dwFlags标识了窗口创建成功以后,在显示之前以何种方式进行显示。其取值如表8.1所示。表8.1程序窗口显示标志取值•注意:在表8.1中所示的程序窗口显示标志的作用仅仅是为了控制相应的成员变量是否有效而已。例如,用户在程序中,需要使用到该结构体中的dwFillAttribute成员。那么,用户必须将参数dwFlags取值为STARTF_USEFILLATTRIBUTE。否则,该成员变量将无效。•参数lpProcessInformation是指向结构体PROCESS_INFORMATION的指针变量。该结构体声明如下:•typedefstruct_PROCESS_INFORMATION•{•HANDLEhProcess;//进程句柄•HANDLEhThread;//线程句柄•DWORDdwProcessId;//进程ID•DWORDdwThreadId;//线程ID•}PROCESS_INFORMATION;取值含义STARTF_USESIZE使用dwXSize和dwYSize成员STARTF_USESHOWWINDOW使用wShowWindow成员STARTF_USEPOSITION使用dwX和dwY成员STARTF_USECOUNTCHARS使用dwXCountChars和dwYCountChars成员STARTF_USEFILLATTRIBUTE使用dwFillAttribute成员STARTF_USESTDHANDLES使用hStdInput、hStdOutput、hStdError成员STARTF_RUN_FULLSCREEN以全屏方式启动程序•该结构体主要用于保存进程的相关信息。其他参数均可以默认设置为NULL。例如,调用操作系统的记事本程序。代码如下:•01...//省略部分代码•02STARTUPINFOsi={sizeof(si)};•//定义结构体变量•03PROCESS_INFORMATIONpi;//定义结构体对象•04CString*str=notepad”;•//记事本名称•05CreateProcess(NULL,str,NULL,NULL,false,NULL,NULL,NULL,&si,&pi);•06//调用函数打开记事本程序•07...//省略部分代码•同样的道理,用户在本例中,也可以使用函数CreateProcess()调用邮件收发程序。代码如下:•01...//省略部分代码•02STARTUPINFOsi={sizeof(si)};•//定义结构体变量•03PROCESS_INFORMATIONpi;•04CString*str=mailto:lymlrl@163.com;•//打开邮件程序的系统命令•05CreateProcess(NULL,str,NULL,NULL,false,NULL,NULL,NULL,&si,&pi);•06//调用函数打开记事本程序•07...//省略部分代码2.使用ShellExecute()函数•在MFC编程中,除了函数CreateProcess()以外,还可以调用函数ShellExecute()实现相同的功能。该函数原型如下:•HINSTANCEShellExecute(•HWNDhwnd,•//父窗口句柄•LPCTSTRlpOperation,•//将要进行的操作形式•LPCTSTRlpFile,•//目录文件名称或文件路径•LPCTSTRlpParameters,•//传递的参数•LPCTSTRlpDirectory,•//一般为NULL•INTnShowCmd•//显示方式•);•该函数执行成功会返回调用程序的应用程序指针,否则返回错误代码。部分错误代码如表8.2所示。表8.2部分错误代码错误代码意义ERROR_FILE_NOT_FOUND找不到相应文件ERROR_PATH_NOT_FOUND找不到所需路径ERROR_BAD_FORMAT无效的.exe文件SE_ERR_ASSOCINCOMPLETE无效的文件名0操作系统的内存溢出•该函数各个参数的说明已在函数原型中标出。使用该函数调用操作系统自带的邮件发送程序,代码如下:•以上代码是使用C语言编写,并且使用命令行窗口界面,目的是为了让用户了解整个调用过程。在随书光盘的第8章中附有代码,请用户自行参考。此段代码在VC中编译后的结果,如图8.3所示。用户在运行界面1中输入字符Y或y,然后按下Enter键。程序提示邮件程序正在打开,当邮件程序打开以后,实例程序会提示已经打开邮件程序,如图8.4所示。图8.3运行界面1图8.4运行界面2•注意:在程序中为了模拟计算机的工作,所以笔者使用了while循环产生时间差,仅仅是为了让用户重复了解该调用过程。在实际编程中,不提倡使用该方法产生时间差,因为这种方法很危险,容易造成系统的崩溃。通常,使用多线程编程的方法比较安全,也是笔者极力推荐的一种方法。该类方法将在后面的相关章节中讲述。8.2SMTP会话过程•SMTP是发送邮件协议,与前面所讲的FTP、HTTP等协议一样被用作某种行为的规范标准。本节的主要内容就是向用户讲解邮件客户端怎么连接SMTP服务器以及向SMTP服务器发送信件等操作。8.2.1怎么连接服务器•在网络中传输邮件信息都是基于TCP/IP协议的,所以用户在Windows操作系统中编写邮件发送程序时可以使用Windows套接字来完成。一般情况下,客户端连接服务器的几个步骤如下。•(1)客户端指定IP地址和端口连接服务器。•(2)服务器收到连接请求,并同意客户端连接请求。•(3)客户端和服务器互相发送数据。•(4)关闭服务器和客户端的套接字。•基于以上几个步骤,用户可以VC中编写程序实现邮件客户端。1.创建套接字对象•该实例与一般网络程序一样,需要Windows套接字的支持,所以用户应该首先初始化套接字库。代码如下:•01BOOLCMyEMAIL::OnInitDialog()•02{•03WSADATAdata;•04WORDss=MAKEWORD(2,0);//指定套接字库版本•05::WSAStartup(ss,&data);//初始化套接字库•06}•用户初始化套接字库以后,还必须记得在程序退出之前释放该套接字库。代码如下:•01voidCMyEMAIL::OnClose()•02{•03::WSAC