Outlook VSTO AddIn如何避免RaceOnRCWCleanup

问题描述 投票:0回答:2

我想要达到的目的还在于使用检查器上的AppProperty Change事件来正确处理日历上的拖放事件:

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

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

问题:据我了解,当您丢失对currentAppointmentItem的引用时,根据Microsoft文档,您还应该使用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
2个回答
0
投票

首先,不需要模拟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文章,它解释了可能的陷阱并为最广泛使用的问题提供了答案。


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.