非同期処理を行う場合、旧バージョン(Mail for .NET 1.0J/Secure Mail 2.0J)では複雑なコード処理が必要でした。たとえば、Imapサーバーへ非同期でログインする場合、同期処理の場合とは異なる専用のメソッド(BeginLoginメソッド)を実行し、さらに非同期ログイン処理の終了時に発生する専用のイベント(EndLoginイベント)に処理を実装する必要があり、その結果メンテナンスしにくいイベント駆動型のコードになっていました。
本バージョンでは、それらのメソッドやイベントに代わり、新たにStartメソッドやMarshalメソッドが用意され、シンプルな実装で非同期処理が可能となりました。各メソッドの使用方法について、以下に説明します。
本製品のメソッドは、同期処理と非同期処理(ブロッキング用途と非ブロッキング用途)の両方をサポートしています。ただし、メソッドを単純に実行した場合、それは同期処理で実行されます。
本バージョンで導入されたStartメソッドは、渡された関数を別のワーカースレッドで実行する機能です。つまり、本製品のメソッドが含まれる関数をStartメソッドに渡すだけで、その関数は呼び出し元のメインスレッドのUIをブロックすることなく、非同期処理で実行されます。このメソッドの構文は単純です。
Smtp1.Start(AddressOf myBlockingFunction, myObject)
' ブロッキング処理のメソッドが含まれる関数です。
Private Sub myBlockingFunction(ByVal myObject As Object)
...
End Sub
smtp1.Start(myBlockingFunction, myObject);
// ブロッキング処理のメソッドが含まれる関数です。
private void myBlockingFunction(object myObject)
{
...
}
このアプローチには、以下のような利点があります。
-
同期処理と非同期処理とで、一つの関数を共用できます。関数を別個に作成する必要はありません。Startメソッドを使用すれば非同期で、Startメソッドを使用しなければ同期処理で、呼び出した関数が実行されます。
-
関数内のメソッドは直列に呼び出されるので、コードのメンテナンスが容易です。多数のイベントハンドラを実装し、あるハンドラから別のハンドラにジャンプするようなコードを記述する必要はありません。
Startメソッドを使用して呼び出された関数は、別のワーカースレッドで実行されます。そこから呼び出し元のメインスレッド(UIスレッド)へ結果やデータを戻す(マーシャリングする)場合には、その関数内でMarshalメソッドを実行します。
Marshalメソッドを実行すると、メソッドの引数の種類により、メインスレッドでUserStateイベントまたはErrorイベントが発生します。それらのイベントハンドラ内で、Marshalメソッドの引数として渡されたデータを受け取ることができます。
' 別のワーカースレッドでMarshalメソッドを実行すると、
' 呼び出し元のメインスレッドでUserStateイベントが発生します。
Smtp1.Marshal("Message from the worker thread!", Nothing)
Private Sub Smtp1_UserState(ByVal sender As Object, ByVal e As UserStateEventArgs) Handles smtp1.UserState
' Marshalメソッドの引数として渡されたメッセージとオブジェクトを、
' メインスレッド側ではUserStateイベントの引数として受け取ることができます。
MessageBox.Show(e.Message)
End Sub
// 別のワーカースレッドでMarshalメソッドを実行すると、
// 呼び出し元のメインスレッドでUserStateイベントが発生します。
smtp1.Marshal("Message from the worker thread!", null);
private void smtp1_UserState(object sender, UserStateEventArgs e)
{
// Marshalメソッドの引数として渡されたメッセージとオブジェクトを、
// メインスレッド側ではUserStateイベントの引数として受け取ることができます。
MessageBox.Show(e.Message);
}
 |
Startメソッドを使用せずに、同期処理として呼び出した関数内でMarshalメソッドが実行されても、動作上特に問題はありません。 |
StartメソッドとMarshalメソッドを使用して、非同期処理を実装する場合のコード例を、以下に示します。
Startメソッドを介して実行したsendMail関数は、別のワーカースレッドで非同期で実行されます。関数内で何らかの例外が発生した場合には、Marshalメソッドを実行し、呼び出し元のメインスレッドでErrorイベントを発生させます。Errorイベントのイベントハンドラ内で、Marshalメソッドにより渡されたExceptionの情報を、MessageBoxに表示しています。
Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
' 指定した受信者にメッセージを送信します。
' Startメソッドを使用して、操作を(ワーカースレッドで)非同期に実行します。
' イベントハンドラはデザイナで追加されました。
smtp1.Start(AddressOf sendMail, New System.Net.Mail.MailAddress("myRecipient@gmail.com"))
End Sub
Private Sub sendMail(ByVal state As Object)
' この関数はワーカースレッドで実行され、マルチスレッドの非ブロッキング操作を行います。
Try
' 送信するメソッドを作成します。
Dim message As New MailMessage()
Dim recipient As System.Net.Mail.MailAddress = TryCast(state, System.Net.Mail.MailAddress)
message.To.Add(recipient)
message.From = "sender@dart.com"
message.Subject = "Here is your file"
message.BodyText = "Please see the attached file."
message.Parts.Add(New Attachment("myAttachment.doc"))
' セッションパラメーターを設定します。
smtp1.Session.RemoteEndPoint = New Dart.Mail.IPEndPoint("myMailServer", Smtp.DefaultPort)
smtp1.Session.Username = "myUsername"
smtp1.Session.Password = "myPassword"
smtp1.Session.Authentication = Authentication.Login
' メッセージを送信してログアウトします。
smtp1.Send(message)
smtp1.Close()
Catch ex As Exception
' Errorイベントを発生させ、例外をUIスレッドにマーシャリングします。
smtp1.Marshal(ex)
End Try
End Sub
Private Sub smtp1_Connection_Log(ByVal sender As Object, ByVal e As DataEventArgs)
' 通信ログをテキストボックスに表示します。
txtLog.AppendText(e.Data.ToString())
End Sub
Private Sub smtp1_Error(ByVal sender As Object, ByVal e As ErrorEventArgs)
' エラーがあった場合、メッセージを表示します。
MessageBox.Show(e.GetException().Message, "There was an error while sending the message")
End Sub
private void button1_Click(object sender, EventArgs e)
{
// 指定した受信者にメッセージを送信します。
// Startメソッドを使用して、操作を(ワーカースレッドで)非同期に実行します。
// イベントハンドラはデザイナで追加されました。
smtp1.Start(sendMail, new System.Net.Mail.MailAddress("myRecipient@gmail.com"));
}
private void sendMail(object state)
{
// この関数はワーカースレッドで実行され、マルチスレッドの非ブロッキング操作を行います。
try
{
// 送信するメッセージを作成します。
MailMessage message = new MailMessage();
System.Net.Mail.MailAddress recipient = state as System.Net.Mail.MailAddress;
message.To.Add(recipient);
message.From = "sender@dart.com";
message.Subject = "Here is your file";
message.BodyText = "Please see the attached file.";
message.Parts.Add(new Attachment("myAttachment.doc"));
// セッションパラメーターを設定します。
smtp1.Session.RemoteEndPoint = new Dart.Mail.IPEndPoint("myMailServer", Smtp.DefaultPort);
smtp1.Session.Username = "myUsername";
smtp1.Session.Password = "myPassword";
smtp1.Session.Authentication = Authentication.Login;
// メッセージを送信してログアウトします。
smtp1.Send(message);
smtp1.Close();
}
catch (Exception ex)
{
// Errorイベントを発生させ、例外をUIスレッドにマーシャリングします。
smtp1.Marshal(ex);
}
}
private void smtp1_Connection_Log(object sender, DataEventArgs e)
{
// 通信ログをテキストボックスに表示します。
txtLog.AppendText(e.Data.ToString());
}
private void smtp1_Error(object sender, ErrorEventArgs e)
{
// エラーがあった場合、メッセージを表示します。
MessageBox.Show(e.GetException().Message, "There was an error while sending the message");
}
 |
上記サンプルコードのsendMail関数は、Startメソッドを使用せずに呼び出すこともできます。この場合は同期処理で実行されます。 |