Autodiscover の詳細動作

Outlook には Autodiscover という機能があります。
この機能により、メールアドレスとパスワードだけでメール アカウントの構成に必要なサーバーやポート番号などの情報を取得して設定することができます。
Autodiscover では、最終的にサーバーなどの構成情報を含むデータを XML ファイルとして取得するのですが、Outlook はインターネットで一般的に使われる POP や IMAP といったサーバーだけでなく、Microsoft 独自のプロトコルで接続する Exchange サーバーにも対応するため、XML の取得をいくつかの方法で試みる動作になっています。
そのため、環境によっては情報の取得に時間がかかったり、想定外の警告が表示されるということがあります。
 
Microsoft サポート技術情報の「Outlook 2016 の Autodiscover の実装」によると、以下の順序で Autodiscover が実行されます。

  1. 再起動のシナリオを確認する
  2. ローカル データの環境設定を確認する
  3. 前回の既知の有効 (LKG) データを確認する
  4. O365 を優先的に確認する
  5. SCP データを確認する
  6. ルート ドメインを確認する
  7. Autodiscover ドメイン
  8. ローカル データを確認する
  9. HTTP リダイレクトを確認する
  10. SRV データを確認する
  11. O365 のフェールセーフを確認する

前述のサポート技術情報では上記の概要が説明されていますが、具体的にどのような処理が行われるかや、どのようなシナリオで必要なのかなど、トラブルシュートに必要な詳細情報がありません。
そこで、今回の記事では、Autodiscover の詳細について上記の順で説明したいと思います。

手順 0: ローカルにキャッシュされた XML ファイルを確認する

いきなり上記の手順にない処理からスタートしましたが、この手順はすでにプロファイルが作成済みの場合の起動時のみ実行されます。
この手順以外で Autodiscover が成功すると、その際にサーバーから取得した XML ファイルが以下の通り保存されます。
 
フォルダー: C:\Users\ユーザー名\AppData\Local\Microsoft\Outlook\16
ファイル名: AutoD.メールアドレス.xml
 
そして、2 回目以降の起動ではこのファイルを読み込んでサーバーに接続するため、素早く起動できるというメリットがあります。
言い換えると、このファイルが何らかの理由で削除されると、Outlook 起動時に上記の手順で Autodiscover が実行される動作となるので、環境によっては起動に時間がかかるということになります。
 
特に RDS や VDI 環境で移動ユーザー プロファイルを使用している場合、通常は上記のファイルは移動対象ではなく、ログオフ時に削除されるため、このような環境でログオン直後の初回の Outlook の起動にだけ時間がかかるという現象の原因は上記のファイルが削除されることにあると考えられます。
 
また、サーバーの移動や、オンプレミスの Exchange サーバーから Exchange Online に移動した後のサーバー切り替えに失敗するという場合、上記のファイルに移動前の古い情報が残っており、その情報での接続で失敗までに時間がかかりすぎている可能性があります。
そのような場合は、切り替え後に一度上記のファイルを削除することで回避するかもしれません。

手順 1: 再起動のシナリオを確認する

この手順は技術情報にもある通り、Outlook の起動中に Exchange アカウントを追加し、再起動した際に実行されるシナリオです。
具体的には、Outlook の起動中に Exchange アカウントを追加すると、Autodiscover が実行されますが、取得された XML ファイルの内容が以下の通り保存されます。
 
フォルダー: C:\Users\ユーザー名\AppData\Local\Microsoft\Outlook
ファイル名: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}.txt
※ X はランダムな 16 進数
 
また、以下のレジストリも設定されます。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\プロファイル名\AutoDisc
名前: 追加したアカウントのメールアドレス
種類: REG_BINARY
値: UNICODE で上記のファイルのフルパスを含むバイナリデータ
 
そして、次回起動時に Outlook がプロファイルのレジストリに上記のデータが含まれていることを確認すると、データに格納されているパスのファイルから Autodiscover の設定を読み込み、サーバーの構成を行います。
通常起こることではありませんが、もしアカウントを追加してから Outlook を終了し、次に起動するまでに上記のファイルが削除されたような場合、次回起動時のアカウント追加処理が失敗し、アカウントの追加は行われません。 

手順 2: ローカル データの環境設定を確認する

この手順は以下のレジストリ設定がなければ実行されません。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: PreferLocalXML
種類: REG_DWORD
値: 1
 
この設定は手順 8 の「ローカル データを確認する」の処理を他よりも前に実行させることを目的としています。
何らかの理由で手順 3 から手順 7 をレジストリ設定でスキップさせずに手順 8 を実行させたい場合にこの設定を行います。
例えば、特定のドメインだけローカル XML で構成し、ほかのドメインは通常の Autodiscover で構成するというような場合などが考えられます。
 
ローカル データの確認の詳細については、手順 8 で説明します。 

手順 3: 前回の既知の有効 (LKG) データを確認する

この手順はプロファイルが作成されている状況でのみ実行され、アカウント作成時には実行されません。
正常にプロファイルが作成されてアカウントが追加されると、以下のレジストリに Autodiscover の XML ファイルを取得した URL が Unicode のバイナリで格納されます。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\プロファイル名\アカウントごとのランダムな 16 進数
名前: 001f664a
種類: REG_BINARY
 
そして、次回以降の Autodiscover は試行錯誤せずに前回の既知の有効な (Last Known Good) URL にアクセスすることで、時間が短縮できるというものです。
サーバーの移行などにより LKG URL が無効となってしまった状況で、この URL へのアクセスが問題になるようであれば以下のレジストリ設定でこの動作を無効にすることができます。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: ExcludeLastKnownGoodURL
種類: REG_DWORD
値: 1
 
ただし、この設定を行うと、後述の SCP による Autodiscover のために GC へのアクセスが発生して GC の負荷が増大したり、Autodiscover に時間がかかるようになる可能性があります。 

手順 4: O365 を優先的に確認する

こちらは、Outlook for Office 365 ProPlus において、特に設定を行っていない状況で最初に行われる Autodiscover の手順です。

技術情報では「Outlook は一連のヒューリスティックを使用して、指定されたユーザー アカウントが Office 365 から提供されたかどうかを判断します」とあるのですが、実際に判断しているのは実は Outlook ではありません。
まず、Outlook は以下の URL で GET リクエストを行います。
 
https://outlook.office365.com/autodiscover/autodiscover.json/v1.0/メールアドレス?Protocol=Autodiscoverv1
 
そうすると、これを受け取った O365 のサーバーがメールアドレスに応じて正しいと思われる Autodiscover URL へのリダイレクトを行います。
つまり、判定を行っているのは O365 のサーバーになります。
ただし、厳密にメールアドレスのチェックを行っているわけではなく、O365 に登録されているドメインであれば O365 の Exchange サーバーの URL に、そうでなければ https://autodiscover.メールドメイン/autodiscover/autodiscover.xml にリダイレクトされるという動作のようです。
そして、O365 のアカウントであれば最終的に https://outlook.office365.com/autodiscover/autodiscover.xml で構成情報の取得が成功します。
 
O365 にメールボックスがなく、outlook.office365.com というサーバーにアクセスができない環境で失敗までに時間がかかるような場合には、以下のレジストリ設定でこの動作を無効にすることができます。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: ExcludeExplicitO365Endpoint
種類: REG_DWORD
値: 1 

手順 5: SCP データを確認する

この手順は、ドメインに参加しているクライアントでのみ実行される手順です。
Exchange サーバーがインストールされると、Active Directory の構成名前付きコンテキストに Autodiscover の URL が書き込まれます。
具体的には以下の DN のオブジェクトの serviceConnectionPoint プロパティに格納されます。
 
CN=サーバー名,CN=Autodiscover,CN=Protocols,CN=サーバー名,CN=Servers,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=組織名,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=組織固有の DC
 
Outlook は PC が Active Directory ドメインに参加していることを認識すると、LDAP によりドメインの GC に対して上記のオブジェクトを Keyword の値で検索し、見つかったオブジェクトの serviceConnectionPoint プロパティに格納されている URL に対して HTTP のリクエストを行って自動構成の XML を取得します。
 
既定では、Exchange サーバーが異なるフォレストにインストールされている場合には上記の情報がクライアントの AD 上に存在しないため SCP による構成は失敗します。
しかし、Exchange サーバーの Export-AutoDiscoverConfig コマンドレットでクライアントのフォレストに SCP の情報をエクスポートすると、そのフォレストで SCP による Autodiscover ができるようになります。
この場合、クライアントの AD では以下の DN のオブジェクトが生成されます。
 
CN=Exchange のドメイン名,CN=Microsoft Exchange Autodiscover,CN=Services,CN=Configuration,DC=組織固有の DC
 
そして、このオブジェクトの serviceConnectionPoint プロパティの serviceConnectionPoint プロパティの値を取得するのですが、ここに格納されているのは Autodiscover の URL 自体ではなく、LDAP://<ドメイン名> という LDAP の URL となります。
そこで、Outlook は指定された LDAP URL に対して改めて SCP 取得のための検索を行い、見つかったオブジェクトの serviceConnectionPoint プロパティから Autodiscover の URL を取得するという処理を行います。
そのため、別フォレストの Exchange への Autodiscover を SCP で成功させるには以下のような要件が必要になります。

  • DNS により Exchange のドメインの LDAP の SRV レコードが取得できる
  • Exchange のドメインの GC の LDAP (TCP ポート 389) に接続ができる
  • Exchange のドメインの GC に対して Windows にログオンしたユーザーの資格情報で認証が通る

クライアントが所属するフォレストに Exchange サーバーが存在せず、GC への不要な LDAP アクセスを抑止したいような場合には、以下のレジストリ設定でこの動作を無効にすることができます。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: ExcludeScpLookup
種類: REG_DWORD
値: 1

手順 6: ルート ドメインを確認する

この手順では、以下のような URL での Autodiscover を実行します。
 
https://<メールドメイン>/Autodiscover/Autodiscover.xml
 
例えば、user@example.com というメールアドレスの Autodiscover の場合、https://example.com/Autodiscover/Autodiscover.xml という URL で構成情報の取得を行います。
これが成功するにはドメイン名で解決される IP アドレスでサーバーが稼働しており、そのサーバーのサーバー証明書の名前にドメイン名が設定されている必要があります。
インターネット プロバイダーなどで POP/IMAP アカウントを Autodiscover で構成する場合などに使用することが想定されていると思われます。
 
なお、Active Directory 環境では、ドメイン名で名前解決するとドメイン コントローラーの IP が返され、https://ドメイン名/ でドメイン コントローラーへのアクセスが発生する場合があります。
また、DNS の管理を外部に委託している場合に、ドメイン名でのアクセスが委託先のサーバーへのリクエストになり、想定外の認証要求や証明書エラー、接続遅延などの原因になることもあります。
例えば、Outlook の起動時に認証ダイアログが表示され、何を入力しても通らないものの、キャンセルしても特に問題ないというような場合、ルート ドメインの Autodiscover が原因の可能性があります。
そのため、Exchange アカウントを作る前提の場合は、以下のレジストリ設定でこの動作を無効にすることをお勧めします。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: ExcludeHttpsRootDomain
種類: REG_DWORD
値: 1 

手順 7: Autodiscover ドメイン

この手順では、以下のような URL での Autodiscover を実行します。
 
https://autodiscover.メールドメイン/Autodiscover/Autodiscover.xml
 
例えば、user@example.com というメールアドレスの Autodiscover の場合、https://autodiscover.example.com/Autodiscover/Autodiscover.xml という URL で構成情報の取得を行います。
オンプレミス環境で社外から接続するというような場合や、ワークグループもしくは Exchange サーバーとは別のフォレスト環境から接続する場合には、SCP での取得ができないため、この手順で Autodiscover を実行するのが一般的です。
この手順が実施されるときのポイントとしては、autodiscover.メールドメイン という名前で解決されるサーバーのサーバー証明書の名前に autodiscover.メールドメイン が含まれていなければならないという点があります。
通常、Exchange サーバーを構築する際にはサーバー自体に autodiscover という名前を付けることはほとんどないと思われるので、サーバー証明書にも実サーバーの名前を設定し、DNS の CNAME で autodiscover として実サーバーに接続するよう構成することになります。
しかし、サーバー証明書に autodiscover が別名で登録されていない場合には、Outlook が https://autodiscover.メールドメイン としてアクセスした際に、想定したサーバー名と異なる証明書であるため、警告が表示される結果となります。
これを回避するには、Exchange に登録するサーバー証明書に以下のいずれかの対応を行う必要があります。

  • 別名として autodiscover.メールドメイン を登録する
  • ワイルドカード証明書を使用する (名前を *.メールドメイン とする)

なお、O365 環境では autodiscover.メールドメイン というサーバーは CNAME で autodiscover.outlook.com に接続されるように構成されることがあります、このサーバーでは TCP ポート 443 での接続は遮断されるので、この手順は失敗するのが想定される動作です。
 
autodiscover という名前で CNAME の登録をしていないような場合には、以下のレジストリ設定でこの動作を無効にすることができます。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: ExcludeHttpsAutodiscoverDomain
種類: REG_DWORD
値: 1 

手順 8: ローカル データを確認する

この手順はあらかじめ Autodiscover の XML ファイルを作成してクライアントに配布しておき、それをもとに自動構成を行うというものです。
この手順を行うには、ローカル ファイルに XML ファイルを配置し、以下のレジストリに保存した XML ファイルのパスを記述する必要があります。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: メールドメイン
種類: REG_SZ
値: XML ファイルのフル パス名
 
Outlook 2010 までは既定でいくつかのプロバイダの XML ファイルがインストールされていたのですが、Outlook 2013 以降はインストールされなくなったようです。
ローカルの XML ファイルだとプロバイダ側での構成変更に対応できなかったからかもしれません。
 
保存する XML ファイルには以下の 2 つのパターンがあります。 

  • XML に構成情報を記載する
  • XML にリダイレクト URL を記載する

前者は、ユーザーごとに固有の情報がメールアドレスやログオン情報のみの場合に有効な方法です。
Outlook 2013 まではこの方法で Exchange アカウントの構成もできていたのですが、Outlook 2016 以降ではこの方法での Exchange アカウントの構成はできなくなりました。
POP/IMAP などの環境であれば Outlook 2016 以降でも構成は可能であり、以下の URL のサンプルを参考にしてXML ファイルを作成することで社内の POP/IMAP サーバーの構成を自動化することもできます。
https://docs.microsoft.com/ja-jp/previous-versions/office/office-2010/cc511507(v=office.14)?redirectedfrom=MSDN
 
後者は、XML ファイル自体には構成情報が含まれておらず、実際に構成情報を取得するための URL が記載されているというものです。
例えば、O365 に接続するための Autodiscover の URL を XML ファイルに記述する場合は以下のようなものとなります。
 
<?xml version=”1.0″ encoding=”utf-8″ ?>
<Autodiscover xmlns=”http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006″>
<Response xmlns=”http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a”>
<Account>
<AccountType>email</AccountType>
<Action>redirectUrl</Action>
<RedirectUrl>https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml</RedirectUrl>
</Account>
</Response>
</Autodiscover>
 
この方法は、O365 に接続したいものの、DNS の構成に手が出せず、後述の CNAME レコードも SRV レコードも設定できないというような場合に使われるようです。
 
なお、メールドメインのレジストリ設定がなければ実行されないものであるため、この手順を無効にするレジストリ設定はありません。 

手順 9: HTTP リダイレクトを確認する

この手順では、まず以下のような URL にアクセスします。
 
http://autodiscover.メールドメイン/Autodiscover/Autodiscover.xml
 
例えば、user@example.com というメールアドレスの Autodiscover の場合、http://autodiscover.example.com/Autodiscover/Autodiscover.xml という URL にアクセスするのですが、この URL で構成情報の取得は行われません。
その代わり、サーバーから HTTP の 302 という応答とともにリダイレクト先の URL が返され、その URL から構成情報を取得するという動作になります。
このような手順が必要となる理由としては、サーバー証明書のサーバー名が特定できない環境があるためです。
具体的にはパブリック クラウド環境や多数のメールドメインを持つような大企業などでは、たとえワイルドカード証明書を使ったとしてもサーバー証明書に追加しなければならない名前が多すぎたり、証明書の書き換えが頻繁に発生する状況となります。
こうした環境で https://autodiscover.ルートドメイン で証明書の警告を出さないようにすることは難しいため、証明書のチェックが不要な http://autodiscover.ルートドメイン にアクセスを行い、そこから単一の URL にリクエストを転送することで、サーバー証明書の問題が回避させるというのがこの手順の目的です。
 
ただし、http によるリクエストの場合、そのサーバーの信頼性が確認できず、何らかの方法で不正なサーバーにリダイレクトされる可能性もあります。
そのため、リダイレクトの際に「この Web サイトで メールアドレス のサーバー設定を構成できるようにしますか?」というような警告メッセージが表示されます。
このダイアログを最初から表示させたくない場合には、以下のレジストリ設定を行う必要があります。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover\RedirectServers
名前: リダイレクト先サーバーの FQDN
種類: REG_SZ
値: 空の文字列
 
DNS に autodiscover という名前で CNAME の登録をしていないような場合には、以下のレジストリ設定でこの動作を無効にすることができます。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: ExcludeHttpRedirect
種類: REG_DWORD
値: 1 

手順 10: SRV データを確認する

この手順は DNS の “メールドメイン” の SRV レコードに以下のような値を設定しておくことで、指定されたサーバーから構成情報を取得するというものです。
 
サービス: _autodiscover
プロトコル: _tcp
ポート番号: 443
ホスト: Autodiscover の取得先となるサーバーの FQDN
 
こちらも、手順 9 と同様に多数のメールドメインを扱う環境で使用され、サーバーにアクセスする際にリダイレクトの警告が表示されます。
警告を表示しないようにする設定も手順 9 と同じです。
 
DNS に Autodiscover の SRV レコードの登録をしていないような場合には、以下のレジストリ設定でこの動作を無効にすることができます。
 
キー: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover
名前: ExcludeSrvRecord
種類: REG_DWORD
値: 1 

手順 11: O365 のフェールセーフを確認する

最後の手順は、アカウント作成時には行われません。
既存のアカウントが O365 のものであることがわかっている状況で、何故か Autodiscover に失敗してしまったという場合に、 O365 の既知の Autodiscover URL から構成情報を取得するというものです。
この動作は手順 4  のレジストリ設定で O365 の優先構成を無効にすると、自動的に無効化されます。

まとめ

上記のように Outlook は様々な方法で Autodiscover を実行しますが、基本的にはどれか 1 つ成功すればよいものであるため、不要な手順は ExcludeXXX レジストリにより無効化しておくと時間短縮や想定外の動作の抑制になるでしょう。
無効化するためのレジストリのキーとしては HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Autodiscover と記載しましたが、グループ ポリシーで展開する場合には HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\16.0\Outlook\Autodiscover というキーで展開することになります。
 
「説明が長すぎて結局どの手順で成功するのかわからない」という方もいると思うので、来週は一般的な環境でどの Autodiscover の手順により構成されるかの解説をします。

特定のフォルダーのメールの添付ファイルを日付と送信者のフォルダーに保存するマクロ

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


コメント

いつも大変参考にさせていただいております

受信したメールの添付ファイルを自動作成した日付フォルダ/送信者フォルダへ保存&添付ファイルリストをExcel形式で出力同じフォルダ内の保存格納 するスクリプトを書いているのですがなにぶん勉強不足でうまくいきません。

加筆修正お願いできませんでしょうか。。日付フォルダを自動作成まではネットで調べてできたのですが、、、

よろしくお願いいたします。

<コード省略>


コメントでいただいたコードでほとんど完成していたのですが、以下の処理を追加しました。

  • 日付のフォルダーの下に送信者名のフォルダーを作成する。この時、送信者名にフォルダー名では使えない文字 (/、: など) が含まれていたら _ に置換する。
  • 同名のファイルが既に保存されていたら、ファイル名に連番をつけて別名で保存する。
  • Excel ファイルを保存後にクローズする。

追加後のマクロは以下のようになります。

' ここをトリプルクリックでマクロ全体を選択できます。

Public Sub SaveAttachments4()
     Const ROOT_PATH = "C:\Users\username\Documents\outlook_temp"
     Dim objInbox As Object
     Dim objFolder As Object
     Dim strPath As String
     Dim i As Long
     '日付用定義
     Dim strDay As String
     'フォルダ名をyyyymmdd形式で入力
     strDay = Format(Date, "yyyymmdd")
     strDay = strDay & "\"
     'Excel用定義
     Dim myExcel 'As Excel.Application
     Dim objBook 'As Excel.Workbook
     Dim objSheet 'As Excel.worksheet
     Dim n As Long
     'Excelオブジェクト生成、ブックの追加
     Set myExcel = CreateObject("Excel.Application")
     Set objBook = myExcel.Workbooks.Add()
     Set objSheet = objBook.sheets(1)
     '項目目を追加
     objSheet.Cells(1, 1) = "ID"
     objSheet.Cells(1, 2) = "件名"
     objSheet.Cells(1, 3) = "送信者"
     objSheet.Cells(1, 4) = "受信日時"
     objSheet.Cells(1, 5) = "添付ファイル"
     objSheet.Cells(1, 6) = "添付ファイルのパス"
     '添付ファイルリストを書き込む行の位置
     n = 2
     Set objInbox = GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
     '添付ファイルがあるメールのフォルダを指定します。2階層以上ある場合は「.Folders.Item(<フォルダ名>)」を追加してください。
     Set objFolder = objInbox.Folders.Item("1.サブフォルダ").Folders.Item("1-1.サブフォルダ")
     '添付ファイルの保存先をパスで指定※日付フォルダ追加
     strPath = ROOT_PATH & "\" & strDay
     '日付フォルダがなければ作成
     If Dir(strPath, vbDirectory) = "" Then
         MkDir strPath
     End If
     For Each objItem In objFolder.Items
         Dim strSubPath As String
         Dim strFileName As String
         ' 送信者名をパスに追加 (フォルダに使用できない文字は _ に置換)
         strSubPath = strPath & ReplaceSpecialChar(objItem.SenderName) & "\"
         For i = 1 To objItem.Attachments.Count
             '添付ファイルに拡張子がある場合のみ処理
             If InStr(objItem.Attachments.Item(i), ".") > 0 Then
                 ' 差出人名のフォルダがなければ作成
                 If Dir(strSubPath, vbDirectory) = "" Then
                     MkDir strSubPath
                 End If
                 ' すでに同名のファイルが存在したら連番を付与
                 strFileName = MakeFileName(strSubPath, objItem.Attachments.Item(i).FileName)
                 ' 添付ファイルを保存
                 objItem.Attachments.Item(i).SaveAsFile strSubPath & strFileName
                 'Excelへ添付ファイル情報を追加
                 objSheet.Cells(n, 1) = n - 1
                 objSheet.Cells(n, 2) = objItem.ConversationTopic '件名
                 objSheet.Cells(n, 3) = objItem.SenderName '送信者
                 objSheet.Cells(n, 4) = objItem.ReceivedTime '受信日時
                 objSheet.Cells(n, 5) = objItem.Attachments.Item(i) '添付ファイル
                 objSheet.Cells(n, 6) = strSubPath & strFileName '添付ファイルのパス”
                 n = n + 1
             End If
         Next i
     Next objItem
     '添付ファイル保存場所へExcelを保存
     objBook.SaveAs strPath & "添付リスト.xlsx"
     objBook.Close
     Set objItem = Nothing
     Set objInbox = Nothing
     Set objFolder = Nothing
     Set objSheet = Nothing
End Sub
'
' フォルダ名に使用できない文字を _ に置き換える関数
Private Function ReplaceSpecialChar(strText As String) As String
     ReplaceSpecialChar = ""
     For i = 1 To Len(strText)
         ch = Mid(strSubject, i, 1)
         If InStr("\/:*?""|", ch) > 0 Then
             ch = "_"
         End If
         ReplaceSpecialChar = ReplaceSpecialChar & ch
     Next
End Function
'
' ファイル名が重複した場合に連番を付与する関数
Private Function MakeFileName(strFolder As String, strOrgFileName As String)
     Dim strFileName As String
     Dim strBase As String
     Dim strExt As String
     Dim c As Integer
     strBase = Left(strOrgFileName, InStr(strOrgFileName, ".") - 1)
     strExt = Mid(strOrgFileName, InStr(strOrgFileName, "."))
     strFileName = strOrgFileName
     c = 1
     '
     While Dir(strFolder & strFileName) <> ""
         strFileName = strBase & c & strExt
         c = c + 1
     Wend
     MakeFileName = strFileName
End Function

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

Outlook 2016 の累積的な修正プログラム 2020 年 1 月分がリリース

1/7 に Outlook 2016 の累積的な修正プログラムがリリースされました。 以下は各製品のそれぞれの KB へのリンクです。

Office 2016

Outlook 2016 の修正

2020 年 1 月 7 日、Outlook 2016 (KB4484212) の更新プログラム 5 件の修正が行われています。

Word 2016 の修正

Word 2016 (KB4484219) の 2020 年 1 月 7 日更新 2 件の Outlook に関する修正が行われています。

13周年

2007 年にこのブログをはじめて、13 周年になりました。

一昨年は年間 100 万アクセスを達成できなかったのですが、昨年は 111 万を超えるアクセスとなりました。
残念ながら「Outlook マクロ」での検索ではトップの座を譲ってしまいましたが、「Outlook VBA マクロ」ではまだまだトップで表示されており、これもひとえにコメントで様々なご要望をお寄せくださる読者の方々のおかげと感謝しております。
本当にありがとうございます。

これからも、Outlook を活用してもらうべく、様々なマクロや Tips を紹介してまいりますので、よろしくお願いいたします。

指定した日付の決まった時間で定型の会議出席依頼を作成するマクロ

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


失礼致します。
有休休暇の会議案内(日にち:任意、時間AM6:00~AM6:30、宛先メンバー:固定)
をVBAでやりたいのですがどのようなマクロになりますでしょうか。
ご教授お願い致します。


日にちが任意とのことなので、日付を指定するとその日の決まった時間で会議出席依頼を作成するというマクロを想定しました。
日付の指定方法としてはダイアログで入力するというものと、予定表で選択するものが考えられたので、両方を実装してみました。
ダイアログで入力するには InputBox 関数を使用します。
予定表で選択された日付を取得するには ActiveExplorer の CurrentView の SelectedStartTime を使用します。
作成した予定を確認してから送信できるように、最後は AppointmentItem オブジェクトの Display メソッドでアイテムの表示を行っていますが、確認せずにすぐに送信したいのであれば Display メソッドの代わりに Send メソッドで送信するよう変更が必要です。
マクロは以下のようになります。
ダイアログで日付を入力する場合は CreateFixedMeetingByInputBox を、予定表で選択した日付で作成する場合は CreateFixedMeetingBySelect を実行してください。

' ここをトリプルクリックでマクロ全体を選択できます。

' ダイアログから入力した日付に会議を作成するマクロ
Public Sub CreateFixedMeetingByInputBox()
     Dim strDate As String
     ' ダイアログで日付を取得
     strDate = InputBox("日付:")
     ' 取得した日付を指定して会議を作成
     CreateFixedMeeting strDate
End Sub
' 予定表で指定した日に会議を作成するマクロ
Public Sub CreateFixedMeetingBySelect()
     Dim calView As CalendarView
     Dim strDate As String
     ' 表示中のビューの種類が [日/週/月] の場合のみ実行
     If TypeName(ActiveExplorer.CurrentView) = "CalendarView" Then
         Set calView = ActiveExplorer.CurrentView
         ' ビューで選択している開始範囲の日付のみ取得
         strDate = FormatDateTime(calView.SelectedStartTime, vbShortDate)
         ' 取得した日付を指定して会議を作成
         CreateFixedMeeting strDate
     End If
End Sub
' パラメータで指定した日の特定の時間に会議を作成するサブ プロシージャ
Private Sub CreateFixedMeeting(strDate As String)
     ' 開始時刻を指定
     Const START_TIME = "6:00"
     ' 終了時刻を指定
     Const END_TIME = "6:30"
     ' 出席者を指定
     Const MEET_ATTENDEES = "user1@example.com;user2@example.com"
     ' 会議開催通知の件名を指定
     Const MEET_SUBJECT = "有給休暇"
     ' 会議開催通知の本文を指定
     Const MEET_BODY = "有給休暇の会議を行います"
     Dim apptMeet As AppointmentItem
     ' 新規予定を作成
     Set apptMeet = CreateItem(olAppointmentItem)
     With apptMeet
         ' 件名を設定
         .Subject = MEET_SUBJECT
         ' 本文を設定
         .Body = MEET_BODY
         ' 出席者を設定
         .RequiredAttendees = MEET_ATTENDEES
         ' 開始日時を設定
         .Start = strDate & " " & START_TIME
         ' 終了日時を設定
         .End = strDate & " " & END_TIME
         ' 予定を会議に変更
         .MeetingStatus = olMeeting
         ' 作成した会議を確認して送信したい場合は以下を使用
         .Display
         ' 作成した会議を直ちに送信する場合は以下を使用
         '.Send
     End With
End Sub

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

指定した日付以降に更新された送受信メールや連絡先を PST にエクスポート/インポートするマクロ

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


いつも参考にさせて頂き、
要望にも対応頂きありがとうございます

Outlook365
Windows10(64bit)

2台の端末で1つのアカウントでログイン(設定)し
1週間の内、端末Aと端末Bを使用します。

例えば、月曜に端末Aで送受信等ををし、
残りの火曜~金曜は端末Bで送受信等とする場合

送受信メール、連絡先(追加などした場合)の
各データを同期させたいのです。
過去の相当古いデータも残しておきたいため
IMAP等では無理だと判断しています。

単純にデータ(pst)のエクスポートとインポートを
日時指定(作成日時、更新日時)で対応しようと考えています。
マクロ作成可能でしょうか?

日時指定(作成日時、更新日時)はダイアログで指定ができると有難いです。

メールの受信トレイ(階層1として)や送信トレイの下層に
サブフォルダを階層3迄で作成しています。
送受信後、返信や解答があったものは手動で移動しています。

サブフォルダを追加した場合もそのフォルダ等も
エクスポートとインポートの対象になりますよね?

よろしくお願いします。

—-

お返事ありがとうございます。

>Outlook のインポート、エクスポートの機能をマクロで呼び出すことはできないため、マクロ
>ですべて実装する必要があります。
≫≫
以前CSVのエクスポートするマクロを参考にさせて頂きましたが
単純にデータ(pst)のエクスポートとインポートを
日時指定(作成日時、更新日時)で対応はできないでしょうか?

>インポート先に同じアイテムがあった場合に単純な上書きとするのかや、
>そもそも同じアイテムと判断する基準はどうするかなどを考慮する必要があります。
≫≫
手動でデータ(pst)のエクスポートとインポートの時の
ダイアログの条件(下記3種)
・重複した場合、インポートするアイテムと置き換える(E)
・重複してもインポートする(A)
・重複したらインポートしない(D)

このうちの
・重複してもインポートする(A)で良いと考えています。

Outlook 365 を使われているというのは、Office 365 の Outlook を使われているということなのでしょうか?
≫≫
はいそうです。
MicrosoftR OutlookR for Office 365 MSO (16.0.12228.20100) 32 ビット  です。

その場合、サーバーは Exchange を使用しているはずで、連絡先などの情報もメールボックスに保存されているので、PST で同期する必要はないはずです。
≫≫
アプリ自体はOffice 365 の Outlookですが
エクスポートしたいアカウント(主にメール)の種類は
POP/SMTP(送信で使用する既定のアカウント)となっています。
もうひとつ 予定表の管理用として
利用している@outlook.comのアカウントは 種類はMicrosoft Exchangeとなっています。


アイテムのエクスポートやインポートの際に「重複してもインポートする」で構わないということであれば、単純にフォルダーのアイテムをコピーするというようなマクロとなります。
ただし、サブ フォルダーもコピーするとなると、エクスポート先にそのフォルダーがないという可能性もあるため、フォルダーがなければ作成するというロジックが必要になります。
また、日付の指定については InputBox で入力を行い、その日付でフィルターを作成して Items コレクションの Restrict メソッドにより日付の絞り込みを行います。
マクロは以下の通りになります。
なお、エクスポート、インポートする PST ファイルはあらかじめプロファイルに追加して置き、その名前を GetPSTRoot 内の PST_NAME で指定してください。

' ここをトリプルクリックでマクロ全体を選択できます。
'
' PST にエクスポートするプロシージャ
Public Sub ExportToPST()
     Dim fldSrc As Folder
     Dim fldDst As Folder
     Dim strFilter As String
     ' コピー元はメールボックス
     Set fldSrc = Session.DefaultStore.GetRootFolder
     ' コピー先は PST
     Set fldDst = GetPSTRoot()
     If fldDst Is Nothing Then Exit Sub
     ' フィルターを初期化
     strFilter = ""
     ' コピー処理をフォルダーごとに呼び出し
     CopyItems fldSrc, fldDst, "受信トレイ", strFilter
     CopyItems fldSrc, fldDst, "送信トレイ", strFilter
     CopyItems fldSrc, fldDst, "送信済みアイテム", strFilter
     CopyItems fldSrc, fldDst, "下書き", strFilter
     CopyItems fldSrc, fldDst, "連絡先", strFilter
End Sub
'
' PST からインポートするプロシージャ
Public Sub ImportFromPST()
     Dim fldSrc As Folder
     Dim fldDst As Folder
     Dim strFilter As String
     ' コピー元は PST
     Set fldSrc = GetPSTRoot()
     ' コピー先はメールボックス
     Set fldDst = Session.DefaultStore.GetRootFolder
     If fldDst Is Nothing Then Exit Sub
     ' フィルターを初期化
     strFilter = ""
     ' コピー処理をフォルダーごとに呼び出し
     CopyItems fldSrc, fldDst, "受信トレイ", strFilter
     CopyItems fldSrc, fldDst, "送信トレイ", strFilter
     CopyItems fldSrc, fldDst, "送信済みアイテム", strFilter
     CopyItems fldSrc, fldDst, "下書き", strFilter
     CopyItems fldSrc, fldDst, "連絡先", strFilter
End Sub
'
' PST のルートフォルダーを取得する関数
Private Function GetPSTRoot() As Folder
     Const PST_NAME = "個人用 Outlook データ ファイル"
     Dim fldRoot As Folder
     ' プロファイル
     For Each fldRoot In Session.Folders
         If fldRoot.Name = PST_NAME Then
             Set GetPSTRoot = fldRoot
             Exit Function
         End If
     Next
     MsgBox PST_NAME & "が見つかりません。", vbCritical
     Set GetPSTRoot = Nothing
End Function
'
' フォルダごとにアイテムをコピーするプロシージャ
Private Sub CopyItems(fldSrcRoot As Folder, fldDstRoot As Folder, strName As String, strFilter As String)
     On Error Resume Next
     Const PR_ATTR_HIDDEN = "http:" & "//schemas.microsoft.com/mapi/proptag/0x10F4000B"
     Dim fldSrc As Folder
     Dim dfType As OlDefaultFolders
     Dim fldDst As Folder
     Dim colItems As Items
     Dim objItem As Object
     Dim objCopy As Object
     Dim fldSub As Folder
     ' フィルターが設定されていなければ基準日を入力してフィルターを作成
     If strFilter = "" Then
         Dim strDate As String
         strDate = FormatDateTime(CDate(InputBox("基準日")), vbShortDate)
         ' 更新日時が基準日以降であるアイテムを取得するフィルター
         strFilter = "[更新日時] >= '" & strDate & "'"
         ' 作成日時が基準日以降であるアイテムを取得する場合は以下のフィルターを使用
         'strFilter = "[作成日時] >= '" & strDate & "'"
     End If
     ' コピー元フォルダーの取得
     Set fldSrc = fldSrcRoot.Folders(strName)
     ' コピー元フォルダーが隠しフォルダーならコピーせず終了
     If fldSrc.PropertyAccessor.GetProperty(PR_ATTR_HIDDEN) = True Then
         Exit Sub
     End If
     ' コピー先フォルダーの取得
     Set fldDst = fldDstRoot.Folders(strName)
     ' コピー先フォルダーが見つからなければ作成
     If fldDst Is Nothing Then
         ' フォルダーに格納されるアイテムの種別からフォルダー種別を設定
         dfType = GetFolderType(fldSrc)
         ' 新規にフォルダーを作成
         Set fldDst = fldDstRoot.Folders.Add(strName, dfType)
     End If
     ' フィルターによりアイテムを抽出
     Set colItems = fldSrc.Items.Restrict(strFilter)
     ' 抽出したアイテムのすべてについて処理
     For Each objItem In colItems
         ' アイテムのコピーを作成
         Set objCopy = objItem.Copy
         ' アイテムのコピーをコピー先フォルダーに移動
         objCopy.Move fldDst
     Next
     ' サブフォルダーについてもコピー処理
     For Each fldSub In fldSrc.Folders
         CopyItems fldSrc, fldDst, fldSub.Name, strFilter
     Next
End Sub
'
' フォルダーに保存するアイテム種別をもとにフォルダー種別を返す関数
Private Function GetFolderType(fldToCheck As Folder) As OlDefaultFolders
     Select Case fldToCheck.DefaultItemType
         Case olMailItem
             GetFolderType = olFolderInbox
         Case olAppointmentItem
             GetFolderType = olFolderCalendar
         Case olContactItem
             GetFolderType = olFolderContacts
         Case olTaskItem
             GetFolderType = olFolderTasks
         Case Else
             GetFolderType = olFolderInbox
     End Select
End Function

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

PropertyAccessor とウイルス感染

コメントにて以下のようなご質問をいただきました。


こんにちは。メールを開くことによるウイルス感染を防止するため、メールを開かずにヘッダ情報より、送信者のアドレスを表示する方法を検討しています。

■環境
・windows10
・Outlook2010(サポート終了後はOutlook2016)

■知りたいこと
別の方法があればご教示いただけますと幸いですが、現在は、PropertyAccessor を使用し、ヘッダ情報より「From:」を検索し、送信者アドレスをmsgboxで表示できるようにvbaを作成しております。

ここで疑問なのですが、PropertyAccessor を使用し取得したヘッダ情報は、メールを開いて取得したものではないのか?ということです。

やりたいことが、ウイルス感染防止。そのために、メールを開かずに送信者アドレスを知りたいなので、PropertyAccessorで、実はメールを開いていたでは、全く意味がありません。
上記の情報または、別にアドイン等導入することなくメールを開かずに、送信者アドレスを取得する方法がありましたら、ご教示いただければと思っております。
よろしくお願いします。


結論から言えば、PropertyAccessor を使用したタイミングでウイルスに感染するというようなことはありません。
それを説明するには、まず「メールを開く」とはどういうことなのかを定義する必要があるでしょう。

一般的な概念では「メールを開く」というのは Outlook のユーザー インターフェイスで本文を表示することを指すと考えられます。
この状態でウイルスに感染する可能性があるのは、本文を表示するコンポーネントにセキュリティ ホールが存在した場合になります。

Outlook 2003 までの Outlook は Internet Explorer により HTML メールの本文を表示しており、Internet Explorer には比較的容易な方法 (HTML に JavaScript を埋め込むなど) でセキュリティ ホールを悪用することができていたため、実際に本文を表示した際にウイルスに感染するという問題が発生することがありました。
しかし、Outlook 2007 以降の Outlook では本文の表示には Word のコンポーネントを使用しており、Internet Explorer のセキュリティ ホールの影響を受けることはありません。
もちろん、Word にセキュリティ ホールがあればそれを悪用した攻撃は可能となるのですが、Outlook の本文の表示の際に悪用するより、細工した Word ファイルを添付して送信したほうが Outlook 以外のメーラーも攻撃できるので、現在の Outlook で本文を表示するだけで感染するウイルスが登場する可能性は極めて低いと思われます。
また、Word のセキュリティ ホールを狙うようなウイルスの対策としては [ファイル]-[オプション]-[セキュリティ センター] の [セキュリティ センターの設定] (または [トラスト センター] の [トラスト センターの設定]) をクリックし、[電子メールのセキュリティ] で [すべての標準メールをテキスト形式で表示する] をオンにするというものがあります。
これにより、Word のコンポーネントを使用せずに抽出したテキスト本文のみが表示されるようになります。

いずれにせよ、「メールを開く」が本文を表示するという意味なら、PropertyAccessor を使用する際に Word のコンポーネントは使用されないので、メールを開いて取得したものではないといえます。

一方、「メールを開く」というのが「メールのデータを参照する」ということになると、話が変わってきます。
一般的に、SMTP で送信されるメールは MIME というフォーマットで送信されます。
このフォーマットは単一のテキスト データの中にメールのヘッダーや本文、添付ファイルなどをエンコードして格納するようなものであるため、受信したメール サーバーやメール クライアントはこの MIME データから件名や差出人などの情報を解析して取り出す必要があります。
そして、この解析処理に何らかのセキュリティ ホールがあり、それを悪用するようなウイルスが存在した場合、メール ソフトがメールを受信した瞬間に感染するという可能性もあるということになるのです。

そのような意味では、PropertyAccessor を使用して取得したヘッダー情報は「メールを開いて」取得したものと言えます。
ただ、Outlook ではヘッダー情報は受信したタイミングで MIME データから MAPI プロパティに変換されているため、PropertyAccessor を使用するタイミングでウイルスに感染するということはありません。
なお、この問題について対処するとなると、Outlook のセキュリティ修正を適用する以外に方法はないでしょう。

ちなみに、ウイルス感染防止のために送信者アドレスをチェックしたいとのことですが、SMTP では送信者のなりすましが簡単にできます。
実際、最近ではすでにやり取りした相手のメール アドレスやメールの内容などを使用してウイルスが含まれる添付ファイルを送って感染させるというような手口も確認されています。
そのため、送信者のアドレスだけで安全かどうかを判断するのはかえって危険なのではないかと思います。

参考リンク:
「Emotet」と呼ばれるウイルスへの感染を狙うメールについて