0

I am trying to send emails via sendasync. The problem is it doesn't seem to go into the sendcompleted event handler. I added a break point there, but it never triggers. The program just waits at my semaphore. Any ideas? The program is a windows forms app.

if (send)
          {
                    print("Starting to send mail to " + Globalcls.projects[i].name);
                    mailSendSemaphore = new Semaphore(0, 1);
                    System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage();
                    msg.To.Add(Globalcls.projects[i].email);
                    msg.From = new MailAddress(Globalcls.settings.server_email);
                    msg.Subject = "Data";
                    msg.SubjectEncoding = System.Text.Encoding.UTF8;
                    msg.Body = "Please see attached data";
                    msg.BodyEncoding = System.Text.Encoding.UTF8;
                    msg.IsBodyHtml = false;
                    msg.Priority = MailPriority.High;

                    foreach (string fileName in files)
                    {
                       msg.Attachments.Add(new System.Net.Mail.Attachment(fileName));
                    }


                    SmtpClient client = new SmtpClient();
                    client.Credentials = new System.Net.NetworkCredential(Globalcls.settings.username, Globalcls.settings.password);
                    client.Port = Convert.ToInt32(Globalcls.settings.portS);//or use 587 

                    client.Host = "smtp.xdsl.co.za";

                    client.SendCompleted += new SendCompletedEventHandler(MailSendCallback);

                    client.SendAsync(msg,null);
                    mailSendSemaphore.WaitOne();
                   // if (Globalcls.error_message != "")
                      //  throw Exception
                    //client.Dispose();
                    print("email sent to " + Globalcls.projects[i].name);
                    client.Dispose();
                    mailSendSemaphore.Dispose();
                    msg.Dispose();

                }

                //Array.ForEach(Directory.GetFiles(GlobalClass.projects[i].foldero), delegate(string path) { File.Delete(path); });
                System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(Globalcls.projects[i].foldero);
                foreach (System.IO.FileInfo file in directory.GetFiles()) file.Delete();
                foreach (System.IO.DirectoryInfo subDirectory in directory.GetDirectories()) subDirectory.Delete(true);


            }

Here is my onsendcomplete evenhandler

        private static void MailSendCallback(object sender, AsyncCompletedEventArgs arg)
    {
        // oncomllete event for async send.
        if (arg.Error != null)
        Globalcls.error_message = arg.Error.ToString();

        // Release the main thread
        mailSendSemaphore.Release();
    }

EDIT

The reason I want to use sendasync is that send async apparently waits for the upload of the files to finish. The current problem is that sometimes the attachments are so big that upload takes longer than the timeout. I can make the timeout longer but I have no idea how long I should make it. Most of the emails reach 3mb easily. And our adsl line isn't always the most stable their is.

The reason for waiting for the send async is that you can't have more than one send per client. Thats why I wait for it to finish.

My problem is more related to the timeout. I want a timeout that only time's out when there is no communication between the smtp and smtp client.

EDIT 2

This is what my code originally looks like. I want to try and avoid that massive timeout. Having it multi threaded only makes the gui not hang.

if (send)
                {
                    print("Starting to send mail to " + Globalcls.projects[i].name);
                  //  mailSendSemaphore = new Semaphore(0, 1);
                    System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage();
                    msg.To.Add(Globalcls.projects[i].email);
                    msg.From = new MailAddress(Globalcls.settings.server_email);
                    msg.Subject = "Data";
                    msg.SubjectEncoding = System.Text.Encoding.UTF8;
                    msg.Body = "Please see attached data";
                    msg.BodyEncoding = System.Text.Encoding.UTF8;
                    msg.IsBodyHtml = false;
                    msg.Priority = MailPriority.High;

                    foreach (string fileName in files)
                    {
                       msg.Attachments.Add(new System.Net.Mail.Attachment(fileName));
                    }


                    SmtpClient client = new SmtpClient();
                    client.Credentials = new System.Net.NetworkCredential(Globalcls.settings.username, Globalcls.settings.password);
                    client.Port = Convert.ToInt32(Globalcls.settings.portS);//or use 587 

                   // client.Host = "127.0.0.1";
                    client.Host = "smtp.xdsl.co.za";
                    client.Timeout = 400000;
                    client.Send(msg);

                   /* client.SendCompleted += new SendCompletedEventHandler(MailSendCallback);

                    client.SendAsync(msg,null);
                    mailSendSemaphore.WaitOne();*/
                   // if (Globalcls.error_message != "")
                      //  throw Exception

                    print("email sent to " + Globalcls.projects[i].name);
                    client.Dispose();
                   //mailSendSemaphore.Dispose();
                    msg.Dispose();

                }

                //Array.ForEach(Directory.GetFiles(GlobalClass.projects[i].foldero), delegate(string path) { File.Delete(path); });
                System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(Globalcls.projects[i].foldero);
                foreach (System.IO.FileInfo file in directory.GetFiles()) file.Delete();
                foreach (System.IO.DirectoryInfo subDirectory in directory.GetDirectories()) subDirectory.Delete(true);


            }
            catch (Exception ex)
            {
                print("Error with " + Globalcls.projects[i].name);
                print(ex.ToString());
                timer1.Enabled = false;
                timer1.Stop();
                btn_start.Enabled = true;
                string contents = "There was an error in sending mail to " + Globalcls.projects[i].name;
                string heading = " Anxo error";
                string subject = "Anxo error";
                errormail(heading, subject, contents, ex.ToString(), Globalcls.projects[i].sos);
                result = false;
            }
blackwolfsa
  • 327
  • 3
  • 14
  • 2
    why send asynchronously when you're waiting for it to finish anyway? There is no benefit to this pattern at all. Just use `Send`. `SendAsync` is for when you want the calling thread to go off and do something else and *not* wait for the Send operation to finish – Andras Zoltan Sep 10 '12 at 14:46
  • 2
    ehh. you are not sending async since you block with the help of the sempahore. Why are you trying to accomplish? – jgauffin Sep 10 '12 at 14:47
  • Please see edit. The comment was too large to insert here. – blackwolfsa Sep 11 '12 at 05:36

1 Answers1

0

install IIS/SmtpService and let it handle the uploads. i.e. Use SmtpClient to send the emails to localhost:25 = local IIS.

imho you get a more solid solution by doing so. Since IIS will continue to try upload the messages if something fails.

Update

Something like this:

public class EmailSender
{
    ConcurrentQueue<MailMessage> _queue = new ConcurrentQueue<MailMessage>();
    private Thread _worker;
    ManualResetEvent _trigger = new ManualResetEvent(false);
    private bool _shutdown;

    public EmailSender()
    {
        _worker = new Thread(SendEmails);
        _worker.Start();
    }
    public void Enqueue(MailMessage message)
    {
        _queue.Enqueue(message);
        _trigger.Set();
    }

    public void Shutdown()
    {
        _shutdown = true;
        _trigger.Set();
        _worker.Join();
    }

    private void SendEmails(object state)
    {
        while (true)
        {
            _trigger.WaitOne(Timeout.Infinite);
            if (_shutdown)
                return; // you might want to send all emails instead.

            MailMessage msg;
            if (!_queue.TryDequeue(out msg))
            {
                _trigger.Reset();
                continue;
            }

            SendEmail(msg);
        }
    }

    private void SendEmail(MailMessage msg)
    {
        var client = new SmtpClient(); // configure
        try
        {
            client.Send(msg);
        }
        catch(SmtpException)
        {
            // try again. You might not want to retry forever = fix
            _queue.Enqueue(msg);
        }

    }
}
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • Some idiot in our company decided it was better to remove our local email hosting and rely entirely on remote hosting. So I don't really see this as a viable solution. – blackwolfsa Sep 11 '12 at 06:59
  • this is not hosting, it's just email relaying. If it's not viable you should create a sender queue instead of blocking like you do. Since you should failures (like network failure) Side note: I've also checked `SmtpClient.Send()` in MSDN. It should wait for completion too. – jgauffin Sep 11 '12 at 07:02
  • This is the exception I keep getting. "System.Net.Mail.SmtpException: The operation has timed out." I have even tried setting the timeout to 240000 up from 100000, 400000 works but I am exploring the smtp service route – blackwolfsa Sep 11 '12 at 07:29
  • Yeah. You need to have a huge timeout. Your mail provider should be able to handle multiple connections (i.e. create several `SmtpClient`, one per email to send) – jgauffin Sep 11 '12 at 07:33
  • I found that I can send the emails. But I cannot seem to be able to pull down the emails with outlook. Thats only with emails that contain no attachments. As soos as they contain attachments it fails. – blackwolfsa Sep 11 '12 at 12:50
  • that's another question. Create a new one here at SO for that. – jgauffin Sep 11 '12 at 13:02
  • I will, but I still feel that's its still in the same problem. To send an email without setting a ridiculously high timeout. If someone can tell me exactly whats wrong with my sendasync method and it works then I will use that. – blackwolfsa Sep 11 '12 at 13:16
  • Don't use async like that. It's really no different than `Send`. a) Create a new SmtpClient per mail to send. b) Use `Send()` – jgauffin Sep 11 '12 at 13:24
  • If you want to queue emails. Use a background thread or the sending – jgauffin Sep 11 '12 at 13:25
  • Its going to do the same than before. Every email is just going to timeout. The only difference is that the the program will try to upload more than one email at a time. Or am I understanding your code wrong? – blackwolfsa Sep 12 '12 at 09:17
  • Not at all. The code is a lot more stable than your async attempt. Using a high timeout and a queue is your only option if you do not want to use a local SMTP server for the relaying. – jgauffin Sep 12 '12 at 09:30
  • Sorry my bad, didnt type it clear enough. Its about the same as my solution before async option. The only difference is that I only use one thread and not multiple threads. I will update my post with my original code. – blackwolfsa Sep 12 '12 at 10:25
  • It's still not the same. The GUI will be responsive. I don't understand what you are looking for? there are ***no*** other solutions since you won't use a relaying SMTP server. – jgauffin Sep 12 '12 at 11:16
  • yes thats the one advantage the multi threaded one has. I have tried to and still trying to get the relaying smtp server route to work 100%. That route you gave me is according to me the best solution currently. – blackwolfsa Sep 12 '12 at 11:20
  • no network is 100% reliable. You have to retry as I did. Or use a relaying SMTP server which will take care of everything for you. My solution puts failed messages ***in the end of the queue*** to allow it to try other emails first. – jgauffin Sep 12 '12 at 11:23