技术开发 频道

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



源代码

【IT168 专稿】

一、什么是邮件列表服务器

    如果你是一个开发人员,那么你一定对邮件列表并不陌生。一个邮件列表包含了一组用户,这些用户通过邮件订阅了指定内容的邮件内容。在通常情况下,处理是自动进行的,也就是我们通过发送一个email请求将自己的mail加到列表中,如果退订,可以再次发送这个email即可。邮件列表通过由列表服务器系统管理,这套系统我们可以通过购买方式获得。而在本文中为读者展示了如何建立自己的邮件列表服务器,在本例子中使用了.NET2.0新提供的System.Net.Mail命名空间,还有其他的一些新技术,如从Outlook2007中获得email等。对于一个完整的邮件列表服务器来说,需要如下的三个功能:

1. 向列表成员发送信息
2. 接收来自用户的订阅和退订请求
3. 自动增加和删除用户

   
对于发送email来说是非常容易的,我们可以使用System.NET.Mail命名空间很容易地做到。然而,这个命名空间仅仅允许我们发送邮件,但却不支持接收邮件。因此,在本文中将演示如何通过和Outlook2007集成来克服这个障碍。

二、接收邮件

    在上面已经提及,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",那么我们将从数据库中删除这个邮件地址。



三、发送邮件

   
接收邮件是一件非常复杂的工作,但发送邮件却是非常的容易。我们所需要的就是使用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
    如上Email参数为空,程序将会向所有订阅用户发送邮件。否则,将会向指定的用户发送邮件。发送信息的按钮中的代码如下:

'---向所有的订阅用户发送信息--- 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所示:



                           向所有订阅的用户发送邮件



 

 

0
相关文章