C# で Outlook の予定表を取得する


# はじめに

*こんにちは、のんびりエンジニアのたっつーです。*
*[ブログの記事](C# で Outlook の予定表を取得する) にしたのですが他の方のためになればと思いQiitaにも記述します。*

Outlookからスケジュール情報を取得しないといけない状況になったので簡単に取得できるソースコードを提供できればと思い記載いたします。
例外処理などをまったく行っていないので各自で実装いただければと思います。

また、本サンプルコードは「NetOffice」ライブラリを使用していますがOutlookのComDLLを使用した場合でも基本的には同じソースコードで動くと思いますが確認していないのでご自由にお使いください。

# 前提条件

・MS Office Outlookがインストールされている事。
・Visual Studio のある程度のバージョンがインストールされている事。

# NetOfficeとは

Microsoft Office製品にアクセスするための.NETラッパーアセンブリのようで、Officeのバージョンに関係なく簡単にコードが書けるとのこと

開発時にOfficeのバージョン指定でDLLを参照しますが、これを使うとバージョンの依存部分を吸収してくれて、開発・運用で別々のオフィスでも可能な限り実行できるのかなと思います。（***未検証***）

# Nugetで取得

Nugetパッケージ管理コンソールで以下を実行します。

```
PM> Install-Package NetOffice.Outlook
```

# ソースコード

VisualStudioに以下、２つのソースコードを追加します。

- **OutlookManager.cs**
   - Outlookのスケジュールを取得するための管理クラス
- **ScheduleItem.cs**
   - Outlookから取得したスケジュールデータを管理するためのアイテムクラス

## OutlookManager.cs

Outlookのスケジュールを取得するための管理クラス

```csharp
using NetOffice.OutlookApi;
using NetOffice.OutlookApi.Enums;

namespace Test
{
    public class OutlookManager
    {
        public List<ScheduleItem> GetScheduleList(string mailOrName, DateTime date)
        {
            return GetScheduleList(mailOrName, date, date.AddDays(1));
        }

        public List<ScheduleItem> GetScheduleList(string mailOrName, DateTime start, DateTime end)
        {
            // 対象フォルダの取得
            var folder = GetCalenderFolder(mailOrName);

            // スケジュールの検索
            var items = GetScheduleItems(folder, start, end);

            // 定期的なスケジュールを展開して、ScheduleItemに変換
            var schedules = ExpansionAndConvert(items, start, end);

            return schedules;
        }

        private Application GetApplication()
        {
            return new Application();
        }

        private MAPIFolder GetCalenderFolder(string mailOrName)
        {
            var outlook = GetApplication();

            // var oFolder = outlook.Session.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);
            // var rObject = outlook.Session.CreateRecipient("rm20160917120031@jp.nssol.nssmc.com");
            var rObject = outlook.Session.CreateRecipient(mailOrName);
            var oFolder = outlook.Session.GetSharedDefaultFolder(rObject, OlDefaultFolders.olFolderCalendar);
            // oFolder.Items.IncludeRecurrences = true;
            // Console.WriteLine(oFolder.Name);

            return oFolder;
        }

        private _Items GetScheduleItems(MAPIFolder folder, DateTime start, DateTime end)
        {
            // Filter apply
            string startDate = start.ToString("yy/MM/dd");
            string endDate = end.ToString("yy/MM/dd");
            string filter = $"[Start] >= '{startDate}' AND [Start] <= '{endDate}'";
            // Console.WriteLine(filter);
            var list = folder.Items.Restrict(filter);

            // Set Sort
            // list.Sort("[Start]", false);

            return list;
        }

        private List<ScheduleItem> ExpansionAndConvert(_Items items, DateTime start, DateTime end)
        {
            var ret = new List<ScheduleItem>();

            foreach (AppointmentItem item in items)
            {
                if (item.IsRecurring)
                {
                    var pattern = item.GetRecurrencePattern();
                    DateTime first = new DateTime(start.Year, start.Month, start.Day, item.Start.Hour, item.Start.Minute, 0);
                    DateTime last = new DateTime(end.Year, end.Month, end.Day);
                    for (DateTime cur = first; cur <= last; cur = cur.AddDays(1))
                    {
                        try
                        {
                            // ここの取得が成功したらスケジュールが存在するって判定になる
                            var recur = pattern.GetOccurrence(cur);
                            var curEnd = new DateTime(cur.Year, cur.Month, cur.Day, item.End.Hour, item.End.Minute, 0);
                            ret.Add(new ScheduleItem(item.Subject, cur, curEnd));
                        }
                        catch
                        { }
                    }
                }
                else
                {
                    ret.Add(new ScheduleItem(item.Subject, item.Start, item.End));
                }
            }

            // 指定の日付幅だけにする、ついでにソート
            ret = ret.Where(x => start <= x.Start)
                     .Where(x => x.End <= end)
                     .OrderBy(x => x.Start)
                     .ToList();

            return ret;
        }
    }
}

```

## ScheduleItem.cs

Outlookから取得したスケジュールデータを管理するためのアイテムクラス

***com経由でのアクセスを基本的にやるので、comデータを直接管理しないようにするために一旦自分の管理クラスに変換したほうが良いかと思います。（経験則）***

```csharp
namespace Test
{
    public class ScheduleItem
    {
        public string Title { get; set; }

        public DateTime Start { get; set; }

        public DateTime End { get; set; }

        public ScheduleItem(string title, DateTime start, DateTime end)
        {
            this.Title = title;
            this.Start = start;
            this.End = end;
        }

        public override string ToString()
        {
            return $"({Start}～{End}) {Title}";
        }
    }
}
```

# 使い方

## Program.cs

```csharp
namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            OutlookManager outlookManager = new OutlookManager();

            // メールアドレス もしくは 名前 を指定
            // 対象日時を指定（Start～Endでの指定もできます）
            var schedules = outlookManager.GetScheduleList("test@test.com", DateTime.Today);
            foreach(var s in schedules)
                Console.WriteLine(s);
            // Console.WriteLine($"Schedules count={schedules.Count}");
        }
    }
}
```

## 実行結果

メールアドレス・当日の２パラメータで指定し、当日のスケジュールデータを３件取得できました。

```
(2018/12/10 14:00:00～2018/12/10 15:00:00) 会議１
(2018/12/10 15:00:00～2018/12/10 16:00:00) 会議２
(2018/12/10 16:00:00～2018/12/10 17:30:00) 会議３
```

# 終わりに

*よければ ブログ「[初心者向けUnity情報サイト](https://www.fast-system.jp/)」の方にも色々記載しているのでぜひご参照いただければと思います。*