1

I've been writing an email function in vb.net and had it working fine when my code was Smtp_Server.Send, the emails were sending successfully with no issues.

Then, I had to change it to SmtpServer_SendAsync and I was able to do this using information from this question about how to log any emails.

However, when trying to send email with the following code, it always fails, yet changing it back to just .Send has no issues. Am I missing something else in the email code?

The code for setting up and sending the email is this: (Obviously I've also got code for setting variables but it's not relevant here so I've left it out)

Smtp_Server.UseDefaultCredentials = False
Smtp_Server.Credentials = New Net.NetworkCredential(senderAdd, senderPass)
Smtp_Server.EnableSsl = False
Smtp_Server.Host = SMTPserver

Try
   AddHandler Smtp_Server.SendCompleted, AddressOf sendComplete
Catch ex As Exception
    silentErrorLog(ex)
End Try

e_mail = New MailMessage()
e_mail.From = New MailAddress(senderAdd)
e_mail.DeliveryNotificationOptions = DeliveryNotificationOptions.OnSuccess
e_mail.To.Add(eMail)
e_mail.Subject = subj
e_mail.IsBodyHtml = False
e_mail.Body = bod

Dim userState As Object = e_mail
Smtp_Server.SendAsync(e_mail, userState)

e_mail.Dispose()
e_mail = Nothing

Try
   If bInvoice = True Then
       triggerInvoice()
   End If

 Catch ex As Exception
      silentErrorLog(ex)
 End Try

 Catch ex As Exception
    errorLog(ex)

 End Try
End Sub

The error for this is

Failure sending email

Inner Exception: Cannot access a disposed object

Code for sendComplete

Public Sub sendComplete(ByVal sender As Object, e As AsyncCompletedEventArgs)

 Try
   If e.Error IsNot Nothing Then
     PictureBox1.Visible = False
     MsgBox("Failed to send email!", MessageBoxIcon.Warning + MsgBoxStyle.OkOnly, "Error")
     mailSent = False

It is always using this first part of the If, never going to the Else clause, so something must be wrong?

Community
  • 1
  • 1
Harambe
  • 423
  • 3
  • 29
  • I don't see any else clause, but I assume it's showing an error if it's always getting an error. What is that error? – Sami Kuhmonen May 24 '17 at 08:33
  • @SamiKuhmonen No I left the Else clause out. The error initially was just "Failed to send email", then I changed `Smtp_Server.EnableSsl = False` to True and it was Server does not support secure connections" or something – Harambe May 24 '17 at 08:51
  • Well, the "Failed to send email!" is from your code and you don't check the actual error at all in that code. I'm sure there is more clear error message than that there. Please copy the exact error to the question – Sami Kuhmonen May 24 '17 at 08:53
  • Do you have any code underneath the `Smtp_Server.SendAsync` line? It might be that you're doing something after that which is causing the error. If not, post the full error message, as @SamiKuhmonen says. – David May 24 '17 at 08:54
  • @SamiKuhmonen Done – Harambe May 24 '17 at 09:05

1 Answers1

1

You dispose the e-mail message right after calling SendAsync. At that point, however, the message has not been sent yet, so you dispose an object that SendAsync still needs:

Smtp_Server.SendAsync(e_mail, userState)

e_mail.Dispose()    ' bad timing

The solution is to attach to the SendCompleted event and dispose the message there. You can use a lambda expression to make sure that the e_mail variable is in scope in the event handler:

AddHandler Smtp_Server.SendCompleted, Sub(sender, e) e_mail.Dispose()

Smtp_Server.SendAsync(e_mail, userState)

Alternatively, since you pass the message as a userstate to your async method, you can dispose the message in your existing event handler:

Public Sub sendComplete(ByVal sender As Object, e As AsyncCompletedEventArgs)
    DirectCast(e.UserState, MailMessage).Dispose()
    ...
End Sub
Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • Where would I then put the call to `sendCompleted`? I just stepped through this and it went straight from `Smtp_Server.SendAsync(e_mail, userState)` to `End Sub`, without going to the `sendCompleted` subroutine – Harambe May 24 '17 at 09:17
  • 1
    @Harambe: You already have an AddHandler call to sendCompleted (wrapped in a useless try-catch-block - you should remove that), which is fine. The whole *point* of async is that your code continues uninterrupted and *in parallel*, the e-mail is sent and *later* sendCompleted is called. Set a brake point in sendComplete and let your code continue. – Heinzi May 24 '17 at 09:20