【IT168 专稿】
如果你是一个开发人员,那么你一定对邮件列表并不陌生。一个邮件列表包含了一组用户,这些用户通过邮件订阅了指定内容的邮件内容。在通常情况下,处理是自动进行的,也就是我们通过发送一个email请求将自己的mail加到列表中,如果退订,可以再次发送这个email即可。邮件列表通过由列表服务器系统管理,这套系统我们可以通过购买方式获得。而在本文中为读者展示了如何建立自己的邮件列表服务器,在本例子中使用了.NET2.0新提供的System.Net.Mail命名空间,还有其他的一些新技术,如从Outlook2007中获得email等。对于一个完整的邮件列表服务器来说,需要如下的三个功能:
1. 向列表成员发送信息
2. 接收来自用户的订阅和退订请求
对于发送email来说是非常容易的,我们可以使用System.NET.Mail命名空间很容易地做到。然而,这个命名空间仅仅允许我们发送邮件,但却不支持接收邮件。因此,在本文中将演示如何通过和Outlook2007集成来克服这个障碍。
在上面已经提及,System.NET.Mail命名空间并不包含用于接收邮件的类。这是因为读取邮件需要处理的东西远比我们想象的多,如需要分析MIME信息格式。这就意味着我们需要找到其他的方法从邮件服务器中读取邮件。
一个非常廉价的且可行的解决方案是利用Outlook,我们可以通过Outlook下载所有的邮件,并在.NET应用程序中通过编程来访问它们。使用这个方法,我们可以远离那些和邮件服务器进行通讯和分析信息的繁琐的工作,而这些都将由Outlook代劳。
一但帐户设置完成,就可以用Outlook来验证一下是否可以正常来接收和发送邮件。一但测试成功,我们就可以进行下面的操作了。
这么做是因为我们的应用程序每一次使用Outlook来访问邮件时,都会提示一个安全警告对话框,因此要关闭它。
1. 按钮控件
图2 Form1中的控件位置和布局
在Form1中最上面的文本框将允许我们向列表成员发送信息。左下角的列表框显示了当参与订阅的用户。右下角将显示用户发送的订阅和退阅的信息。下面我们在VS2005中引用Outlook的库,库名为Microsoft.Office.Interop.Outlook library,如图3所示:
然后在Form1中导入如下的命名空间:
Imports System.Net.Mail 接下来,在我们的工程中加入一个SQL Server数据库(在Solution浏览器中右击工程名,然后选择Add > New Item...)。选择SQL数据库,并使用默认的Database1.mdf。一但数据库被加到工程中,就可以双击并编辑它了。在本例子中我们向这个数据库加一个表(这个表非常简单,只有一个保存用户mail的字段EmailAddress字段),到现在为止,数据库和表都已经建立完成了。接下来我们来定义一个Delegate和一个用于更新两个列表框的子程序,代码如下: 当Outlook接收到新的邮件信息时,ApplicationClass对象在接收每一个新邮件后将会触发NewMailEx事件。这个事件通过EntryIDCollection参数传递了新邮件的标识。我们可以在这个事件中提取这些信息,代码如下: 在这里要注意的是,我们现在获得订阅者的邮件地址和标题,并将他们作为字符串保存在队列数据结构中,如图4所示。但是为什么不立即处理它们呢?这是因为Outlook在接受多封邮件时,如果在事件处理中有延迟,这个事件将不会被触发多次。因此,就只有将这些事件句柄释放,以便在接收到下一个邮件时仍然可以触发这个事件。
接下来,在Form1中声明成员变量,代码如下:
图4 用于保存订阅和退订动作的队列
现在订阅和退订的动作已经保存在队列中了,那么什么时候处理它们呢?最简单的方法是使用一个定时器控件。我们在Form1上加一个Timer控件,并设置Enable属性为True,Interval为5000(5秒),如图5所示:
现在这个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",那么我们将从数据库中删除这个邮件地址。
三、发送邮件
接收邮件是一件非常复杂的工作,但发送邮件却是非常的容易。我们所需要的就是使用SmtpClient对象和MailMessage发送邮件,这两个类都在System.Net.Mail命名空间中。在上面代码中调用的SendEmail()子程序的代码如下:
' 发送邮件 Private Sub SendEmail( _ ByVal Email As String, _ ByVal Subject As String, _ ByVal Msg As String) Try Dim mailmessage As New MailMessage With mailmessage .From = New MailAddress(USER_NAME) ' 发送到所有的订阅用户 If Email = String.Empty Then sqlCmd = New SqlCommand( _ "SELECT * FROM Users", conn) Dim reader As SqlDataReader conn.Open() reader = sqlCmd.ExecuteReader While reader.Read .To.Add(reader(0)) End While conn.Close() Else .To.Add(Email) End If ' 标题 .Subject = Subject ' 邮件内容 .Body = msg End With SMTPClient.Send(mailmessage) Catch ex As Exception Console.WriteLine(ex.ToString) End Try End Sub
'---向所有的订阅用户发送信息--- Private Sub btnSendMessage_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnSendMessage.Click SendEmail(String.Empty, txtSubject.Text, txtMessage.Text) MsgBox("Message sent!") End Sub
四、测试应用程序
现在我们已经完成了这个程序的所有功能。接下来让我们来测试它。对于本例,必须确认在运行我们的程序之前先要运行Outlook。然后我们发送一个邮件到我们的帐户,标题使用"subscribe"。在Outlook下载新邮件后,会引发NewMailEx事件来通知应用程序。这个请求会被加到如图6左侧的队列状态列表框中,。在大约五秒之后,这个请求会被处理,并将订阅用户加到Form1左侧的订阅用户列表框中,如图6所示: 图6 处理一个订阅用户,并将其加入到邮件列表中 为了退订邮件列表,我们可以简单发送一个标题为"unsubscribe"的邮件到相同的邮件帐号。当邮件列表管理员想发送一封邮件到所有订阅的用户时,只需要简单地点击Send Message按钮即可,如图7所示:
图7 向所有订阅的用户发送邮件