Outlook VBAで送信メールを任意フォルダーへ保存する方法|ExchangeとGmail(IMAP)両対応の決定版コード

Outlookで「送信するときに保存先フォルダーを選びたい」のに、Exchangeでは動くVBAがGmail(IMAP)では反応せず、結局「送信済みアイテム」にしか残らない――そんな悩みを一気に解消します。根本原因はたった1行の条件分岐。この記事では原因の仕組みから、Exchange/Gmailの両方で確実に動く最終版コード、実装手順、運用のコツまでを実践的にまとめました。

目次

問題の全体像

同一のOutlookプロファイル内で、ExchangeアカウントではVBAマクロが正常に動作し、送信前にフォルダー選択ダイアログ(PickFolder)を出して保存先を指定できる一方、Gmail(IMAP)アカウントではマクロが発動せず、メールは既定の「送信済みアイテム」にだけ保存される現象が起こります。ユーザー体験としては「Exchangeだけ便利、Gmailは不便」という不一致が発生します。

症状早見表

項目Exchange アカウントGmail(IMAP)アカウント
送信時のフォルダー選択ダイアログ表示される表示されない/後続処理へ進まない
保存先選択したフォルダーに保存既定の「送信済みアイテム」にのみ保存
ユーザー要望両アカウントで同じ操作感(送信時に保存先を選べる)を実現したい

原因:DeleteAfterSubmit の既定値差による条件分岐スキップ

元のコードには以下の条件が含まれていました。

If Item.DeleteAfterSubmit = False Then
    ' 保存先を変える処理…
End If

IMAP メール(Gmail)の場合、DeleteAfterSubmit プロパティが True になる挙動があるため、この条件文を満たせず、以降の保存先切り替え処理がスキップされていました。結果として、Gmail だけが「既定の送信済み」に残り続けるわけです。

プロパティ既定値の違い(傾向)

プロパティExchangeGmail(IMAP)影響
DeleteAfterSubmitFalseTrueTrue の場合、条件分岐で保存先変更ロジックに到達しない
SaveSentMessageFolder任意のフォルダーを設定・保存可任意のフォルダーを設定・保存可(ただし条件により未到達)条件を取り除けば両方で機能する

解決策:DeleteAfterSubmit を先頭で False 固定 → PickFolder の結果へ保存

アカウント種別に依存しない最小変更で、確実に動くアプローチがこちらです。DeleteAfterSubmit = False を先頭で強制し、その後にユーザーが選んだフォルダーへ直接保存します。移動処理の追加は不要です。

決定版コード(ThisOutlookSession)

Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
    If TypeOf Item Is Outlook.MailItem Then
        SaveSentMail Item
    End If
End Sub

Private Sub SaveSentMail(Item As Outlook.MailItem)
Dim F As Outlook.Folder          ' Outlook.MAPIFolder でも可
Item.DeleteAfterSubmit = False   ' ← 先頭で必ず False に固定
Set F = Application.Session.PickFolder
If Not F Is Nothing Then
Set Item.SaveSentMessageFolder = F
End If
End Sub 
  • ポイント1:DeleteAfterSubmit を最初に False にすることで、アカウント種別の差を吸収します。
  • ポイント2:PickFolder はユーザーがキャンセルし得るため、If Not F Is Nothing Then で安全にガードします。
  • ポイント3:SaveSentMessageFolder に直接フォルダーをセットするので、送信後に移動するロジックは不要です。
  • 検証環境:Outlook 2016 以降(Windows 版)で確認。

導入手順(手戻りゼロで確実に)

  1. Outlook を起動し、Alt+F11 で「Microsoft Visual Basic for Applications」を開く。
  2. 左ペインの「プロジェクト – VBAProject」内から ThisOutlookSession をダブルクリック。
  3. 上記の決定版コードを貼り付けて保存(Ctrl+S)。
  4. Outlook をいったん終了し、再起動。
    ※「再起動までイベントが効かない」ケースがあるため必ず実施。
  5. トラスト センターマクロの設定 で「VBA を有効化」。組織ポリシー配下の場合は管理者の承認や署名付きマクロが必要なことがあります。
  6. テスト送信:新規メールを作成し送信するとフォルダー選択ダイアログが表示され、選んだフォルダーに保存されれば成功。

運用のコツ(UXとミス防止)

  • お気に入りを活用:よく使う保存先をナビゲーションの「お気に入り」に入れておくと PickFolder での探索が早くなります。
  • アカウント混在時の迷子防止:フォルダー名に接頭辞(例:「営業\_」、「開発\_」)を付けると、同名フォルダーを見分けやすくなります。
  • 重複保存の抑止:IMAP(特に Gmail)はサーバー側でも送信済みコピーを作ることがあります。Outlook 側の「送信済みアイテムに保存」設定と合わせて、保存先を一箇所に寄せる方針にしておくと、重複と混乱を防げます。
  • 共有メールボックス:他人のメールボックスへ保存する場合は、当該メールボックスへの適切な権限が必要です。

採用しなかった案とその理由

  • ItemSend + ItemSendComplete の二段構え:Exchange は直接保存、IMAP は送信後に移動――という構成は実装コストが増え、失敗時の巻き戻し処理も複雑になります。
  • IMAP で SaveSentMessageFolder = Nothing実行時エラーが発生するため例外処理が増え、安定運用に不向きです。

トラブルシューティング

現象想定原因対処
ダイアログが出ない他マクロ/COM アドインが先に処理している/イベント競合不要なアドインを無効化。ItemSend の中で TypeOf チェックを入れて影響範囲を限定
保存先にコピーされないDeleteAfterSubmit が True のまま/コードが再起動後に読み込まれていない本記事の最終版コードで先頭に DeleteAfterSubmit = False を明記。Outlook を再起動
会議出席依頼やタスクでエラーMailItem 以外のアイテムに処理が当たっているIf TypeOf Item Is Outlook.MailItem Then でガード(既に実装済み)
保存先の選択に時間がかかるフォルダー階層が深い/候補が多い「お気に入り」に集約。後述の「前回フォルダー記憶」拡張を検討
組織環境で実行不可マクロ禁止ポリシー/署名必須管理者に相談。自己署名証明書での署名やコード署名の導入

安全・セキュリティの留意点

  • マクロ有効化:トラスト センターで VBA を許可。組織ポリシーでは署名付きマクロのみ許可される場合があります。
  • プログラムによるアクセス:セキュリティ設定により、送信時に警告が表示されることがあります。最新のウイルス対策と信頼済み発行元の署名で不要なプロンプトを抑制できます。
  • 最小権限:共有先フォルダーに保存する際は必要なアクセス許可のみ付与。

より快適に:前回選択フォルダーを記憶する(任意の拡張)

毎回 PickFolder を開くのが手間なら、最後に選んだフォルダーをレジストリ(ユーザー領域)へ保存し、次回も同じ場所に保存する拡張が便利です。以下は追加レシピとしての一例です(決定版コードに置き換えではなく、拡張として利用)。

' --- 任意のモジュールに追加 ---
Private Const APP_NAME As String = "OutlookVBA"
Private Const SEC_NAME As String = "SaveSent"
Private Const KEY_FOLDER_ID As String = "FolderID"
Private Const KEY_STORE_ID  As String = "StoreID"

Private Function GetLastFolder() As Outlook.Folder
Dim fid As String, sid As String
fid = GetSetting(APP_NAME, SEC_NAME, KEY_FOLDER_ID, "")
sid = GetSetting(APP_NAME, SEC_NAME, KEY_STORE_ID, "")
On Error Resume Next
If fid <> "" And sid <> "" Then
Set GetLastFolder = Application.Session.GetFolderFromID(fid, sid)
End If
On Error GoTo 0
End Function

Private Sub RememberFolder(ByVal F As Outlook.Folder)
On Error Resume Next
SaveSetting APP_NAME, SEC_NAME, KEY_FOLDER_ID, F.EntryID
SaveSetting APP_NAME, SEC_NAME, KEY_STORE_ID, F.StoreID
On Error GoTo 0
End Sub

' --- SaveSentMail を次のように差し替え可能 ---
Private Sub SaveSentMail(Item As Outlook.MailItem)
Dim F As Outlook.Folder
Item.DeleteAfterSubmit = False


Set F = GetLastFolder()
If F Is Nothing Then
    Set F = Application.Session.PickFolder
End If

If Not F Is Nothing Then
    Set Item.SaveSentMessageFolder = F
    RememberFolder F
End If


End Sub 

この拡張により、同じフォルダーへ連続して保存するケースではダイアログ表示が省略され、入力時間を短縮できます。異なるフォルダーへ保存したい場合は、前述の基本版のまま運用するか、送信前に一時的に拡張を無効化する運用にしてください。

応用:送信アカウントごとに候補を出し分け(任意の拡張)

Exchange と Gmail で保存先を変えたい、部署別に保存先を固定したい――といった要望には、アカウント名とフォルダーの対応表を用意します。参照設定は不要(遅延バインディング)にしてあります。

Private Function ResolveDefaultFolder(ByVal acctName As String) As Outlook.Folder
    Dim map As Object 'Scripting.Dictionary
    Set map = CreateObject("Scripting.Dictionary")
    ' --- アカウント表示名とフォルダーの EntryID を対応付け ---
    ' map("Exchange の表示名") = "EntryID|StoreID"
    ' map("Gmail の表示名")    = "EntryID|StoreID"


Dim pair As String
If map.Exists(acctName) Then
    pair = map(acctName)
    Dim fid As String, sid As String, p() As String
    p = Split(pair, "|")
    fid = p(0): sid = p(1)
    On Error Resume Next
    Set ResolveDefaultFolder = Application.Session.GetFolderFromID(fid, sid)
    On Error GoTo 0
End If


End Function

Private Sub SaveSentMail(Item As Outlook.MailItem)
Dim F As Outlook.Folder
Item.DeleteAfterSubmit = False


Dim acctName As String
If Not Item.SendUsingAccount Is Nothing Then
    acctName = Item.SendUsingAccount.DisplayName
End If

Set F = ResolveDefaultFolder(acctName)
If F Is Nothing Then
    Set F = Application.Session.PickFolder
End If

If Not F Is Nothing Then
    Set Item.SaveSentMessageFolder = F
End If


End Sub 

運用開始前に、対象フォルダーの EntryIDStoreID を取得して対応表へ入力しておく必要があります(管理者がセットアップして配布するとスムーズです)。

実運用でのベストプラクティス

  • 手間の最小化:「常に同じフォルダー」派は前回フォルダー記憶の拡張でクリックを減らす。「案件ごとに変える」派は、お気に入り+短いフォルダー名で選びやすく。
  • ログと再現性:保存先の誤りが問題になる組織では、件名末尾にフォルダー名を入れる社内ルールを加えると追跡しやすくなります(VBAで自動追記も可)。
  • ガバナンス:Exchange Online などで「他メールボックスへの保存禁止」や「既定以外禁止」のポリシーがある場合は、運用前に管理者へ相談。
  • メンテナンス:Outlook のメジャーアップデート後は、イベントが読み込まれているか(ThisOutlookSession が有効か)を確認。必要なら「コンパイル(デバッグ → VBAProject のコンパイル)」を実行。

技術的補足:なぜ DeleteAfterSubmit を False 固定でよいのか

DeleteAfterSubmit は「送信後にアイテムを削除するか」を示すフラグです。これが True のままだと、保存先の切り替えに進む前提が崩れ、そもそもロジックが走らない実装になりがちです。反対に、先頭で False を強制すれば送信後のアイテムは削除されず、SaveSentMessageFolder で指定した場所へ正しく保存されます。Exchange と IMAP(Gmail)で既定値が異なっても、コードが責務として値を確定するため、挙動を統一できます。

検証環境と再現テストケース

OS / Outlookアカウントテスト観点期待結果
Windows 10/11 + Outlook 2016/2019/Microsoft 365Exchange(MAPI)送信時ダイアログ、指定保存フォルダー選択後、該当フォルダーへ保存
同上Gmail(IMAP)DeleteAfterSubmit=False 強制の効果ダイアログ表示・指定フォルダーへ保存(既定条件に左右されない)
同上混在プロファイルアカウント切替時の安定性どちらのアカウントでも同一UXを維持

FAQ

  • Q:会議出席依頼にもダイアログが出ますか?
    A:いいえ。本コードは TypeOf Item Is Outlook.MailItemメール のみを対象にしているため、会議やタスクには影響しません。
  • Q:送信時に毎回ダイアログを出したくない。
    A:前回フォルダー記憶の拡張を使うか、アカウント別の既定フォルダーを対応表で指定してください。
  • Q:Gmail 側の「送信済み」へもコピーが残り、二重になりませんか?
    A:組織・アカウント設定によりサーバー側コピーが作成されることがあります。Outlook 側の「送信済みアイテムに保存」設定とポリシーを合わせ、保存先を一箇所に寄せると重複を防げます。
  • Q:VBA の参照設定は必要?
    A:本記事のコードは Outlook オブジェクトを前提にしており、標準環境で追加の参照は不要です(応用の辞書利用は遅延バインディング)。

まとめ

原因はシンプル――DeleteAfterSubmit の既定値差(特に Gmail/IMAP で True になり得る点)により、保存先変更ロジックへ到達していなかったこと。送信処理の先頭で DeleteAfterSubmit=False を明示し、PickFolder で選んだフォルダーを SaveSentMessageFolder にセットするだけで、Exchange と Gmail の両方で同じ操作感を実現できます。ルールや移動の二段構えは不要。あとは運用の工夫(お気に入り、前回フォルダー記憶、アカウント別の既定化)で、現場の生産性を大きく引き上げられます。

付録:最小実装(貼り付け用)

Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
    If TypeOf Item Is Outlook.MailItem Then
        SaveSentMail Item
    End If
End Sub

Private Sub SaveSentMail(Item As Outlook.MailItem)
Dim F As Outlook.Folder
Item.DeleteAfterSubmit = False
Set F = Application.Session.PickFolder
If Not F Is Nothing Then
Set Item.SaveSentMessageFolder = F
End If
End Sub 

チェックリスト(導入時にこの順で確認)

  1. VBA が「ThisOutlookSession」に正しく保存されている。
  2. Outlook を再起動した。
  3. マクロが有効化されている(トラスト センター)。
  4. テスト送信でフォルダー選択ダイアログが出る。
  5. 選択したフォルダーに送信済みメールが保存される。
  6. Exchange と Gmail(IMAP)の双方で同じ動作になる。

実務ポイント(再掲)

  • マクロを有効化:トラストセンターのマクロ設定で VBA を許可する。
  • フォルダー選択の効率化:保存先は「お気に入り」に登録して選択を簡略化。
  • 組織ポリシーの確認:他メールボックスへの保存を制限する設定がある場合は管理者に相談。
  • アドイン競合:送信時に他のマクロ/COM アドインが動いているとダイアログが出ないことがある。イベントの優先順位や構成を見直す。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次