技术开发 频道

Outlook2007携手.Net2.0打造邮件列表服务器



二、接收邮件

    在上面已经提及,System.NET.Mail命名空间并不包含用于接收邮件的类。这是因为读取邮件需要处理的东西远比我们想象的多,如需要分析MIME信息格式。这就意味着我们需要找到其他的方法从邮件服务器中读取邮件。

    一个非常廉价的且可行的解决方案是利用Outlook,我们可以通过Outlook下载所有的邮件,并在.NET应用程序中通过编程来访问它们。使用这个方法,我们可以远离那些和邮件服务器进行通讯和分析信息的繁琐的工作,而这些都将由Outlook代劳。

在本文中,我们将使用邮件列表别名建立一个邮件帐号。为了避免不必要的麻烦,我建议建立一个Gmail帐号。然后使用我们建立的GMail帐号来配置Outlook2007(当然,我们也可以使用Outlook2003)来从这个帐号下载邮件。关于如何在Outlook上配置GMail,我们可以通过如下的url获得详细的步骤:
   
http://mail.google.com/support/bin/answer.py?ctx=%67mail&hl=en&answer=12103

    一但帐户设置完成,就可以用Outlook来验证一下是否可以正常来接收和发送邮件。一但测试成功,我们就可以进行下面的操作了。

    在这一步中我们将通过编程方式使用Outlook来访问邮件。在编程之前,建议在Tools>Trust Center...中选择"Never warn me about suspicious activity (not recommended)"选项,如图1所示:



图1 关闭安全警告对话框

    这么做是因为我们的应用程序每一次使用Outlook来访问邮件时,都会提示一个安全警告对话框,因此要关闭它。

下面我们使用Visual Studio 2005来建立一个Windows应用程序,并将其命名为MailingListServer。并在默认生成的Form1中放置以下控件,如图2所示:

1. 按钮控件
2. 标签控件
3. 文本框控件
4. 列表框控件



                                                   2 Form1中的控件位置和布局



   
Form1中最上面的文本框将允许我们向列表成员发送信息。左下角的列表框显示了当参与订阅的用户。右下角将显示用户发送的订阅和退阅的信息。下面我们在VS2005中引用Outlook的库,库名为Microsoft.Office.Interop.Outlook library,如图3所示:



图3 在VS2005中引用Microsoft.Office.Interop.Outlook library

然后在Form1中导入如下的命名空间:

Imports System.Data
Imports System.Data.SqlClient
Imports Microsoft.Office.Interop
Imports System.Net

Imports System.Net.Mail

接下来,在Form1中声明成员变量,代码如下:

Public Class Form1 Const SMTP_SERVER As String = "smtp.gmail.com" Const SMTP_PORT As Integer = 587 Const USER_NAME As String = "user_name@gmail.com" Const PASSWORD As String = "password" Private WithEvents outlookApp As Outlook.ApplicationClass '---保存引入的Mail--- Private EmailQueue As New Queue Private SMTPClient As New SmtpClient(SMTP_SERVER, SMTP_PORT) '访问数据库 Private conn As New SqlConnection( _ "Data Source=.\SQLEXPRESS;AttachDbFilename=" & _ Application.StartupPath & "\Database1.mdf;" & _ "Integrated Security=True;User Instance=True") Private sqlCmd As SqlCommand = Nothing

    上面的代码有一点需要说明一下。Gmail的SMTP服务器的端口号是587,而不是默认的25。当我们在设置Outlook时也可以用465,但在编程时必须用587。

    接下来,在我们的工程中加入一个SQL Server数据库(在Solution浏览器中右击工程名,然后选择Add > New Item...)。选择SQL数据库,并使用默认的Database1.mdf。一但数据库被加到工程中,就可以双击并编辑它了。在本例子中我们向这个数据库加一个表(这个表非常简单,只有一个保存用户mail的字段EmailAddress字段),到现在为止,数据库和表都已经建立完成了。接下来我们来定义一个Delegate和一个用于更新两个列表框的子程序,代码如下:

'---用于更新两个列表框的子程序--- Private Delegate Sub MyDelegate() Private Sub UpdateStatus() '---更新队列状态列表框控件--- lstQueue.DataSource = EmailQueue.ToArray Try '---更新用户列表框控件--- sqlCmd = New SqlCommand( _ "SELECT * FROM Users", conn) conn.Open() Dim reader As SqlDataReader reader = sqlCmd.ExecuteReader lstUsers.Items.Clear() While reader.Read lstUsers.Items.Add(reader(0)) End While Catch ex As Exception Console.WriteLine(ex.ToString) Finally conn.Close() End Try End Sub
    从上面的代码可以看出,UpdateStatus()子程序更新了两个列表框控件:lstUsers和lstQueue。在lstUsers控件中显示了在数据库中保存的所有的邮件地址。而在lstQueue控件 中显示了所有被用户发送的订阅和退订请求。这些我们在后面将会更详细的讨论。

    当窗体首先被装载时,我们建立了一个Outlook.ApplicationClass类的实例,以便我们可以使用Outlook接收邮件。另外,我们还配置了SMTPClient对象,以便用它来给用户发送信息,代码如下:
Private Sub Form1_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load outlookApp = New Outlook.ApplicationClass() ' 设置SMTPClient With SMTPClient .UseDefaultCredentials = False .EnableSsl = True .Credentials = New NetworkCredential(USER_NAME, PASSWORD) End With ' 更新队列状态 lstQueue.BeginInvoke(New _ MyDelegate(AddressOf UpdateStatus), _ New Object() {}) End Sub

    当Outlook接收到新的邮件信息时,ApplicationClass对象在接收每一个新邮件后将会触发NewMailEx事件。这个事件通过EntryIDCollection参数传递了新邮件的标识。我们可以在这个事件中提取这些信息,代码如下:

'---当新的邮件来时触发这个事件--- Private Sub outlookApp_NewMailEx(ByVal EntryIDCollection As String) _ Handles outlookApp.NewMailEx Dim OutlookNS As Outlook.NameSpace OutlookNS = outlookApp.GetNamespace("MAPI") '---得到收件箱邮件夹--- Dim Inbox As Outlook.MAPIFolder Inbox = outlookApp.Session.GetDefaultFolder( _ Outlook.OlDefaultFolders.olFolderInbox) '—获得新邮件--- Dim NewMail As Outlook.MailItem NewMail = OutlookNS.GetItemFromID(EntryIDCollection, Inbox.StoreID) ' 将email和标题保存在队列中 Dim str As String = _ NewMail.SenderEmailAddress & "," & NewMail.Subject EmailQueue.Enqueue(str) ' 更新队列状态 lstQueue.BeginInvoke(New _ MyDelegate(AddressOf UpdateStatus), _ New Object() {}) End Sub

    在这里要注意的是,我们现在获得订阅者的邮件地址和标题,并将他们作为字符串保存在队列数据结构中,如图4所示。但是为什么不立即处理它们呢?这是因为Outlook在接受多封邮件时,如果在事件处理中有延迟,这个事件将不会被触发多次。因此,就只有将这些事件句柄释放,以便在接收到下一个邮件时仍然可以触发这个事件。



 用于保存订阅和退订动作的队列

   
现在订阅和退订的动作已经保存在队列中了,那么什么时候处理它们呢?最简单的方法是使用一个定时器控件。我们在Form1上加一个Timer控件,并设置Enable属性为TrueInterval5000(5),如图5所示:




 

 图5 Timer控件的属性设置

现在这个Timer控件的Tick事件每5秒执行一次,在这个事件中我们将处理事件队列中订阅和退订的请求。代码如下:

'---每5秒处理一次请求--- Private Sub Timer1_Tick( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles Timer1.Tick Timer1.Enabled = False While EmailQueue.Count > 0 '---从队列中获得请求信息 Dim Email As String = EmailQueue.Dequeue '---格式化邮件地址和标题 Dim fields As String() = Email.Split(",") '---处理订阅和退订请求 HandleSubscription(fields(0), fields(1)) '---更新lstQueue lstQueue.BeginInvoke(New _ MyDelegate(AddressOf UpdateStatus), _ New Object() {}) End While Timer1.Enabled = True End Sub


在上面的代码中我们将客户端发来的请求依次地出列,并调用HandleSubscription()子程序来处理它们,这个子程序的代码如下:

' 处理订阅和退订 Public Sub HandleSubscription( ByVal email As String, ByVal subject As String) Select Case UCase(subject) Case "SUBSCRIBE" ' 如果用户是订阅的,将其插入数据库中 sqlCmd = New SqlCommand( _ "INSERT INTO Users VALUES ('" & email & "')", conn) SendEmail( _ email, _ "Welcome to the mailing list", _ "You have been subscribed...") Case "UNSUBSCRIBE" '如果用户是退订,将数据从数据库中删除 sqlCmd = New SqlCommand( _ "DELETE FROM Users WHERE EmailAddress='" & email & _ "'", conn) SendEmail(email, _ "You have been unsubscribed", _ "You have been unsubscribed...") End Select '更新用户数据库 SyncLock EmailQueue Try conn.Open() sqlCmd.ExecuteNonQuery() Catch ex As Exception Console.WriteLine(ex.ToString) Finally conn.Close() End Try End SyncLock End Sub

     从上面的代码我们可以看出,如果用户使用标题"subscribe"来发送email,我们将这个用户邮件地址保存在数据库中,然后通过SendEmail()子程序给用户回复订阅成功(这一步将在下面详细讨论)。如果邮件标题" unsubscribe",那么我们将从数据库中删除这个邮件地址。

0
相关文章