Link to home
Start Free TrialLog in
Avatar of zipnotic
zipnoticFlag for United States of America

asked on

ASYNC Email callback progress check

Helllo,

I'm using the following code to send an email asynchronously.  It's usually through gmail.  It works fine but occasionally there is a collision with an IMAP check of the email account or a follow up SMTP async send.  The attacments can get to 10-25 mbs which takes a minute or two to upload.  I'm trying to prevent those collisions by incorporating a global variable 'MailSendInProgress'  It gets set to TRUE just before the asyncsend and to FALSE in the callback from the async.  I also use it as a check during some IMAP operations elsewhere in the application as this is what seemed to interfere and cause an error in the async email sending process.  My question is whether there is a better way to accomplish this.  This process seems to work OK but it feels hokey and maybe will cause problems in the future?   Thanks for any thoughts you might have!

Check to prevent collisions from IMAP or other SMTP send operations:
If MailSendInprogress Then Exit Sub


 Public MailSendInprogress As Boolean = False

public sub SendMail()
 Try
      
            Dim SmtpServer As New SmtpClient()
            AddHandler SmtpServer.SendCompleted, AddressOf SendCompletedCallback
            Dim mail As New MailMessage()
            SmtpServer.Credentials = New Net.NetworkCredential(strMailLogin, strMailPassword)
            SmtpServer.Port = 587
            SmtpServer.EnableSsl = True
            SmtpServer.Host = strSMTPServer
            mail = New MailMessage()
            mail.From = New MailAddress(strMailLogin)
            If strAttachments = "YES" Then
                Dim attachment As System.Net.Mail.Attachment
                Using attachment
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)

                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                 

                End Using
            End If
            mail.To.Add(strTo)
            mail.Subject = strSubject
            mail.Body = strBody
            If IsHTML Then mail.IsBodyHtml = True
            Do Until MailSendInprogress = False
                Thread.Sleep(1000)
            Loop
            MailSendInprogress = True
            SmtpServer.SendAsync(mail, Nothing)  

            LogIt("****  MAIL SENT  *****")

        Catch ex As Exception

            LogIt("ERROR Sending email!" & ex.StackTrace)
        End Try


    Private Sub SendCompletedCallback(sender As Object, e As AsyncCompletedEventArgs)
        ' Throw New NotImplementedException
        Try


            If e.Error IsNot Nothing Then
                LogIt("ERROR SENDING ASYNC EMAIL: " & e.Error.ToString)
            Else
                LogIt("ASYNC EMAIL SEND COMPLETED.")

            End If
        Catch ex As Exception
            LogIt("ERROR " & ex.ToString)
        End Try
        MailSendInprogress = False
    End Sub

Open in new window

Avatar of Imran Javed Zia
Imran Javed Zia
Flag of Pakistan image

Hi,

If you are concerned about multiple hits to same process or method, then you can use lock statement. It is reffered as SyncLock in VB.net. Furthermore you may not need MailSendInprogress to track status of SendMail.

You may find following articles helpful:

https://msdn.microsoft.com/en-us/library/3a86s51t.aspx
http://www.dotnetperls.com/synclock

 Public MailSendInprogress As Boolean = False

public sub SendMail()
 Try
      SyncLock Me' Or MailSendInprogress Or any global or static/shared variable
            Dim SmtpServer As New SmtpClient()
            AddHandler SmtpServer.SendCompleted, AddressOf SendCompletedCallback
            Dim mail As New MailMessage()
            SmtpServer.Credentials = New Net.NetworkCredential(strMailLogin, strMailPassword)
            SmtpServer.Port = 587
            SmtpServer.EnableSsl = True
            SmtpServer.Host = strSMTPServer
            mail = New MailMessage()
            mail.From = New MailAddress(strMailLogin)
            If strAttachments = "YES" Then
                Dim attachment As System.Net.Mail.Attachment
                Using attachment
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)

                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                 

                End Using
            End If
            mail.To.Add(strTo)
            mail.Subject = strSubject
            mail.Body = strBody
            If IsHTML Then mail.IsBodyHtml = True
            Do Until MailSendInprogress = False
                Thread.Sleep(1000)
            Loop
            MailSendInprogress = True
            SmtpServer.SendAsync(mail, Nothing)  

            LogIt("****  MAIL SENT  *****")

        Catch ex As Exception

            LogIt("ERROR Sending email!" & ex.StackTrace)
        End Try
End SyncLock

Open in new window

Avatar of zipnotic

ASKER

Hi,

Thanks for your comment.  I spent some time checking out synclock and it doesn't seem like it's going to be appropriate for my code.  What I would like to do is the prototypical long email send process in the background while the GUI remains responsive.  I thought the asyncsend would be the answer but it isn't reliable.  I thought it was due to collisions of checking the same email account with IMAP in one code block and sending an email with SMTP using the same email account.  Now I'm not so sure.  On my development computer the below code worked flawlessly for several days.  When I installed it on a test computer it only works intermittently.  It will send a few emails then putter out after an hour or two at the most.  SOME emails will send after that but the majority result in an sendasync error captured in the callback.  Both computers have .Net 4, WIndows 7.  One is on a wireless network the other is cat6.  The test computer has several other network processes running that are reliable using a synchronous mail send process.  COuld that be the interference?  The error information returned on the call back of sendasync is limited:

System.Net.Mail.SmtpException: Failure sending mail. ---> System.Net.Sockets.SocketException: An invalid argument was supplied
   at System.Net.Mail.SmtpConnection.ConnectAndHandshakeAsyncResult.End(IAsyncResult result)
   at System.Net.Mail.SmtpClient.ConnectCallback(IAsyncResult result)
   --- End of inner exception stack trace ---INNER EXCEPTION: System.Net.Sockets.SocketException (0x80004005): An invalid argument was supplied
   at System.Net.Mail.SmtpConnection.ConnectAndHandshakeAsyncResult.End(IAsyncResult result)
   at System.Net.Mail.SmtpClient.ConnectCallback(IAsyncResult result)

Here's the code I'm currently using:


    Public MailSendInprogress As Boolean = False
    Dim mail As New MailMessage()
    Dim SmtpServer As New SmtpClient()


    Public Sub SendMail(ByVal strFrom As String, ByVal strTo As String, _
    ByVal strCC As String, ByVal strSubject As String, ByVal strBody As String, ByVal strAttachments As String, ByVal strSMTPServer As String, _
    ByVal strMailLogin As String, ByVal strMailPassword As String, IsHTML As Boolean)

        Try
            
            SmtpServer = New SmtpClient()
            AddHandler SmtpServer.SendCompleted, AddressOf SendCompletedCallback


            SmtpServer.Credentials = New Net.NetworkCredential(strMailLogin, strMailPassword)
            SmtpServer.Port = 587
            SmtpServer.EnableSsl = True
            SmtpServer.Host = strSMTPServer
            mail = New MailMessage()
            mail.From = New MailAddress(strMailLogin)
            If strAttachments = "YES" Then
                Dim attachment As System.Net.Mail.Attachment
                Using attachment
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)

                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                    attachment = New System.Net.Mail.Attachment(Application.StartupPath & "\")
                    mail.Attachments.Add(attachment)
                   

                End Using
            End If
            mail.To.Add(strTo)
            mail.Subject = strSubject
            mail.Body = strBody
            If IsHTML Then mail.IsBodyHtml = True
            MailSendInprogress = True
            SmtpServer.SendAsync(mail, Nothing)  

            LogIt("**** ATTEMPTING TO  SEND  MAIL  *****")

        Catch ex As Exception
            SmtpServer.Dispose()
            mail.Dispose()
            LogIt("ERROR Sending email!" & ex.StackTrace)
        End Try

    End Sub

    Private Sub SendCompletedCallback(sender As Object, e As AsyncCompletedEventArgs)
        ' Throw New NotImplementedException
        Try


            If e.Error IsNot Nothing Then
                LogIt("ERROR SENDING ASYNC EMAIL: " & e.Error.ToString & "INNER EXCEPTION: " & e.Error.InnerException.ToString())
            Else
                LogIt("ASYNC EMAIL SEND COMPLETED.")

            End If
        Catch ex As Exception
            LogIt("ERROR " & ex.ToString)
        End Try
        MailSendInprogress = False
        SmtpServer.Dispose()

        mail.Dispose()

    End Sub

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of zipnotic
zipnotic
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avoiding asynch was best solution