VB5/6用のiNetMail 1.0J、そして.NET対応初版のSecure Mail 2.0J、どちらもその当時に担当していたコンシューマーからのメールを受信してシステムに取り込むという要件がある開発プロジェクトで採用しました。やはり、肌感覚として文字化けなどの問題が生じない安心感は、自信をもってリリース日を迎えることができた根拠の一つになっていました。
最新版のSecure iNetSuite 4.0JはWindows 8やVisual Studio 2012に対応していますが、2013/10/17から提供が開始されたVisual Studio 2013や発売が開始されたWindows 8.1については現時点ではまだ対応予定の状態です(注1)。そこで、あえて最新のWindows 8.1とVisual Studio 2013でSecure iNetSuite 4.0Jを構成するコンポーネントの一つであるSecure Mail 4.0Jを使ってみて、実際のところどうなのかを確認してみたいと思います。また、今回はデスクトップアプリとしてサンプルを作成しましたが、MVCデザインパターンでの適用がしやすい非同期モード実装に変わったとのことなので、このあたりも意識してサンプルを作成しています。
2013/11/7にリリースされたService Pack 1で対応されました。
Windowsフォームアプリへの適用例
(※サンプルファイルの「CZ1310Win」に対応)
まずは一番オーソドックスなWindowsフォームアプリにSecure iNetSuite 4.0Jを適用してみましょう。とはいっても、Windowsフォーム画面にSecure Mail 4.0Jのsmtpコンポーネントやpopコンポーネントをドラッグ&ドロップで配置してプロパティを設定し、イベントプロシージャの中でメソッドを呼び出すのでは、少々オーソドックスすぎます。
そこで、popコンポーネントを使ってメール一覧を作成するModel部と、WindowsフォームのView部を別クラスになるような構成をとってみましょう。
プロジェクトの構成
Visual Studio 2013を起動して[ファイル]-[新しいプロジェクト]から[Visual Basic]-[Windows]-[Windowsフォームアプリケーション]を選択してWindowsフォームアプリの新規プロジェクトを作成します。
新規プロジェクトが作成できたら、以下の操作を行い、プロジェクトの初期状態を定義します。
- 「Views」「ViewModels」「Models」の3つのフォルダを作成
- Form1.vbファイルをMainForm.vbファイルにリネームしてからViewsフォルダに移動
- ViewModelsフォルダにMainViewModelクラスを作成
- ModelsフォルダにMailModelクラスを作成
なお、今回のサンプルは、MailModelクラスにpopコンポーネントを使ったメール受信のコードを作成し、MainForm.vbファイルには画面定義を記述します。そしてこの2つの橋渡し用のコードを、MainViewModelに記述するという構成になっています。
Secure Mailの参照追加
Secure Mailコンポーネントを利用するためには、参照設定が必要です。
ソリューションエクスプローラーでプロジェクト名を右クリックして[追加]-[参照]メニューから[参照マネージャー]ダイアログを表示します。次に、[アセンブリ]-[拡張]から「PowerTCP Mail for.NET」を選択して、参照の追加を行います。
ライセンスファイルの追加
グレープシティ社のコンポーネント製品に共通しているのは、開発環境でコンポーネントを使う場合、コンポーネント製品をインストールしてあることに加えて、プロジェクトにライセンスファイルを記述しておく必要がある点です。
ソリューションエクスプローラーでプロジェクト名を右クリックして[追加]-[新しい項目]メニューから[新しい項目の追加]ダイアログを表示します。続いて、[全般]から「テキストファイル」を選択して「licenses.licx」という名前でライセンスファイルをプロジェクトに追加してください。
ライセンスファイルには試用するコンポーネント名を記載しますので、今回は次のような内容になります。
Dart.Mail.Pop, Dart.Mail Dart.Mail.MailMessage, Dart.Mail
余談ですが、参照設定やライセンスファイルの追加は、popコンポーネントをツールボックスからWindowsフォームエディタ上にドラッグ&ドロップして配置すると自動的に行われるため、今回のような手間はありません。ドラッグ&ドロップ方式の方が圧倒的に手軽ではありますが、今回はWindowsフォーム上には見た目の定義以外は極力排除するためにこのような手順をとっています。
画面に表示する情報の定義
コンポーネントを使う準備ができたならば、画面に表示したい情報や画面での操作に関する処理をMainViewModelクラスに記述します。
MainViewModelクラスに記述するコードは実際にはもう少し必要ですが、重要な部分だけを抜き出すと次のようになります。
Imports System.Collections.ObjectModel Imports System.Runtime.CompilerServices Imports System.ComponentModel Public Class MainViewModel Implements INotifyPropertyChanged Private WithEvents Model As New MailModel Public Property Items As ObservableCollection(Of MailModel.TMail) Get Return Me.Model.Items End Get Set(value As ObservableCollection(Of MailModel.TMail)) Me.Model.Items = value End Set End Property Public Async Function GetMail() As Task Await Me.Model.GetMail() End Function Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) _ Implements INotifyPropertyChanged.PropertyChanged End Class
受信メール一覧はItemsプロパティに設定され、一覧を作るためのメソッドがGetMailメソッドとして定義されています。この時点では、まだMailModelクラスの中身は記述していないため、例えば「Me.Model.GetMail()」などと入力するとIDEの即時チェックでコンパイルエラーです。しかし、エラー修正のオプションで「MailModelにGetMailのメソッドスタブを生成」を選択すると、MailModelクラスにPublicメソッドの入り口だけ定義され、コンパイルエラーが解消できます。
画面の定義
今回のサンプルでは、非常にシンプルな画面構成を採用しています。
[一覧受信]をクリックしたら、受信メールの一覧をボタン下のListBoxに表示します。
画面のコードビハインド定義
画面定義が終わったら、次に画面定義に紐づいたコードを記述します。
ソリューションエクスプローラーで「MainForm.vb」を右クリックして[コード]メニューを選択すると、MainFormと関連づいたコード(コードビハインド)を記述するためのコードエディタが開きます。
Public Class MainForm Private WithEvents ViewModel As New MainViewModel Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles Me.Load Me.ListBox1.DrawMode = DrawMode.OwnerDrawVariable End Sub Private Async Sub Get_Button_Click(sender As Object, e As EventArgs) _ Handles Get_Button.Click Await Me.ViewModel.GetMail() Me.ListBox1.DataSource = Me.ViewModel.Items.ToList End Sub : (中略) : End Class
Get_Button_Clickイベントプロシージャでは、MainViewModelクラスのGetMailメソッドを非同期待ち合わせで呼び出しています。この非同期待ち合わせ(Await)は、Visual Studio 2012(.NET Framework 4.5)から採用されたもので、UIスレッドへ制御を戻しつつ、GetMailメソッドの実行が完了するまで待ち合わせる動きが可能になります。つまり、Windowsフォームアプリでありがちな、長時間処理実行中にWindowsフォームが真っ白になったり、「応答待ち」表示になったりすることを防止できます。
メール受信ロジックの記述
最後に、popコンポーネントを使ったメール受信一覧の取得ロジックを記述しましょう。
Imports System.ComponentModel Imports System.Collections.ObjectModel Imports System.Runtime.CompilerServices Imports System.Threading Public Class MailModel Implements INotifyPropertyChanged Public Class TMail Public Property From As String Public Property Subject As String Public Property SendDate As DateTime End Class Public Property Items As New ObservableCollection(Of TMail) Public Property Pop3Server As String = "popServer" Public Property UserId As String = "UserID" Public Property Password As String = "Password" Public Async Function GetMail() As Task Using pop As New Dart.Mail.Pop AddHandler pop.Progress, AddressOf PopProgress pop.Session.RemoteEndPoint = New Dart.Mail.IPEndPoint(Me.Pop3Server, 110) pop.Session.Username = Me.UserId pop.Session.Password = Me.Password pop.Connect() pop.Authenticate(True, True) ''ヘッダを取得します Await Task.Run(Sub() Try For Each mes In pop.Messages mes.Get(0) Next Catch ex As Exception Debug.Assert(ex.Message) End Try End Sub) pop.Close() RemoveHandler pop.Progress, AddressOf PopProgress End Using End Function Private Sub PopProgress(sender As Object, e As Dart.Mail.PopProgressEventArgs) If e.Final Then Me.Items.Add(New TMail With { .From = e.Message.Message.From, .Subject = e.Message.Message.Subject, .SendDate = e.Message.Message.Date }) End If End Sub Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) _ Implements INotifyPropertyChanged.PropertyChanged Private Sub OnPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub End Class
PopServerプロパティ、UserIdプロパティ、Passwordプロパティは使用しているメールサーバーの設定に合わせて値を設定しておいてください。
このクラスで最重要なのはGetMailメソッドの中の動きなので、メソッドの中を少し詳しく見ていきたいと思います。
pop.Session.RemoteEndPoint = New Dart.Mail.IPEndPoint(Me.Pop3Server, 110)
POP3サーバーのアドレスとポート番号を指定して、接続先をRemoteEndPointメソッドに設定します。
pop.Session.Username = Me.UserId / pop.Session.Password = Me.Password
POP3接続用のユーザIDとパスワードを指定します。
pop.Connect()
POP3サーバーに接続します。
pop.Authenticate(True, True)
認証を行い、POP3サーバーにログインできたら、pop.Messagesにメールの情報を取得します。
Await Task.Run(Sub()…)
Task.Runメソッドを使って、その中で指定しているコード(For Eachループ)を非同期実行しています。また、Awaitをつけてその非同期実行を待ち合せしているので、For Eachループがすべて回りきった後に次の行のpop.Close()やRemoveHandlerが実行されます。
MainForm.vbにあるet_Button_Clickイベントプロシージャは、この非同期待ち合わせが完了すると処理が終わることになります。
For Each… mes.Get(0)…
Task.Runの中で指定しているのは、pop.Messagesに格納されたメール情報を元に1通ずつ、Get(0)メソッドを実行して本文なし(0指定のため)でヘッダだけを受信しています。この受信のタイミングでProgressイベントが随時発生します。
PopProgressイベントプロシージャの中では、取得できたヘッダ情報から、From、Subject、DateをMe.Itemsに追加しています。
実行結果の確認
それでは、実行結果を確認してみましょう。
日本語のFromやSubjectの文字化けもなく、一覧が取得できました。
WPFアプリへの適用例
(※サンプルファイルの「CZ1310Wpf」に対応)
Windowsフォームには、Secure iNetSuiteのコンポーネントをツールボックスからドラッグ&ドロップで設置ができます。そのため、WindowsフォームアプリはSecure iNetSuiteが想定しているアプリ形式だともいえるでしょう。
そこで次の勝手流動作確認として、Windows 8.1+Visual Studio 2013に加えてWPFアプリ形式でもSecure iNetSuiteを使ってみようと思います。WPFアプリでも、popコンポーネントを使ってメール一覧を作成するModel部とWindowsフォームのView部を別クラスになるような構成で作成します。
プロジェクトの構成
Visual Studio 2013を起動して[ファイル]-[新しいプロジェクト]から[Visual Basic]-[Windows]-[WPFアプリケーション]を選択してWPFアプリの新規プロジェクトを作成します。
新規プロジェクトが作成できたら、以下の操作を行い、プロジェクトの初期状態を定義します。
- 「Views」「ViewModels」「Models」の3つのフォルダを作成
- Window1.xamlファイルをMainWindow.xamlファイルにリネームしてからViewsフォルダに移動
- ViewModelsフォルダにMainViewModelクラスを作成
- ModelsフォルダにMailModelクラスを作成
MailModelクラスやMainViewModelクラスの役目やコードは、Windowsフォームアプリのサンプルコードと同じ内容になります。
Secure Mailの参照追加、ライセンスファイルの追加もWindowsフォームアプリのサンプルと同じ手順で行います。ここまで作業が完了したときのプロジェクト構成は、次のようになります。
画面の定義
画面の構成も、Windowsフォームアプリのサンプルと同じような感じにしてみましょう。
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="CZ1310Wpf" Height="270" Width="600"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Button Content="一覧受信" Width="75" Height="23" Grid.Row="0" Margin="5" HorizontalAlignment="Left" Command="{Binding GetMailCommand}"/> <ListBox Grid.Row="1" Margin="5" ItemsSource="{Binding Items}"> <ListBox.ItemTemplate> <DataTemplate> <ListBoxItem> <StackPanel> <TextBlock Text="{Binding From}" /> <TextBlock Text="{Binding Subject}" /> </StackPanel> </ListBoxItem> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window>
[一覧受信]をクリックしたら、受信メールの一覧をボタン下のListBoxに表示します。
Windowsフォームのときは、ボタンにClickイベントを指定していました。WPFアプリでも同じようにClickイベントを記述することができます。
このサンプルは、別バリエーションとしてボタンにはCommandプロパティを設定して、その中でMainViewModelのプロパティを呼び出す方式でボタンがクリックされた時の処理を記述しています。これがXAMLのコマンドバインディングと呼ばれるものです。この方式に変更すると、コードビハインド側にClickイベントに対する処理を記述しなくて済みます。
画面のコードビハインド定義
画面定義がおわったら、次に画面定義に紐づいたコードを記述します。
ソリューションエクスプローラーで「MainWindow.xaml.vb」をダブルクリックすると、MainWindow.xamlと関連づいたコード(コードビハインド)を記述するためのコードエディタが開きます。
Class MainWindow Private ViewModel As New MainViewModel Public Sub New() InitializeComponent() Me.DataContext = Me.ViewModel End Sub End Class
WPFアプリでは、XAML側でボタンクリックしたときに呼び出すViewModelのプロパティを指定できるので、コードビハインドに記載が必要なコードはほとんどありません。
MainViewModelへの変更
Windowsフォームアプリを作成したときのMainViewModelクラスでは、ボタンクリック時にMainWindow.vbから呼び出されていたのはGetMailメソッドでした。XAMLからはICommandインターフェースを持ったプロパティにコマンドバインディングされて呼び出されるので、MainViewModelのGetMailメソッド部分を次のように書き換えます。
: (中略) : Public Async Function GetMail() As Task Await Me.Model.GetMail() End Sub : (中略) :
: (中略) : Private _GetMailCommand As Common.RelayCommand Public Property GetMailCommand() As Common.RelayCommand Get If _GetMailCommand Is Nothing Then _GetMailCommand = New Common.RelayCommand(AddressOf Me.GetMail) End If Return _GetMailCommand End Get Set(value As Common.RelayCommand) _GetMailCommand = value End Set End Property Private Async Sub GetMail() Await Me.Model.GetMail() End Sub : (中略) :
記述量は少し増えてしまうのですが、こうすることでXAML側とViewModelの関係はますます疎にできます。なお、Common.RelayCommandクラスはサンプルコードのCommonフォルダの中に新規作成していますので、詳しい内容はサンプルコードをダウンロードして確認してください。
実行結果の確認
それでは、実行結果を確認してみましょう。
残念ながらMailModelクラスの中で実行時エラーとなってしまいました。原因は、Windowsフォームのときと異なり、MailModelクラスのItemsはMainViewModelを介してMainWindowのListBoxにBindingされている点です。つまり、非同期実行しているmes.Get(0)実行時に発生するProgressイベントの処理の中でMe.Items.Addすると、それはUIスレッドでXAMLが更新することになり、非同期スレッドからUIスレッドというスレッド間の連携が生じてしまうからです。
そのため、PopProgressイベントプロシージャの中を次のように書き換えることで解決ができます。
: (中略) : Private Context As SynchronizationContext = SynchronizationContext.Current : (中略) : Private Sub PopProgress(sender As Object, e As Dart.Mail.PopProgressEventArgs) If e.Final Then Me.Context.Post(Sub(o) Me.Items.Add(New TMail With { .From = e.Message.Message.From, .Subject = e.Message.Message.Subject, .SendDate = e.Message.Message.Date }) End Sub, Nothing) End If End Sub : (中略) :
それでは再度、実行結果を確認してみましょう。
今度は正常に表示できました。もちろん文字化けすることもまったくなく、一覧が作成されています。
Windowsストアアプリへの適用例
(※サンプルファイルの「CZ1310Wrt」に対応)
WPFアプリでもSecure iNetSuiteが使えたので、試しにWindowsストアアプリでも使ってみましょう。デスクトップアプリであるWindowsフォームアプリやWPFアプリは.NET Framework(今回は.NET Framework 4.5)で動作します。対して、クラスの作りは非常によく似ていますが、WindowsストアアプリはWindows Runtimeという別フレームワーク上で動作します。Windows 8.1+Visual Studio 2013+Windows Runtimeの組み合わせは、Secure iNetSuiteの実行環境としてはまったく想定外の環境だと思われます。
プロジェクトの構成
Visual Studio 2013を起動して[ファイル]-[新しいプロジェクト]から[Visual Basic]-[Windowsストア]-[新しいアプリケーション]を選択し、Windowsストアアプリの新規プロジェクトを作成します。
新規プロジェクトが作成できたら、以下の操作を行い、プロジェクトの初期状態を定義します。
- 「Views」「ViewModels」「Models」の3つのフォルダを作成
- MainPage.xamlファイルをViewsフォルダに移動
- ViewModelsフォルダにMainViewModelクラスを作成
- ModelsフォルダにMailModelクラスを作成
MailModelクラスやMainViewModelクラスの役目やコードは、WPFアプリのサンプルコードと同じ内容になります。
Secure Mailの参照追加
Secure Mailコンポーネントの参照設定を行います。
ソリューションエクスプローラーでプロジェクト名を右クリックして[追加]-[参照]メニューから[参照マネージャー]ダイアログを表示します。続いて[アセンブリ]の中を探してみますが、「PowerTCP Mail for.NET」は表示されません。サポートしていないことがほぼ確定していますが、[参照…]をクリックして「Dart.Mail.dll」を直接参照してみます。
すると「.NETCoreではなく.NET Frameworkを対象としている」ため、サポートされていませんと明確に断言されてしまいます。
残念ながらWindowsストアアプリでは、Secure iNetSuiteを現時点で使用することはできないようです。もちろん似ているとはいえ.NET Frameworkとは別の仕組みなので、使えなくて当然なのです。ちょっと無茶しすぎましたね。
まとめ
あえてツールボックスからドラッグ&ドロップで配置して使う方法を封印してSecure Mailを使ってみましたが、どのような印象になったでしょうか。ドラッグ&ドロップで作った時との相違点は、基本的には「pop As New Dart.Mail.Pop」と明示的にコンポーネントからオブジェクトを生成するコードのみになります。参照追加やライセンスファイルという手間はありましたが、どちらの方法でも難易度に大差はないと言えるでしょう。
それよりも、WPFアプリなどWindowsフォームアプリ以外でも使えるということの方が重要だと思います。今回は取り上げませんでしたが、Windowsサービスアプリの中でも使用可能なので、サーバーにWindowsサービスとしてSecure iNetSuiteを使ったアプリを常駐させておき、FTPやメールでの自動連携を実現するようなシステムも構築できます。
ぜひ、Secure iNetSuiteを使って、安心・安全な日本語メール環境/日本語ファイル転送環境を実現してみてください。