Use events to control MailBee.NET execution and monitor SMTP activities


Introduction

Smtp object provides lots of events you can subscribe to. This gives you a great control over the entire process how the message is sent.

You can track every byte being transmitted over the network and every step of the connection, alter the e-mail being sent just before it gets submitted to the server (useful for mail merge), and so on.

Some events of Smtp object mainly make sense for bulk sending mode when multiple e-mails get sent within a single method call. They are MergingMessageSendingMessageMessageSentMessageNotSent,SubmittingMessageToPickupFolderMessageSubmittedToPickupFolder, and FinishingJob .

It should be also stated that MailBee.NET provides a protected On<EventName> method for every event. This allows you to derive your own class from Smtp class and implement your own handlers for events. This can be useful in some special cases such as handling events when the message loop thread is blocked (can happen in desktop applications). See Smtp.OnConnected method documentation for details.


Monitor progress of SMTP session with events

This console sample subscribes to a few events to control the key points of the SMTP session: establishing the connection, submitting the sender and every recipient, the upload progress of the message data:

using System;
using MailBee;
using MailBee.Mime;
using MailBee.SmtpMail;

class Sample
{
    private static void OnConnected(object sender,
        ConnectedEventArgs e)
    {
        Console.WriteLine("The server accepted the connection.");
    }

    private static void OnSenderOK(object sender,
        SmtpMessageSenderSubmittedEventArgs e)
    {
        Console.WriteLine(e.SenderEmail + " accepted as sender.");
    }

    private static void OnRecipientOK(object sender,
        SmtpMessageRecipientSubmittedEventArgs e)
    {
        Console.WriteLine(e.RecipientEmail + " accepted as recipient");
    }

    private static void OnDataChunkSent(object sender,
        SmtpMessageDataChunkSentEventArgs e)
    {
        Console.WriteLine(string.Format("{0} of {1} bytes sent",
            e.TotalBytesSent.ToString(), e.DataTotalLength.ToString()));
    }

    static void Main(string[] args)
    {
        Smtp mailer = new Smtp();

        // Use SMTP relay server with authentication.
        mailer.SmtpServers.Add("mail.here.com", "john.doe", "secret");

        // Set From, To and CC as strings (with friendly names).
        mailer.Message.From.AsString = "John Doe <john.doe@here.com>";
        mailer.Message.To.AsString = "Jane Doe <jane.doe@there.com>";
        mailer.Message.Cc.AsString = "John Smith <j.smith@elsewhere.com>";

        mailer.Message.Subject = "Test message";
        mailer.Message.BodyPlainText = "This is a test message";

        // Subscribe to events.
        mailer.Connected += new ConnectedEventHandler(OnConnected);
        mailer.MessageSenderSubmitted +=
            new SmtpMessageSenderSubmittedEventHandler(OnSenderOK);
        mailer.MessageRecipientSubmitted +=
            new SmtpMessageRecipientSubmittedEventHandler(OnRecipientOK);
        mailer.MessageDataChunkSent +=
            new SmtpMessageDataChunkSentEventHandler(OnDataChunkSent);

        mailer.Send();
    }
}
Imports System
Imports MailBee
Imports MailBee.Mime
Imports MailBee.SmtpMail

Module Module1
    Private Sub OnConnected(ByVal sender As Object, _
            ByVal e As ConnectedEventArgs)

        Console.WriteLine("The server accepted the connection.")
    End Sub

    Private Sub OnSenderOK(ByVal sender As Object, _
            ByVal e As SmtpMessageSenderSubmittedEventArgs)

        Console.WriteLine(e.SenderEmail + " accepted as sender.")
    End Sub

    Private Sub OnRecipientOK(ByVal sender As Object, _
            ByVal e As SmtpMessageRecipientSubmittedEventArgs)

        Console.WriteLine(e.RecipientEmail + " accepted as recipient")
    End Sub

    Private Sub OnDataChunkSent(ByVal sender As Object, _
            ByVal e As SmtpMessageDataChunkSentEventArgs)

        Console.WriteLine(String.Format("{0} of {1} bytes sent", _
          e.TotalBytesSent.ToString(), _
          e.DataTotalLength.ToString()))
    End Sub

    Sub Main()
        Dim mailer As Smtp = New Smtp()

        ' Use SMTP relay server with authentication.
        mailer.SmtpServers.Add("mail.here.com", "john.doe", "secret")

        ' Set From, To and CC as strings (with friendly names).
        mailer.Message.From.AsString = "John Doe <john.doe@here.com>"
        mailer.Message.To.AsString = "Jane Doe <jane.doe@there.com>"
        mailer.Message.Cc.AsString = "John Smith <j.smith@elsewhere.com>"

        mailer.Message.Subject = "Test message"
        mailer.Message.BodyPlainText = "This is a test message"

        ' Subscribe to events.
        AddHandler mailer.Connected, AddressOf OnConnected
        AddHandler mailer.MessageSenderSubmitted, AddressOf OnSenderOK
        AddHandler mailer.MessageRecipientSubmitted, AddressOf OnRecipientOK
        AddHandler mailer.MessageDataChunkSent, AddressOf OnDataChunkSent

        mailer.Send()
    End Sub
End Module

The samples throughout this guide assume that MailBee.NET SMTP license key is already set. See Import namespaces and set license key topic for details.


Alter e-mail message during sending

MergingMessageSendingMessage and SubmittingMessageToPickupFolder events of Smtp object allow you to pre-process the e-mail before it gets sent (in case of MergingMessage, before the e-mail is created from the template).

Consider the following mail merge scenario. You've set the template and the data source and started the mail merge process. For every row of the data source, mail merge procedure generates an e-mail and sends it. All is great until you find out that you need to "touch" every message a bit.

When sending a single e-mail, you can fine-tune it as you need before calling Smtp.Send method. But during mail merge all e-mails get generated automatically. In this case, you can use SendingMessage event. This will work with a single e-mail as well.

The console sample below performs mail merge and uses SendingMessage event to sign every outgoing message with a DKIM signature:

using System;
using System.Data;
using System.IO;
using MailBee;
using MailBee.Mime;
using MailBee.SmtpMail;
using MailBee.Security;

class Sample
{
    private static void OnSendingMessage(object sender,
        SmtpSendingMessageEventArgs e)
    {
        // Sign each outgoing e-mail with DKIM signature.
        e.MailMessage.DomainKeysSign(
            false, null, privateKey, false, "dk", DomainKeysTypes.DKIM);
    }

    static string privateKey = null;

    static void Main(string[] args)
    {
        // Describe the data source for the mail merge.
        // In real apps, you'll connect to the database.
        DataTable workTable = new DataTable("customers");

        DataColumn workCol = workTable.Columns.Add("id", typeof(int));
        workCol.AllowDBNull = false;
        workCol.Unique = true;

        workTable.Columns.Add("name", typeof(string));
        workTable.Columns.Add("email", typeof(string));

        // Add two rows to the data source. This way, the mail merge
        // procedure will generate two e-mails.
        DataRow row = workTable.NewRow();
        row["id"] = 1;
        row["name"] = "John Doe";
        row["email"] = "john.doe@company.com";
        workTable.Rows.Add(row);

        row = workTable.NewRow();
        row["id"] = 2;
        row["name"] = "Bob Brown";
        row["email"] = "bob.brown@example.com";
        workTable.Rows.Add(row);

        // To avoid re-reading private key for each message,
        // we read it once and then use memory copy.
        privateKey = File.ReadAllText(@"C:\Temp\rsa512.private");

        Smtp mailer = new Smtp();

        // Use SMTP relay server with authentication.
        mailer.SmtpServers.Add("mail.here.com", "jane.doe", "secret");

        // Set static From and Subject.
        mailer.Message.From.AsString = "Jane Doe <jane.doe@here.com>";
        mailer.Message.Subject = "Greetings";

        // Set templates for To and body.
        mailer.Message.To.AsString = "##name## <##email##>";
        mailer.Message.BodyPlainText = "Message for ##name##";

        // Subscribe to SendingMessage event.
        mailer.SendingMessage +=
            new SmtpSendingMessageEventHandler(OnSendingMessage);

        // Generate and send e-mails.
        mailer.SendMailMerge(null, null, workTable);
    }
}
Imports System
Imports System.Data
Imports System.IO
Imports MailBee
Imports MailBee.Mime
Imports MailBee.SmtpMail
Imports MailBee.Security

Module Module1
    Private Sub OnSendingMessage(ByVal sender As Object, _
            ByVal e As SmtpSendingMessageEventArgs)

        ' Sign each outgoing e-mail with DKIM signature.
        e.MailMessage.DomainKeysSign(False, Nothing, privateKey, _
        False, "dk", DomainKeysTypes.DKIM)
    End Sub

    Private privateKey As String = Nothing

    Sub Main()
        ' Describe the data source for the mail merge.
        ' In real apps, you'll connect to the database.
        Dim workTable As New DataTable("customers")

        Dim workCol As DataColumn = _
        workTable.Columns.Add("id", GetType(Integer))
        workCol.AllowDBNull = False
        workCol.Unique = True

        workTable.Columns.Add("name", GetType(String))
        workTable.Columns.Add("email", GetType(String))

        ' Add two rows to the data source. This way, the mail merge
        ' procedure will generate two e-mails.
        Dim row As DataRow = workTable.NewRow()
        row("id") = 1
        row("name") = "John Doe"
        row("email") = "john.doe@company.com"
        workTable.Rows.Add(row)

        row = workTable.NewRow()
        row("id") = 2
        row("name") = "Bob Brown"
        row("email") = "bob.brown@example.com"
        workTable.Rows.Add(row)

        ' To avoid re-reading private key for each message,
        ' we read it once and then use memory copy.
        privateKey = File.ReadAllText("C:\Temp\rsa512.private")

        Dim mailer As Smtp = New Smtp()

        ' Use SMTP relay server with authentication.
        mailer.SmtpServers.Add("mail.here.com", "jane.doe", "secret")

        ' Set static From and Subject.
        mailer.Message.From.AsString = "Jane Doe <jane.doe@here.com>"
        mailer.Message.Subject = "Greetings"

        ' Set templates for To and body.
        mailer.Message.To.AsString = "##name## <##email##>"
        mailer.Message.BodyPlainText = "Message for ##name##"

        ' Subscribe to SendingMessage event.
        AddHandler mailer.SendingMessage, AddressOf OnSendingMessage

        ' Generate and send e-mails.
        mailer.SendMailMerge(Nothing, Nothing, workTable)
    End Sub
End Module

Cancel sending process

In any event handler (or in any thread), you can call mailer.Abort to immediately close the connection (mailer denotes Smtp instance which raised this event).

"Any event handler" is not limited to "any MailBee.NET event handler". For instance, you can call mailer.Abort in Form_Closing event handler of your main form (this will abort MailBee.NET immediately when the user closes the application). Just make sure you declared mailer on the global level so that Form_Closing could access it.

Also, MergingMessageSendingMessage and SubmittingMessageToPickupFolder events of Smtp object allow you to skip the particular message but continue to subsequent messages.

With MessageRecipientSubmitted event, you can cancel the sending process in case if the e-mail address of a certain important recipient gets rejected by the SMTP server. See Use events to track successful and failed recipients during sending e-mail topic for details.

And there is Smtp.StopJobs method (can be used with mail merge or whenever you send bulk mail). It stops bulk mail process more gracefully than Smtp.Abort because it first completes sending of the e-mail currently being sent and simply does not continue processing other pending e-mails.

The code snippet below contains the handler of SendingMessage event, which skips messages if they are greater than 100KB in size. It also aborts the entire process if certain external variable aborted becomes true:

using System;
using MailBee;
using MailBee.SmtpMail;

class Sample
{
    static bool aborted = false;
    static Smtp mailer = new Smtp();

    static void OnSendingMessage(object sender,
        SmtpSendingMessageEventArgs e)
    {
        if (aborted)
        {
            mailer.Abort();
        }
        else if (e.MailMessage.Size > 1024 * 100)
        {
            e.SendIt = false;
        }
    }

    static void Main(string[] args)
    {
        // Attach event handler.
        mailer.SendingMessage +=
            new SmtpSendingMessageEventHandler(OnSendingMessage);

        // The rest of your code...
    }
}
Imports System
Imports MailBee
Imports MailBee.SmtpMail

Module Module1
    Private aborted As Boolean = False
    Private mailer As New Smtp()

    Sub OnSendingMessage(ByVal sender As Object, _
            ByVal e As SmtpSendingMessageEventArgs)
        If aborted Then
            mailer.Abort()
        Else
            If e.MailMessage.Size > 1024 * 100 Then
                e.SendIt = False
            End If
        End If
    End Sub

    Sub Main()
        ' Attach event handler.
        AddHandler mailer.SendingMessage, AddressOf OnSendingMessage

        ' The rest of your code...
    End Sub
End Module

We assume the main code uses mailer object to send bulk mail and sets aborted to true when it wants MailBee.NET not to send any more messages and close the connection.

It's understood that the connection won't be closed immediately when you set aborted to true. OnSendingMessage handler will be able to respond to this only when SendingMessage event gets raised. If you want to abort the connection immediately, call mailer.Abort.


Send feedback to AfterLogic

Copyright © 2006-2017 AfterLogic Corporation. All rights reserved.