【问题标题】:Outlook VSTO AddIn How to avoid RaceOnRCWCleanupOutlook VSTO AddIn 如何避免 RaceOnRCWCleanup
【发布时间】:2020-05-17 08:48:05
【问题描述】:

我想要实现的是还使用 Inspector 上的 AppProperty Change 事件正确处理日历上的拖放事件:

每当用户与界面交互时,我都会更新 currentAppointmentItem(Explorer.SelectionChange、NewInspector、CloseInspector 等) 每当用户与界面交互时,我都会更新 currentInspector(SelectionChange、NewInspector、CloseInspector) 更新意味着我尝试适当地设置/取消设置事件处理程序,并相应地设置/取消设置 Marshal.ReleaseComObject。最后将引用清空。

但是当用户只点击日历中的 AppointmentItem 时,不会创建 Inspector 窗口。因此我无法捕捉 AppPropertyChange 事件。所以我决定在选定的 AppointmentItem 上调用 GetInspector,以防它不为空。我尝试使用它来接收 AppProperty 事件的更改,以便我可以正确处理日历上的拖放事件

问题:从 Microsoft 文档中我了解到,每当您丢失对 currentAppointmentItem 的引用时,您还应该使用 Marshal.ReleaseComObject ,否则您可能会遇到其他问题。 现在我遇到了我无法捕捉到的异常:RaceOnRCWCleanup ...似乎我试图释放一个仍在使用的 COM 对象(可能由 Outlook 使用)。我怎样才能避免这种情况? Marshal.ReleaseComObject(currentAppointmentItem) 是否正确

我在 Outlook.Explorer 上注册了 SelectionChange 事件。在那里我尝试注册 currentAppointment:

[...]
            log.Info("Selection_Change");
            if (currentExplorer == null)
            {
                return;
            }

            try
            {
                log.Info("Selection_Change: " + currentExplorer.Caption);
                Outlook.MAPIFolder selectedFolder = currentExplorer.CurrentFolder;
                if (currentExplorer.Selection.Count > 0)
                {
                    Object selObject = currentExplorer.Selection[1];
                    if (selObject is Outlook.AppointmentItem)
                    {
                        currentAppointmentItem = (Outlook.AppointmentItem)selObject;
                        Inspectors_NewInspector(currentAppointmentItem.GetInspector);                     
                    }

[...]

请注意:INspectors_NewInspector 也会在 Inspectors 集合中调用。

NewInspector 的代码是这样的

    void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
            {
                try
                {
                    log.Info("Inspectors_NewInspector");
                    //  This function (apparently) gets kicked off whenever a user opens a new or existing item
                    //  in Outlook (Calendar appointment, Email, etc).  
                    //  We can intercept it, modify it's properties, before letting our Ribbon know about its existance.
                    //            
                    if (Inspector != null)
                    {
                        log.Info("Inspectors_NewInspector: " + Inspector.Caption);
                        unregisterCurrentInspector();
                        currentInspector = Inspector;

                        object item = Inspector.CurrentItem;
                        if (item == null)
                            return;

                        if (!(item is Outlook.AppointmentItem))
                            return;
                        unregisterCurrentAppointmentItem();
                        currentAppointmentItem = (Outlook.AppointmentItem)item;
                        currentAppointmentItem.PropertyChange += AppPropertyChanged; // Handle situations where the 
                                                                                     // user tries to convert an appointment w/ an agreedo protocol to a recurring appointment. 
                                                                                     // This needs to be avoided .                
                        currentAppointmentItem.CustomPropertyChange += AppPropertyChanged;
                    }
                    ((Microsoft.Office.Interop.Outlook.InspectorEvents_10_Event)Inspector).Close += Inspector_Close;

                } catch (Exception ex)
                {
                    log.Error(ex.Message);
                }
            }

unregisterCurrentApppointmentItem:

private void unregisterCurrentAppointmentItem()
    {
        try
        {
            log.Info("unregisterCurrentAppointmentItem");
            if (currentAppointmentItem != null)
            {
                currentAppointmentItem.PropertyChange -= AppPropertyChanged; // Handle situations where the 
                currentAppointmentItem.CustomPropertyChange -= AppPropertyChanged;

                Marshal.ReleaseComObject(currentAppointmentItem);
                currentAppointmentItem = null;
            }
        } catch (Exception ex)
        {
            log.Error(ex.Message);
        }
    }

unregisterCurrentInspector:

    private void unregisterCurrentInspector()
            {
                log.Info("unregisterCurrentInspector");
                if (currentInspector != null)
                {
                    ((Microsoft.Office.Interop.Outlook.InspectorEvents_10_Event)currentInspector).Close -= Inspector_Close;
                    Marshal.ReleaseComObject(currentInspector);
                    currentInspector = null;
                }
            }

对此有何建议?

我已经尝试/考虑过的内容:

【问题讨论】:

    标签: outlook vsto outlook-addin


    【解决方案1】:

    首先,不需要模拟NewInspector事件。相反,您需要正确设置事件处理程序。看来您只需要实现检查器或资源管理器包装器。请参阅Implement a wrapper for inspectors and track item-level events in each inspector 了解更多信息。

    似乎我试图释放一个仍在使用中的 COM 对象(可能由 Outlook 使用)。我怎样才能避免这种情况? Marshal.ReleaseComObject(currentAppointmentItem) 是否正确

    是的,是的。但是你真的应该对通过调用属性和方法在你的代码中检索到的对象使用这个方法。您不应释放由 Office 应用程序作为参数传递的对象。查看When to release COM objects in Office add-ins developed in .NET 文章,该文章解释了可能的陷阱并回答了最广泛传播的问题。

    【讨论】:

    • 看来我已经按照参考文章中的建议实现了 Wrapper。虽然这篇文章没有在任何相关Item上调用Marshal.releaseComObject。因此,这似乎不再是必要的了。对我来说这很难理解,因为提到 AppointmentItem 有另一篇文章要求调用此方法?
    • 是的,您需要释放 COM 对象,而不是作为参数传递给事件处理程序的对象。
    【解决方案2】:

    为什么你甚至需要 Inspector 对象?您只使用Inspector.Close 事件吗? 使用AppointmentItem.Close / Write 事件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 2022-11-01
      • 2017-03-31
      • 1970-01-01
      • 2012-02-28
      • 2021-03-03
      • 2016-12-02
      相关资源
      最近更新 更多