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