[vba]outlookでappfoldersを経由してメールを送信しない方がよい

はじめに

Outlookには、誤送信を防ぐために、メールを送信しても送信トレイに移動し、送信トレイに対してフォルダ更新をすることで初めてメールが送信されるという仕組みがある。

Outlook > ファイル > 詳細設定 > 「接続したら直ちに送信する」のチェックを外すことで上記動作となる。

VBAでメールを送る場合もこの設定は適用され、mail.send()としただけではファイルは送信されず、送信トレイに移動するだけである。

送信トレイのメールを送信する方法(失敗例)

VBAでは送信トレイにあるメールを送信することはできない。送信する代わりに同期処理が必要となる。

送信トレイに移動したメールを送信するには2つの設定が必要である。

  1. 送信対象とするメールフォルダに対して同期フラグ「InAppFolderSyncObjectを立てる。
  2. 同期を開始する「Session.SyncObjects.AppFolders.Start

ソースコード

■呼び出し処理
Public Sub MailTest()
    
    ' メールアカウント2の同期フラグをONにする
    Call ChangeSendFlag(TEST_EMAIL_2, True)
    
    ' メールアカウント1にメールを送る
    Call SendMail(TEST_EMAIL_1, "1通目")
    Call SendMail(TEST_EMAIL_1, "2通目")
    Call SendMail(TEST_EMAIL_1, "3通目")

    ' メールアカウント2にメールを送る
    Call SendMail(TEST_EMAIL_2, "1通目")
    Call SendMail(TEST_EMAIL_2, "2通目")
    Call SendMail(TEST_EMAIL_2, "3通目")
    
    ' メールアカウント2の同期を開始する
    Call SyncStart()
    ' メールアカウント2の同期フラグをOFFにする
    Call ChangeSendFlag(TEST_EMAIL_2, False)
    
    MsgBox ("完了")
End Sub
■共通処理
' 送信フラグを切り替える
Private Sub ChangeSendFlag(sender_mail_address As String, sync_flag As Boolean)

    ' Outlookオブジェクト
    Dim outlook As outlook.Application
    Set outlook = New outlook.Application

    ' メールアカウントを取得する
    Dim account As outlook.account
    Set account = outlook.Session.Accounts(sender_mail_address)
    
    ' アカウントに紐づくデータファイルを取得する
    Dim store As outlook.store
    Set store = account.DeliveryStore
    
    ' データファイルから送信トレイを取得する
    Dim outbox As outlook.Folder
    Set outbox = store.GetDefaultFolder(olFolderOutbox)
        
    ' 送信トレイの同期フラグを設定する
    outbox.InAppFolderSyncObject = sync_flag
End Sub

' 同期を開始する
Private Sub SyncStart()

    ' Outlookオブジェクト
    Dim outlook As outlook.Application
    Set outlook = New outlook.Application
    outlook.Session.SyncObjects.AppFolders.Start
End Sub

AppFoldersとは

引用 (※2)

SyncObject は、 Folder オブジェクトの InAppFolderSyncObject プロパティが True に設定するとフォルダーが追加自動的にします。 SyncObject では、 Microsoft Outlookフォルダー、アドレス帳、およびオフラインで使用するフォルダーのホーム ページを同期することができます。

InAppFolderSyncObjectの動作

まず、MailTest()にてChangeSendFlag()が呼ばれる。さらに内部ではoutbox.InAppFolderSyncObject = sync_flagが呼ばれる。

InAppFolderSyncObjectのフラグをtrueにしたときの挙動は以下の通り

引用 (※1)

Returns or sets a Boolean that determines if the specified folder will be synchronized with the email server. Read/write.

If True, this folder will be synchronized when the “Application Folders” SyncObject is synchronized. If False, the folder will not synchronize.

This is equivalent to selecting the check box for this folder in the Application Folders group on the Send/Receive dialog box.

If this property is set to True, and the “Application Folders” SyncObject does not already exist, a SyncObject will be automatically created. The “Application Folders” SyncObject is the only Send/Receive group that can be programmatically modified.

和訳

True に設定されている場合、このフォルダーは「アプリケーション フォルダー(Application Folders)」の SyncObject が同期されるときに、一緒に同期されます。
False に設定されている場合、このフォルダーは同期されません。

これは、「送受信」ダイアログ ボックスの [アプリケーション フォルダー]グループで、そのフォルダーに対してチェックボックスをオンにする操作と同じ意味です。

このプロパティを True に設定すると、もし「アプリケーション フォルダー(Application Folders)」の SyncObject がまだ存在しない場合、自動的に作成されます。
なお、「アプリケーション フォルダー(Application Folders)」の SyncObject は、プログラムから変更できる唯一の送受信グループです。

要約

  • InAppFolderSyncObject は AppFolders グループの「同期対象に含めるかどうか」を表すだけ。
  • True にすると、そのフォルダーが AppFolders グループの同期対象になる
  • False にしても 送信そのものを止める機能ではない
  • しかも、プログラムで変更できる送受信グループはこの “Application Folders” だけ、という仕様。

バグの挙動

ところが、以下のコードを実行すると、InAppFolderSyncObjectのフラグによらず、1通目、2通目共に送信されてしまう。

Chat GPT曰く、Outlookをオフラインにしたり、キャッシュをクリアすることで、InAppFolderSyncObjectのフラグをリセットすることができるようであるが、やはり手間である。

' 送信フラグを切り替える
Private Sub ChangeSendFlag(sender_mail_address As String, sync_flag As Boolean)

Public Sub MailTest_003()
    ' Trueにしているのでこれは送られてよい。
    Call ChangeSendFlag(TEST_EMAIL_2, True)
    Call SendMail(TEST_EMAIL_2, "1通目")
    Call SyncStart(TEST_EMAIL_2)
    ' Falseにしているが、こちらも送信されてしまう。
    Call ChangeSendFlag(TEST_EMAIL_2, False)
    Call SendMail(TEST_EMAIL_2, "2通目")
    Call SyncStart()
    
    MsgBox ("完了")
End Sub

バグにより起こりうること

例えば、あるユーザーはメールアカウントA ~ Cを利用しているとする。

プログラムAではメールアカウントA、BではB、CではCという具合に、対応付けがされているとする。

プログラムAを実行するとAppFolders グループにメールアカウントAの送信トレイが追加される。次にプログラムBを実行するとAppFolders グループにメールアカウントBの送信トレイが追加される。

つまり、プログラムBを実行しようとすると、AppFolders グループに登録されているA, Bの送信トレイのメールを送信する。本来、プログラムBを実行したら、Bの送信トレイだけを見に行きたいのだ。可能性としては低いが、メールアカウントAで手動で送信し、送信トレイにメールが移動した直後にプログラムBを実行した場合、手動送信したメールも送信されてしまう。

文字に起こすと非常に分かりづらいが、つまりは送信すべきでないメールが送られてしまう可能性があるということ。

ゆえに、AppFolders グループ は利用せず手動で送受信グループを作成するのが安全である。

 

送信トレイのメールを送信する方法(安全に動作をさせる)

送受信グループの作成

(1) Outlook > ファイル > オプション > 詳細設定 > 送受信 > 新規登録

グループ名を指定し作成する。

(2) 「次の時間ごとに自動的に送受信を実行する」のチェックを外す

(3) VBAで利用するメールアカウントのみ、「この送受信グループに選択されたアカウントを含める」にチェックを入れる。

 

ソースコード

■共通処理
' 同期を開始する
Private Sub SyncStart(sender_mail_address As String)

    ' Outlookオブジェクト
    Dim outlook As outlook.Application
    Set outlook = New outlook.Application
    
    ' 同期開始
    outlook.Session.SyncObjects("VBA実行用送受信フォルダ").Start
End Sub

変更点

  • InAppFolderSyncObjectフラグの更新処理の削除
    ⇒ あくまでもAppFolder を利用する際に必要な処理なので、自作の送受信グループを使う場合には必要のない処理
  • 送受信グループの変更
    ⇒ 作成した送受信グループに対して同期を開始する
    SyncObjects("VBA実行用送受信フォルダ").Start

これにより、VBAから利用しないメールアカウントの送受信フォルダには影響を与えないようにすることができる。

参考サイト

※1 Folder.InAppFolderSyncObject property (Outlook) | Microsoft Learn

※2 SyncObjects.AppFolders プロパティ (Outlook) | Microsoft Learn