【问题标题】:Outlook 2013 & Outlook Add-In :: MAPIFolderEvents_12_Event.BeforeItemMove Not Firing?Outlook 2013 和 Outlook 加载项 :: MAPIFolder Events_12_Event.BeforeItemMove 未触发?
【发布时间】:2015-09-12 16:49:52
【问题描述】:

我正在尝试实现 Adrian Brown 非常好的 Outlook Add-In code,它在 3 次中有 2 次有效。 ItemAddItemChange 事件按预期触发,但 MAPIFolderEvents_12_Event.BeforeItemMove 的事件处理程序似乎没有做任何事情 - 我什至没有命中在第一行的断点事件处理程序。

更多代码清晰

这是 CalendarMonitor 类;它监视文件夹的 Items 集合上的 ItemAdd、ItemChange 事件,以及 MAPIFolder 上的 BeforeItemMove:

public class CalendarMonitor
{
    private Explorer _explorer;
    private List<string> _folderPaths;
    private List<MAPIFolder> _calendarFolders;
    private List<Items> _calendarItems;
    private MAPIFolder _deletedItemsFolder;

    public event EventHandler<EventArgs<AppointmentItem>> AppointmentAdded;
    public event EventHandler<EventArgs<AppointmentItem>> AppointmentModified;
    public event EventHandler<CancelEventArgs<AppointmentItem>> AppointmentDeleting;

    public CalendarMonitor(Explorer explorer)
    {
        _folderPaths = new List<string>();
        _calendarFolders = new List<MAPIFolder>();
        _calendarItems = new List<Items>();

        _explorer = explorer;
        _explorer.BeforeFolderSwitch += Explorer_BeforeFolderSwitch;

        var session = _explorer.Session;
        try
        {
            _deletedItemsFolder = session.GetDefaultFolder(OlDefaultFolders.olFolderDeletedItems);
            HookupDefaultCalendarEvents(session);
        }
        finally
        {
            Marshal.ReleaseComObject(session);
            session = null;
        }
    }

    private void HookupDefaultCalendarEvents(_NameSpace session)
    {
        var folder = session.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);
        if (folder == null) return;

        try
        {
            HookupCalendarEvents(folder);
        }
        finally
        {
            Marshal.ReleaseComObject(folder);
            folder = null;
        }
    }

    private void Explorer_BeforeFolderSwitch(object obj, ref bool cancel)
    {
        var folder = (obj as MAPIFolder);
        if (folder == null) return;

        try
        {
            // Hookup events to any other Calendar folder opened.
            if (folder.DefaultItemType == OlItemType.olAppointmentItem)
                HookupCalendarEvents(folder);
        }
        finally
        {
            Marshal.ReleaseComObject(folder);
            folder = null;
        }
    }

    private void HookupCalendarEvents(MAPIFolder calendarFolder)
    {
        if (calendarFolder.DefaultItemType != OlItemType.olAppointmentItem)
        {
            throw new ArgumentException("The MAPIFolder must use AppointmentItems as the default type.");
        }

        // Ignore other user's calendars.
        if (_folderPaths.Contains(calendarFolder.FolderPath) || (!IsUsersCalendar(calendarFolder))) return;

        var items = calendarFolder.Items;

        // Store folder path to prevent repeating listeners
        _folderPaths.Add(calendarFolder.FolderPath);

        // Store a reference to the folder & items to prevent garbage collection
        _calendarFolders.Add(calendarFolder);
        _calendarItems.Add(items);

        // Add listeners
        ((MAPIFolderEvents_12_Event)calendarFolder).BeforeItemMove += Calendar_BeforeItemMove;
        items.ItemChange += CalendarItems_ItemChange;
        items.ItemAdd += CalendarItems_ItemAdd;
    }

    private void CalendarItems_ItemAdd(object obj)
    {
        var appointment = (obj as AppointmentItem);
        if (appointment == null) return;

        try
        {
            if (AppointmentAdded != null)
                AppointmentAdded(this, new EventArgs<AppointmentItem>(appointment));
        }
        finally
        {
            Marshal.ReleaseComObject(appointment);
            appointment = null;
        }
    }

    private void CalendarItems_ItemChange(object obj)
    {
        var appointment = (obj as AppointmentItem);
        if (appointment == null) return;

        try
        {
            if (AppointmentModified != null)
                AppointmentModified(this, new EventArgs<AppointmentItem>(appointment));
        }
        finally
        {
            Marshal.ReleaseComObject(appointment);
            appointment = null;
        }
    }

    private void Calendar_BeforeItemMove(object obj, MAPIFolder moveToFolder, ref bool cancel)
    { 
        if ((moveToFolder != null) && (!IsDeletedItemsFolder(moveToFolder))) return;

        var appointment = (obj as AppointmentItem);
        if (appointment == null) return;

        try
        {
            if (AppointmentDeleting == null) return;

            // Listeners to the AppointmentDeleting event can cancel the move operation if moving
            // to the deleted items folder.
            var args = new CancelEventArgs<AppointmentItem>(appointment);
            AppointmentDeleting(this, args);
            cancel = args.Cancel;
        }
        finally
        {
            Marshal.ReleaseComObject(appointment);
            appointment = null;
        }
    }

    private bool IsUsersCalendar(MAPIFolder folder)
    {
        // This is based purely on my observations so far - a better way?
        return (folder.Store != null);
    }

    private bool IsDeletedItemsFolder(MAPIFolder folder)
    {
        return (folder.EntryID == _deletedItemsFolder.EntryID);
    }

    public AppointmentItem Item { get; set; }
}

新信息:

我做了一些额外的“故障排除”并提供了更多信息:一时兴起,我在 Outlook 中创建了一个新日历(在调试时),你瞧,BeforeItemMove 事件就像我预期的那样触发了删除新日历中的约会,但在原来的日历中仍然不起作用。

如果我退出调试会话并重新启动,日历的事件都不会按预期运行,尽管之前工作正常。任何新日历的 BeforeItemMove 事件都可以正常工作,直到我关闭 Outlook - 然后它又恢复为无响应。

我希望这些额外的信息能为那些比我更聪明的人提供见解。非常感谢任何帮助。

【问题讨论】:

  • 如果断点未命中且事件未触发,您是否检查了“模块”窗口以查看正在加载的 DLL 和版本?
  • 您能说得更具体些吗?我找不到“模块”窗口。
  • 在 Visual Studio 的调试菜单 > Windows > 模块下

标签: c# outlook outlook-addin


【解决方案1】:

calFolder 变量必须在全局/类级别声明以避免被垃圾收集器释放。

【讨论】:

  • aCalendarFolder 的引用存储在 private List&lt;MAPIFolder&gt; folders 中 - 我还需要存储对 MAPIFolderEvents_12_Events 演员表的引用吗?
  • 不,应该没问题,但是 BeforeItemMove 有问题。您是否安装了所有最新的服务包?您是否在 OutlookSpy 中看到事件触发(单击文件夹按钮,转到事件选项卡,查看页面底部的日志)?
  • OutlookSpy(很好的提示 - 谢谢)说事件 is 正在触发,但我仍然没有看到该方法的执行。出于好奇,既然BeforeItemMove 有问题,如何确定哪个AppointmentItem 已被删除?
  • 您可以使用 Items.ItemRemove 事件,但它不会将要删除的项目作为参数传递,因为该项目已经消失(MAPI 事件是异步的)。您可以使用 Redemption 及其 RDOItems.ItemRemove 事件 (dimastr.com/redemption/RDOItems.htm) - 它传递 PR_SOURCE_KEY 属性的值,但这意味着您需要在开始侦听事件之前从内容表中缓存 PR_SOURCE_KEY 属性的值。
  • 我已经编辑了问题 - 您能查看新信息吗?
【解决方案2】:

你在哪里声明了源对象?应该触发事件时它是否还活着?

无论如何,您可以考虑开发一个检查器包装器。请参阅Developing an Inspector Wrapper for Outlook 2010How to: Implement a Wrapper for Inspectors and Track Item-Level Events in Each Inspector 了解更多信息。

C# 和 VB.NET 中的示例项目也可用 - Outlook 2010: Developing an Inspector Wrapper

【讨论】:

    猜你喜欢
    • 2015-01-30
    • 2013-05-16
    • 2014-11-12
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多