我们公司最近通过Office 365订阅将内部邮件服务器(CommunigatePro)迁移到托管邮件。从那以后,我遇到了从我的VB.NET应用程序向组织外部的人发送电子邮件的问题。我找到了一个解决方法,但是我想知道是否在配置中丢失了可能导致问题的东西。通过O365发送电子邮件
我发送消息的代码是非常标准的(显然混淆了一下用于发布),但我必须包括一个独立的功能通过CDO发送消息,如果它失败:
Private Function SendFTPSuccessMail() As Boolean
Dim Email As New System.Net.Mail.MailMessage
Dim MailServer As New System.Net.Mail.SmtpClient("domain-com.mail.protection.outlook.com")
Dim SenderAddress As New System.Net.Mail.MailAddress("[email protected]", "IT HelpDesk")
Dim BodyText As String = String.Empty
With Email
.From = SenderAddress
.Bcc.Add(SenderAddress)
BodyText = "SOME TEXT FOR THE E-MAIL MESSAGE"
.To.Add(New System.Net.Mail.MailAddress("[email protected]", "Recipient"))
.Subject = "MESSAGE SUBJECT"
.Body = BodyText
End With
Try
MailServer.Send(Email)
Return True
Catch ex As Exception
Dim CDOError As String = SendCDOEmail(Email, ex)
If Not String.IsNullOrEmpty(CDOError) Then
Dim ExceptionMessage As String = ".NET SEND ERROR: " & ex.Message & vbCrLf
If Not ex.InnerException Is Nothing Then
ExceptionMessage += ex.InnerException.Message & vbCrLf
End If
ExceptionMessage += vbCrLf & CDOError
MessageBox.Show(ExceptionMessage, "NOTIFICATION E-MAIL FAILED", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return False
Else
Return True
End If
Finally
If Not Email Is Nothing Then
Email.Dispose()
End If
MailServer = Nothing
End Try
End Function
的MailServer.Send
如果所有收件人都在同一个(我的)域中,但方法会正常工作,但会为具有不同域的任何收件人地址发出SmtpFailedRecipientException
或SmtpFailedRecipientsException
。该SendCDOEmail
功能如下:
Option Strict Off
Public Function SendCDOEmail(ByRef OriginalMessage As System.Net.Mail.MailMessage, ByVal OriginalException As Exception) As String
Dim ErrorMessage As String = String.Empty
If TypeOf (OriginalException) Is System.Net.Mail.SmtpFailedRecipientsException Then
Dim SMTPEX As System.Net.Mail.SmtpFailedRecipientsException = CType(OriginalException, System.Net.Mail.SmtpFailedRecipientsException)
Dim FailedAddresses As New List(Of String)
Dim NewTo As New List(Of System.Net.Mail.MailAddress)
Dim NewCC As New List(Of System.Net.Mail.MailAddress)
Dim NewBCC As New List(Of System.Net.Mail.MailAddress)
For Each InnerEx As System.Net.Mail.SmtpFailedRecipientException In SMTPEX.InnerExceptions
FailedAddresses.Add(InnerEx.FailedRecipient.ToString.Replace("<", "").Replace(">", ""))
Next
For Each Recipient As System.Net.Mail.MailAddress In OriginalMessage.To
For Each KeepAddress As String In FailedAddresses
If Recipient.Address = KeepAddress Then
NewTo.Add(Recipient)
Exit For
End If
Next
Next
OriginalMessage.To.Clear()
For Each Recipient As System.Net.Mail.MailAddress In NewTo
OriginalMessage.To.Add(Recipient)
Next
For Each Recipient As System.Net.Mail.MailAddress In OriginalMessage.CC
For Each KeepAddress As String In FailedAddresses
If Recipient.Address = KeepAddress Then
NewCC.Add(Recipient)
Exit For
End If
Next
Next
OriginalMessage.CC.Clear()
For Each Recipient As System.Net.Mail.MailAddress In NewCC
OriginalMessage.CC.Add(Recipient)
Next
For Each Recipient As System.Net.Mail.MailAddress In OriginalMessage.Bcc
For Each KeepAddress As String In FailedAddresses
If Recipient.Address = KeepAddress Then
NewBCC.Add(Recipient)
Exit For
End If
Next
Next
OriginalMessage.Bcc.Clear()
For Each Recipient As System.Net.Mail.MailAddress In NewBCC
OriginalMessage.Bcc.Add(Recipient)
Next
ElseIf TypeOf (OriginalException) Is System.Net.Mail.SmtpFailedRecipientException Then
Dim SMTPEX As SmtpFailedRecipientException = CType(OriginalException, SmtpFailedRecipientException)
Dim NewTo As New List(Of System.Net.Mail.MailAddress)
Dim NewCC As New List(Of System.Net.Mail.MailAddress)
Dim NewBCC As New List(Of System.Net.Mail.MailAddress)
Dim FailedAddress As String = SMTPEX.FailedRecipient.ToString.Replace("<", "").Replace(">", "")
For Each Recipient As System.Net.Mail.MailAddress In OriginalMessage.To
If Recipient.Address = FailedAddress Then
NewTo.Add(Recipient)
End If
Next
OriginalMessage.To.Clear()
For Each Recipient As System.Net.Mail.MailAddress In NewTo
OriginalMessage.To.Add(Recipient)
Next
For Each Recipient As System.Net.Mail.MailAddress In OriginalMessage.CC
If Recipient.Address = FailedAddress Then
NewCC.Add(Recipient)
End If
Next
OriginalMessage.CC.Clear()
For Each Recipient As System.Net.Mail.MailAddress In NewCC
OriginalMessage.CC.Add(Recipient)
Next
For Each Recipient As System.Net.Mail.MailAddress In OriginalMessage.Bcc
If Recipient.Address = FailedAddress Then
NewBCC.Add(Recipient)
End If
Next
OriginalMessage.Bcc.Clear()
For Each Recipient As System.Net.Mail.MailAddress In NewBCC
OriginalMessage.Bcc.Add(Recipient)
Next
End If
Dim CDOMessage As Object = CreateObject("CDO.Message")
With CDOMessage.Configuration.Fields
.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.office365.com"
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = 1
.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "[email protected]"
.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "mypassword"
.Update()
End With
With OriginalMessage
CDOMessage.Subject = .Subject
CDOMessage.From = .From.DisplayName & " <" & .From.Address & ">"
For Each ToAddress As System.Net.Mail.MailAddress In .To
CDOMessage.To = CDOMessage.To & ToAddress.DisplayName & " <" & ToAddress.Address & ">; "
Next
For Each ToAddress As System.Net.Mail.MailAddress In .CC
CDOMessage.CC = CDOMessage.CC & ToAddress.DisplayName & " <" & ToAddress.Address & ">; "
Next
For Each ToAddress As System.Net.Mail.MailAddress In .Bcc
CDOMessage.BCC = CDOMessage.BCC & ToAddress.DisplayName & " <" & ToAddress.Address & ">; "
Next
Dim FileNames = .Attachments.[Select](Function(a) a.ContentStream).OfType(Of System.IO.FileStream)().[Select](Function(fs) fs.Name)
For Each File As String In FileNames
CDOMessage.AddAttachment(File)
Next
CDOMessage.TextBody = .Body
Try
CDOMessage.Send()
Catch ex As Exception
ErrorMessage = "CDO SEND ERROR: " & ex.Message
Finally
CDOMessage = Nothing
End Try
End With
Return ErrorMessage
End Function
这似乎每次都工作得很多,但我不禁想,如果有一种方法,我可以完全避免使用CDO发送方法。我已经用domain-com.mail.protection.outlook.com
和smtp.office365.com
服务器地址为SmtpClient
对象尝试了它,并且创建了一个新的Credentials
对象并将相同的用户名/密码设置为我用于CDO发送方法的对象。任何关于如何让System.Net.Mail
对象工作而不必诉诸老派CDO的想法?
编辑:为了澄清和完整性,我对该功能进行了另一项测试,在该测试中,我试图向该域以外的Google托管电子邮件帐户发送邮件。这一次,我再手动设置Credentials
属性在我的主要的邮件功能的try/catch块的SmtpClient
对象如下:
Try
MailServer.Credentials = New System.Net.NetworkCredential("[email protected]", "mypassword")
MailServer.Send(Email)
Return True
Catch ex As Exception
Dim CDOError As String = SendCDOEmail(Email, ex)
[...]
Finally
If Not Email Is Nothing Then
Email.Dispose()
End If
MailServer = Nothing
End Try
为了减少变量的数目尽可能地,我复制并粘贴用户名/密码信息直接来自CDO发送方法。这些凭证用于我的电子邮件帐户,这是我们公司的O365管理员帐户。不幸的是,测试仍然失败,完全相同SmtpFailedRecipientException
。给出的服务器响应为:
“邮箱不可用的服务器响应为:64年5月7日TenantAttribution;中继接入拒绝[SN1NAM01FT060.eop-nam01.prod.protection.outlook.com]”
个人,我想通过设置某种“通用”登录,或者实现一些允许从我的网络发送“匿名”的方法来摆脱为邮件服务器使用我的凭据,但这超出了范围这个问题。
无论如何,我只是不知道我错过了什么。也许这是我在Exchange配置中的设置,或者是我忽略的其他“怪癖”。感谢您的帮助。
另一件需要注意的事情:我做了一些额外的测试,其中有许多不同的配置。IF我改变了SmtpClient
主机smtp.office365.com
和设置端口587
(或25
),和的EnableSsl
属性设置为True
和明确提供的用户凭证(这是我试图让主要的事情远离),我能够得到一条消息,而不会触及Catch
区块并使用SendCDOEmail
方法(如上所述,它使用明确定义的凭证)。
Dim MailServer As New System.Net.Mail.SmtpClient("smtp.office365.com")
[...]
With MailServer
.Credentials = New System.Net.NetworkCredential("[email protected]", "mypassword")
.Port = 587 'or 25
.EnableSsl = True
.Send(Email)
End With
虽然这种方法在技术上的作品,我真的想从我的应用程序明确指定凭据脱身,尤其是因为我没有在Exchange服务器上的“通用”用户帐户,我可以使用。这是为SmtpClient
主机名使用domain-com.mail.protection.outlook.com
的目的。问题是我仍然必须提供CDO发送方法的凭据(主机名为smtp.office365.com
),所以没有什么可以解决的。
感谢您的反馈,但您可能错过了原始文章中的部分内容,我表示我也尝试过这一部分,但没有成功。当我明天上午进入办公室时,我会更新我的问题,更详细地介绍我使用'Credentials'属性获得的例外情况。 –