获取共享的 Outlook 日历事件 C#

Fetch Shared Outlook Calendar Events C#

我知道如何在当前用户的 outlook 日历中检索事件,下面的代码用于删除与特定模式匹配的项目,例如:

private void RemoveAppointments()
        {
            Outlook.Application outlook = new Outlook.Application();
            Outlook.MAPIFolder calendarFolder = outlook.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);
            Outlook.Items outlookCalendarItems = calendarFolder.Items;
            for (int i = outlookCalendarItems.Count; i > 0; i--)
            {
                if (outlookCalendarItems[i].Subject.Contains("On Call: Regions:") && outlookCalendarItems[i].Start.Year == _year)
                {
                    outlookCalendarItems[i].Delete();
                }
            }
        }

但是,我现在需要能够读取 Outlook 团队中所有用户的日历事件(假设已正确设置共享权限)。理想情况下,我需要能够对每个用户进行迭代,但如果我只是获取所有事件的集合,然后可以按用户查询就足够了。

有什么可以开始的想法吗?


注意:这是团队在 Outlook 的“日历”窗格中的表示方式。敏感细节已编辑。

不使用 Namespace.GetDefaultFolder,而是使用 Namespace.GetSharedDefaultFolder,传递从 Namespace.CreateRecipient 返回的 Recipient 对象。

另请记住,遍历文件夹中的 所有 项目是一个糟糕的主意,尤其是当您打开未缓存在本地 OST 文件中的在线文件夹时。请改用 Items.Find/FindNextItems.Restrict

感谢@Dmitry 帮助我解决了我的问题。但是,为了最大限度地提高这个问题对未来读者的效用,我想我会对其进行扩展。


假设:

using Microsoft.Office.Interop.Outlook;

并且引用了 COM 程序集 Microsoft Outlook 16.0 Object Library


第一步是创建一个 Outlook.Application 对象作为 Outlook 功能的接口(您可以将其视为完整 Outlook 程序的内部实例):

Application app = new Application();

然后我从与团队关联的全局地址列表中的通讯组列表中提取所有用户。这是通过从 Application 实例的 Session 属性 创建一个 Recipient 对象来完成的。

Recipient distList = app.Session.CreateRecipient(yourDistList);

从这里我们可以提取收件人的所有真实姓名和用户名 AdressEntry.Members 属性。要将它们都拉入 (string,string) 的匿名元组,我们可以使用此 LINQ 查询,如果您不喜欢这样,您可以像平常一样迭代:

List<(string,string)> usersData = distList.AddressEntry.Members.Cast<AddressEntry>().Select(entry => (entry.Name,entry.Address)).ToList();

现在,给定一个特定的用户名,只要日历已经与当前用户共享,您就可以使用 Session:

GetSharedDefaultFolder() 方法访问它
MAPIFolder sharedCalendar = _app.Session.GetSharedDefaultFolder(teamMember, OlDefaultFolders.olFolderCalendar);

在这一点上,我发现进行一些过滤以尝试避免最常见的 COMExceptions 很有用,但是,有很多我似乎无法确定原因所以我只是 catch (COMException) 并将它们装箱。我知道这不是很好的做法,但它似乎并没有影响我访问我有权访问的日历。一些(非常)基本的过滤:

if (sharedCalendar.DefaultMessageClass != "IPM.Appointment" || teamMember.DisplayType != 0)
{
    return null; //Calendar not shared.
}

现在我们必须使用 Microsoft Outlook 格式构建一个 Filter 字符串,这可以使用以下语句来完成(其中 fromto 都是 DateTime 个对象):

string sFilter = $"[End] > '{from:g}' AND [Start] < '{to:g}' AND [Recurring] = 'No'";

我们过滤掉重复发生的事件,否则开始和结束日期可能会大大超出范围而不会发生内部事件。出于我的目的,无论如何我都不需要重复发生的事件,但是,如果你这样做,你将不得不单独处理它。

现在我们终于可以使用 MAPIFolderItems.Restrict() 方法收集我们需要的事件了:

Items results = sharedCalendar.Items.Restrict(sFilter);

这个 returns 一个 Items 接口,指向我们过滤器中的所有项目。

最后,我们可以迭代每个项目(我以相反的顺序迭代,因为我从我的一个删除事件的旧应用程序中复制了代码,但在这种情况下应该无关紧要)。您可能必须将 object 转换为 AppointmentItem,具体取决于编译器是否可以推断出这一点。

List<AppData> appointments = new List<AppData>();
for (int i = results.Count; i > 0; i--)
{
    appointments.Add(new AppData(results[i], username));
}

我将每个事件存储为一个 AppData 结构,只保留我需要的数据:

public struct AppData
{
    public string Subject { get; }
    public DateTime From { get; }      
    public DateTime To { get; }       
    public string Location { get; }      
    public string Categories { get; }      
    public string Username { get; }
    public AppData(AppointmentItem appItem, string username)
    {
        Subject = appItem.Subject;
        From = appItem.Start;
        To = appItem.End;
        Location = appItem.Location;
        Categories = appItem.Categories;
        Username = username;
    }
}

所有这些导致 class 看起来像这样:

public class OutlookCommunicator : IDisposable
{
    private readonly Application _app;

    public OutlookCommunicator()
    {
        _app = new Application();
    }

    /// <summary>
    /// Username of the distribution list according to the GAL.
    /// </summary>
    private const string DistList = "redacted";

    /// <summary>
    /// Fetches a list of all usernames and names within the DistList.
    /// </summary>
    /// <returns>List&lt;string&gt; containing all usernames.</returns>
    public List<(string,string)> GetUsers()
    {
            Recipient warEngineering = _app.Session.CreateRecipient(DistList);
            List<(string,string)> usernames = warEngineering.AddressEntry.Members.Cast<AddressEntry>().Select(entry => (entry.Name,entry.Address)).ToList();
            return usernames;

    }



    /// <summary>
    /// Fetches all calendar events for a user falling within the provided range.
    /// </summary>
    /// <param name="from">Start search date.</param>
    /// <param name="to">End search dat.</param>
    /// <param name="username">User's calendar to search.</param>
    /// <returns></returns>
    public List<AppData> GetEventsInRange(DateTime from, DateTime to, string username)
    {
        List<AppData> appointments = new List<AppData>();
        try
        {

            Recipient teamMember = _app.Session.CreateRecipient(username);
            MAPIFolder sharedCalendar = _app.Session.GetSharedDefaultFolder(teamMember, OlDefaultFolders.olFolderCalendar);
            if (sharedCalendar.DefaultMessageClass != "IPM.Appointment" || teamMember.DisplayType != 0)
            {
                return null; //Calendar not shared.
            }

            string sFilter = $"[End] > '{from:g}' AND [Start] < '{to:g}' AND [Recurring] = 'No'";
            Items results = sharedCalendar.Items.Restrict(sFilter);
            for (int i = results.Count; i > 0; i--)
            {
                appointments.Add(new AppData(results[i], username));
            }

            return appointments;
        }
        catch (COMException)
        {
            return null;
        }
    }

    public void Dispose()
    {
        _app?.Quit();
    }