共有メールボックスの送信済みアイテム フォルダーを取得するマクロ

コメントにて以下のご要望をいただきました。


共有メールボックス下で送信済みフォルダーのFolder識別子を取得したいのですが、方法はあるでしょうか?

Microsoftの解説を読むと、Session.GetSharedDefaultFolderで、引数にolFolderSentMailは取れないとされており、実際に試してみましたが、取得出来ず、メール類のデータが取り出せません。

もしも、ご存じなら教えて頂けると大変助かります。


ご確認いただいている通り、GetShraredDefaultFolder メソッドでは送信済みアイテム フォルダーは取得できません。
Outlook の [他のユーザーのフォルダー] でも送信済みアイテム フォルダーが取得できないためです。

ただ、共有メールボックスをオートマッピングや追加のアカウントとして追加しており、メールボックス全体が参照できる状態なのであれば、共有メールボックスに対応する Store オブジェクトの GetDefaultFolder を使用して送信済みアイテム フォルダーを取得することが可能です。

以下のマクロ関数は、引数で指定した名前の共有メールボックスの送信済みアイテム フォルダーを取得するというものです。
ナビゲーション ウィンドウで表示されている文字列を指定する必要があり、オートマッピングの場合は表示名、追加のアカウントの場合はメールアドレスになるでしょう。

' 指定された共有メールボックスの送信済みアイテムを取得する関数
Private Function GetSentItemsOfSharedMailbox(strName As String) As Folder
     Dim stoShared As Store
     '
     Set stoShared = Session.Stores(strName)
     Set GetSentItemsOfSharedMailbox = stoShared.GetDefaultFolder(olFolderSentMail)
End Function

マクロの登録方法やメニューへの追加について

受信時に差出人の名前の文字列を / で分割し、入れ替えるマクロ

受信時に差出人の名前の 最初の / より前の文字列を削除するマクロのコメントにて以下のご要望をいただきました。


初めまして。
記事参考にさせて頂いています。

私の会社では

【tanaka taro/社員番号/田中 太郎】

となっているため

【田中 太郎/tanaka taro】

へ変換するマクロを教えて頂きたいです。

宜しくお願い致します。


参照いただいていたマクロは単に最初の / 以降だけを取り出すということで実現できていましたが、今回のマクロは文字列の入れ替えが必要になります。
特定の記号で区切った文字列の順序を入れ替えるという場合、Split 関数が役に立ちます。
この関数は特定の文字列を基準に文字列を分割して配列に格納するものであり、これにより作成された配列を任意の順番で連結することで、順序を入れ替えることが可能です。
これを使用したマクロは以下のようになります。

Private Sub Application_NewMailEx(ByVal EntryIDCollection As String)
     Dim objItem As Object
     Set objItem = Session.GetItemFromID(EntryIDCollection)
     If objItem.MessageClass = "IPM.Note" Then
         ' メールアイテムの場合だけ入れ替え処理を実行
         SwapName objItem
     End If
End Sub
'
Private Sub SwapName(ByVal objMail As MailItem)
     Dim strName As String
     Dim arrName As Variant
     ' 差出人の表示名を取得
     strName = objMail.SenderName
     ' 表示名が "xxx/xxx/xxx" というフォーマットの場合だけ入れ替え処理
     If strName Like "*/*/*" Then
         ' 表示名を / で分割
         arrName = Split(strName, "/")
         ' 表示名の最初と最後を入れ替え、中央の文字列を削除
         strName = arrName(2) & "/" & arrName(0)
         ' 代理送信者名に変更後の表示名を設定
         objMail.SentOnBehalfOfName = strName
         ' アイテムを保存
         objMail.Save
     End If
End Sub

マクロの登録方法やメニューへの追加について

返信時や転送時のヘッダーをシンプルにするマクロ(改訂版)

返信時や転送時のヘッダーをシンプルにするマクロのコメントにて以下のご要望をいただきました。


こんにちは。

365でこちらを設定してみたところ、デフォルトのヘッダーが表示されてしまい通常の返信・転送と同じ状態になってしまいました。

365で使用する場合の注意点やコードの修正点等あればご教示ください。


元のマクロでは返信・転送メッセージでヘッダーを追加する位置を取得するための変数を Integer 型で定義していたのですが、HTML 形式のメールの Style タグが多いなどの理由で Integer 型の最大値 (32767) を超える位置にヘッダーを追加する必要がある場合に、オーバーフローを起こして正常に動作しなくなっていたようです。
この問題を修正したバージョンは以下の様になります。

' 全員に返信
Public Sub ReplyAllSimple()
     Dim objMail As MailItem
     With ActiveInspector.CurrentItem
         Set objMail = .ReplyAll
         ReplaceHeader objMail, .SenderName, .SentOn, "Reply"
         objMail.Display
     End With
End Sub
' 転送
Public Sub ForwardSimple()
     Dim objMail As MailItem
     With ActiveInspector.CurrentItem
         Set objMail = .Forward
         ReplaceHeader objMail, .SenderName, .SentOn, "Forward"
         objMail.Display
     End With
End Sub
' ヘッダ置き換え
Private Sub ReplaceHeader(objMail As MailItem, strSender As String, dtSentOn As Date, strMode As String)
     On Error Resume Next
     Dim objReply As MailItem
     Dim strBody As String
     Dim s As Long
     Dim e As Long
     '
     If objMail.BodyFormat = olFormatHTML Then
         strBody = objMail.HTMLBody
         s = InStr(strBody, "<a name=""_MailOriginal"">")
         e = InStr(s, strBody, "</p>")
         strBody = Left(strBody, s - 1) & "<b>(" & dtSentOn & "), " & strSender & " </b> wrote:" & Mid(strBody, e)
         'strBody = Left(strBody, s - 1) & "<b>From: </b>" & strSender & Mid(strBody, e)
         objMail.HTMLBody = strBody
     Else
         Dim strPrefix As String
         strPrefix = ""
         strBody = objMail.Body
         s = InStr(strBody, "-----Original Message-----")
         If Mid(strBody, s - 1, 1) <> vbLf Then
             strPrefix = GetPrefixText(strMode)
         End If
         e = InStr(s, strBody, vbCrLf & strPrefix & vbCrLf)
         strBody = Left(strBody, s - 1) & "(" & dtSentOn & "), " & strSender & " wrote:" & Mid(strBody, e)
         'strBody = Left(strBody, s - 1) & "-----Original Message-----" & vbCrLf & "From: " & strSender & Mid(strBody, e)
         objMail.Body = strBody
     End If
End Sub
'
Function GetPrefixText(strMode As String) As String
     On Error Resume Next
     Dim wshShell As Variant
     Dim iStyle As Integer
     Dim strPrefix As String
     strPrefix = ""
     Set wshShell = CreateObject("WScript.Shell")
     iStyle = wshShell.RegRead("HKCU\Software\Microsoft\Office\16.0\Outlook\Preferences\" & strMode & "Style")
     If iStyle = 1000 Then
         strPrefix = wshShell.RegRead("HKCU\Software\Microsoft\Office\16.0\Outlook\Preferences\PrefixText")
         If strPrefix = "" Then
             strPrefix = "> "
         End If
     End If
     GetPrefixText = strPrefix
End Function

マクロの登録方法やメニューへの追加について

翌日の 00:00 に配信時刻を設定するマクロ

コメントにて以下のご要望をいただきました。


アウトルックで分からないことの出現時・マクロの作成で大変お世話になっています。
今回は「指定日時以降に配信」の、規定の時間の「17:00」を「00:00」に変更できないでしょうか、という質問です。
環境としては、Windows11 Office365のアウトルックを使っています。
よろしくお願いします。 <(_ _)>


残念ながら、[指定日時以降に配信] の既定の時間は勤務時間の終了時刻が設定されるようになっており、これを 00:00 にすることはできません。
そのため、配信時刻を設定してから [配信タイミング] を呼び出すマクロを作ってみました。
当日の 00:00 を配信時刻に設定するのは意味がないので、翌日の 00:00 に設定するようにしています。
金曜日に実行した際に土曜日の 00:00 ではなく月曜日の 00:00 にするロジックを入れていますが、不要であれば「金曜日だったら月曜の 00:00 にする」というコメントの下の 3 行を削除してください。
また、すでに配信時刻を設定しているときに誤ってマクロを実行して配信時刻を上書きしないようにするロジックも実装しています。

なお、配信時刻を 00:00 に設定する場合、その時間に Outlook が起動していなければメールは送信されません。(Exchange サーバーにキャッシュ モード無効で接続している場合を除く)
マクロは以下の通りです。

Public Sub SetDeferredDelivery()
     Dim objItem As MailItem
     Dim dtDeferred As Date
     Set objItem = ActiveInspector.CurrentItem
     ' 配信時刻が設定されていない場合のみ設定
     If objItem.DeferredDeliveryTime = #1/1/4501# Then
         ' 翌日の 00:00 に設定
         dtDeferred = DateAdd("d", 1, Now)
         ' 金曜日だったら月曜の 00:00 にする
         If Weekday(Now) = vbFriday Then
             dtDeferred = DateAdd("d", 3, Now)
         End If
         ' 配信時刻を設定
         objItem.DeferredDeliveryTime = FormatDateTime(dtDeferred, vbShortDate) & " 0:00"
     End If
     ' リボンの [配信タイミング] を実行
     ActiveInspector.CommandBars.ExecuteMso "DelayDeliveryOutlook"
End Sub

マクロの登録方法やメニューへの追加について

送信するメールにフラグと期限を設定するマクロ

本文の内容からフラグの期限を設定するマクロのコメントにて以下のご要望をいただきました。


こんにちは。初めてのコメント失礼します。
複数人に締切の有るメールをお送りすることがあり、
Excel VBAで個々のメールを一括で作成しています。
VBAでさらに、リマインド機能を付けたいのですが、
Outlookの仕様でポップアップ締切間近であることを表示させたりフラグを付けたりなど
できるのでしょうか?


Outlook のオブジェクト モデルでは以下のプロパティにより受信者側で動作するフラグなどが設定可能です。

FlagStatus – フラグの状態を設定
FlagRequest – フラグの文字列を設定
FlagDueBy – フラグの期限を設定
ReminderTime – アラームの時刻を設定

以下のマクロは上記のプロパティの設定例です。

    With objMail
         .FlagStatus = 2 ' olFlagMarked
         .FlagRequest = "返信してください!"
         .FlagDueBy = #2024/09/08#
         .ReminderTime = #2024/09/07 05:00:00 PM#
     End With

このように指定してメールを送信すると、受信者側ではメール本文の上に「返信してください! (期限: 2024年9月8日日曜日 0:00)」と表示され、2024年9月7日の午後5時にアラームが表示されます。

なお、この機能を使用するには、サーバーが Exchange Server であり、受信者も Outlook を使用している必要があります。

アイテムの種別にかかわらず To や Cc を取得するための関数

コメントにて以下のご要望をいただきました。


お世話になります。
毎日の仕事の振り返りのヒントとして、Outlookから発信したメール、開催通知等(以後メール等と表記)を記録に残しておきたいと思っています。
以前は「ジャーナル」機能があったのでそれを使っていたのですが、現在では自動的に記録する機能がありません。

そこで、メール等がSentOnになったらExcelのファイルに追記していくOutlookVBAマクロを作ってみました。
———————–8<———————–8<———————–
Option Explicit

Dim WithEvents mySentItems As Items ‘ ItemAdd イベントをハンドルするオブジェクト

‘ Outlook 起動時に実行されるイベント
Private Sub Application_Startup()
  Set mySentItems = Session.GetDefaultFolder(olFolderSentMail).Items ‘ 送信済みアイテム フォルダーへのアイテム追加をハンドルするためのオブジェクト設定
End Sub

‘ 送信済みアイテムに移ってきたメッセージの情報をExcelファイルに追記する処理
Private Sub mySentItems_ItemAdd(ByVal Item As Object)
  Dim excelApp As Object ‘ Excelオブジェクトの作成
  Set excelApp = CreateObject(“Excel.Application”)
  Dim workbook As Object ‘ Excelファイルを開く
  Set workbook = excelApp.Workbooks.Open(“C:Users********送信ログ.xlsx”)
  Dim sheet As Object ‘ 追記するシートを選択
  Set sheet = workbook.Sheets(“送信ログ”)
  Dim lastRow As Long lastRow = sheet.Cells(sheet.Rows.Count, 1).End(-4162).Row ‘ 最終行を取得 ‘ データを追記
  sheet.Cells(lastRow + 1, 1).Value = Format(Item.SentOn, “yyyy/mm/dd hh:mm:ss”)
  sheet.Cells(lastRow + 1, 2).Value = Item.To
  sheet.Cells(lastRow + 1, 3).Value = Item.subject
  workbook.Save ‘ Excelファイルを保存
  workbook.Close ‘ Excelファイルを閉じる
  Set sheet = Nothing ‘ Excelオブジェクトを解放
  Set workbook = Nothing
  Set excelApp = Nothing

End Sub
———————–8<———————–8<———————–
上記のマクロはメールを送信したときは問題なく機能しますが、会議開催通知などの場合は
Item.To
のところでエラーになります。
(送信済フォルダに入ってくるのはメールだけとは限らないからだと思いますが)

送信したメール等はすべて記録しておきたいので、どのように修正すればよいでしょうか。
よろしくご教授ください


To プロパティと Cc プロパティについては、MailItem オブジェクトでのみ使用できるものです。
そのため、会議出席依頼に相当する MeetingItem オブジェクトでこれらのプロパティを取得しようとするとエラーとなります。
To や Cc をサポートしないオブジェクトでも、Recipients プロパティからあて先情報が取得できるので、これをもとに To や Cc の文字列を生成することでご要望は満たせるでしょう。
以下の関数は引数 objItem で指定されたメールなどのアイテムから lType で指定された種類の受信者すべての表示名を取得するものです。

Private Function GetToCc(ByVal objItem As Object, lType As Long)
     Dim strNames As String
     Dim objRec As Recipient
     strNames = ""
     For Each objRec In objItem.Recipients
         If objRec.Type = lType Then
             strNames = strNames & objRec.Name & "; "
         End If
     Next
     ' 余計な最後の ; を削除
     If strNames <> "" Then
         strNames = Left(strNames, Len(strNames) - 2)
     End If
     GetToCc = strNames
End Function

例えば、

    sheet.Cells(lastRow + 1, 2).Value = Item.To

としている個所を、

    sheet.Cells(lastRow + 1, 2).Value = GetToCc(Item, olTo)

のようにすることで、メール以外のアイテムでもエラーを発生させることなくあて先情報が取得できます。

受信トレイの返信済みメールをアーカイブ フォルダーに移動するマクロ

未返信のメールを別のフォルダーに移動するマクロ のコメントにて以下のご要望をいただきました。


はじめまして。Outlookで相手のメールに返信した時に返信した元のメールを自動でアーカイブするマクロを作りたいのですが、同じように以下のプロパティをチェックすれば作成可能でしょうか?

PR_ICON_INDEX (メッセージ一覧で表示するアイコンの指定) が 261 (返信アイコン) に変わる

PR_LAST_VERB_EXECUTED (メッセージに対して最後に実行された処理) が 102 (差出人に返信) または 103 (全員に返信) に変わる

当方、送信済みメールも受信ボックスにコピーされるように設定しており、受信ボックスの中を「送信済みメールのコピー=返信待ちメールもしくは自分のメールで終わったメール」「受信メール(未返信)=要返信もしくは相手のメールで終わったメール」のみにするのが目的です。

自分が返信したメールを受信ボックスにコピーするので、返信元のメールはアーカイブに送りたいと考えております。ご助言いただけると助かります。


自分が返信したメールを確認するのであれば PR_ICON_INDEX などでも確認はできますが、これだけだと相手が返信してきたメールが判断できず、「送信済みメールのコピー=返信待ちメールもしくは自分のメールで終わったメール」が実現できないと思われます。
Outlook オブジェクト モデルでは、Conversation オブジェクトを使用してメールのスレッドのツリーを確認できるので、件名が RE: で始まるメールの親 (返信元) のメールをアーカイブに移動するという処理を行えば、ご要望は満たせると思います。
マクロは以下の様になります。

Public Sub ArchiveOldItemInThreads()
     Dim fldInbox As Folder
     Dim fldArchive As Folder
     Dim colItems As Items
     Dim objConv As Conversation
     Dim curItem As Object
     Dim prevItem As Object
     Dim i As Integer
     ' 受信トレイの取得
     Set fldInbox = Session.GetDefaultFolder(olFolderInbox)
     ' アーカイブ フォルダーの取得
     Set fldArchive = fldInbox.Parent.Folders("アーカイブ")
     ' 受信トレイのアイテムを受信日時の降順で並び替え
     Set colItems = fldInbox.Items
     colItems.Sort "ReceivedTime", True
     ' アイテムを一つずつチェック
     For i = fldInbox.Items.Count To 1 Step -1
         Set curItem = fldInbox.Items(i)
         ' 件名が RE: で始まる場合はスレッドのチェック
         If UCase(curItem.Subject) Like "RE:*" Then
             ' Conversation オブジェクトを取得
             Set objConv = curItem.GetConversation()
             ' Conversation より返信元のメールを取得
             Set prevItem = objConv.GetParent(curItem)
             ' 返信元メールが見つかったら
             If Not prevItem Is Nothing Then
                 ' 既にアーカイブ フォルダーに移動されていなければ
                 If prevItem.Parent <> fldArchive Then
                     ' アーカイブ フォルダーに移動
                     prevItem.Move fldArchive
                 End If
             End If
         End If
     Next
End Sub

マクロの登録方法やメニューへの追加について

返信を行うマクロで [返信/転送時に元のメッセージのウィンドウを閉じる] を実装する方法

コメントにて以下のご要望をいただきました。


「返信メッセージで表示名を連絡先のものに置き換えるマクロ」を
もう何年も便利に使わせて頂いている者ですが,このマクロに一つ要望があります.

このマクロを使ってメールの返信/転送を実行しますと,
「返信/転送時に元のメッセージのウィンドウを閉じる」が効かなくなります.

これを効くように改善出来ないでしょうか.
もし可能でしたら効くようにして頂きたいのです.


マクロで ReplyAll メソッドや Forward メソッドを実行した場合、Outlook は [返信/転送時に元のメッセージのウィンドウを閉じる] の設定を参照しません。
そのため、この設定がオンでも元のメッセージのウィンドウは開いたままになります。
これを回避するには、マクロ自体で明示的にウィンドウを閉じる必要があります。
具体的には ReplyAll や Forward の直後に Close で返信元のメールを閉じるということです。
例えば、

Set objReply = ActiveExplorer.CurrentItem.ReplyAll

として返信メールを作成している場合、その次の行に

ActiveExplorer.CurrentItem.Close

と記載することで元のメッセージを閉じることができます。

Outlook オブジェクト モデルによりメールを送信しようとしても、送信トレイに滞留してしまう現象について

RPA や Excel マクロなどで Outlook オブジェクト モデルを使用して MailItem の Send メソッドによりメールを送信した際に、メールが送信されずに送信トレイに滞留したままの状態となる場合があります。
これは以下のような条件で発生します。

  • メールを送信する際に Outlook が起動していない
  • インターネット アカウントを使用しているか、Exchange アカウントでキャッシュ モードを有効にしている
  • Send メソッドで送信した後、すぐに Outlook のオブジェクトを開放している
  • メールサイズが大きかったり、一度に複数のメールを送信するなど、送信処理に時間がかかる状況である

上記の条件でメールが送信されない場合があるのは、Outlook の送信処理が以下のような順序で行われるためです。

  1. Send メソッドが実行されると、メールが送信トレイに保存され、送信のためのバックグラウンド タスクが起動される
  2. バックグラウンド タスクにより送信が実行される
  3. 送信が完了すると送信トレイのメールが送信済みアイテムに移動される

上記の 1. の処理が完了すると、それ以降の処理が行われる前に呼び出し元に制御が戻ります。
そして、呼び出し元のアプリケーションなどが直後に Outlook のオブジェクトを開放すると、Outlook 上では誰も Outlook を利用していない状態になります。
この状態になると、Outlook はバックグラウンド タスクが完了次第終了するのですが、何らかの理由でメール送信のタスクが起動していなかったり、複数のタスクの合間で実行中のタスクがない状況になると送信前のメールがあっても Outlook は終了します。
その結果、マクロなどでの送信が完了しても実際にはメールが送信されず、次に Outlook が起動されたタイミングで送信が行われるということになります。
これを回避するには、Outlook でメールの送信が完了したことを確認するまで、Outlook のオブジェクトを参照し続ける必要があります。
メールの送信が完了したかどうか判断する方法としては、送信トレイのアイテム数が 0 になるのを待つというものが考えられるでしょう。
appOlk という変数に Outlook.Application オブジェクトが格納されている場合に送信完了を待つ Excel VBA のサンプル コードは以下の通りです。
なお、サーバーやネットワークの問題などにより送信処理がいつまでも完了しない場合を考慮し、60 秒待っても送信トレイのアイテム数が 0 にならなければエラー表示するようにしています。

    Dim fldOutbox As Object
    Dim dtStart As Date
    Dim bAbort As Boolean
    Set fldOutbox = appOlk.Session.GetDefaultFolder(4) ' 4 = olFolderOutbox
    dtStart = Now
    bAbort = False
    While fldOutbox.Items.Count > 0 And Not bAbort
        ' Send メソッド実行から 60 秒以上経過していたら待つのをやめる
        If DateDiff("s", dtStart, Now) > 60 Then
            bAbort = True
        End If
        ' 送信トレイにアイテムが残っていたら 5 秒待つ
        Application.Wait (Now + TimeValue("00:00:05"))
    Wend
    '
    If bAbort Then
        MsgBox "送信処理が完了しませんでした。Outlook を起動して送信トレイを確認してください。"
    End If

Temp\Diagnostics\Outlook に作成されるログを終了時に削除するマクロ

コメントにて以下のご要望をいただきました。


お世話になっております。

Outlook Logging フォルダーに ETL ファイルが作成されないようにする方法

を参考に

C:\Users\***\AppData\Local\Temp\Diagnostics\OUTLOOK

に生成されるテキストドキュメントを作成されない方法はございますか?

放置していたら、10Gb以上あったので・・・


環境変数 %TEMP% で示されるフォルダーの下の Diagnostics には Office 製品共通のコンポーネントによる動作履歴ログが保存されます。
こちらについて調査してみましたが、作成されないようにする方法は見つかりませんでした。

そのため、終了時にログを削除するようなマクロを作ってみました。
マクロ実行中に書き込みされていたログについては削除できない場合がありますが、古いログについては削除されます。
マクロは以下の通りです。

Private Sub Application_Quit()
     On Error Resume Next
     Dim strDiag As String
     Dim strLog As String
     ' ログ ファイルが保存されているフォルダーを取得
     strDiag = Environ("TEMP") & "\Diagnostics\OUTLOOK\"
     ' フォルダーのファイルを取得
     strLog = Dir(strDiag)
     ' ファイルの列挙が終わるまで繰り返し
     While strLog <> ""
         ' ファイルを削除
         Kill strDiag & "\" & strLog
         ' 次のファイルを取得
         strLog = Dir()
     Wend
End Sub

マクロの登録方法やメニューへの追加について