2015-09-17 42 views
2

虽然我在SO上发现了这个问题的很多实例,但我没有实现的解决方案已经解决了我的问题;希望你能帮我解决这个谜题。 注意:这是我第一次进入COM对象的世界,所以我的无知如此广泛。Outlook加载项::与其底层RCW分离的COM对象不能使用

作为一个开始,我使用阿德里安布朗的Outlook Add-In code。我不会完全复制他的CalendarMonitor类;以下是相关部分:

public class CalendarMonitor 
{ 
    private ItemsEvents_ItemAddEventHandler itemAddEventHandler; 
    public event EventHandler<EventArgs<AppointmentItem>> AppointmentAdded = delegate { }; 

    public CalendarMonitor(Explorer explorer) 
    { 
     _calendarItems = new List<Items>(); 
     HookupDefaultCalendarEvents(session); 
    } 

    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 HookupCalendarEvents(MAPIFolder calendarFolder) 
    { 
     var items = calendarFolder.Items; 

     _calendarItems.Add(items); 

     // Add listeners 
     itemAddEventHandler = new ItemsEvents_ItemAddEventHandler(CalendarItems_ItemAdd); 
     items.ItemAdd += itemAddEventHandler; 
    } 

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

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

与添加约会无关的位已被编辑。

我实例化CalendarMonitor上课的时候我阀芯了外接,和做的工作在AppointmentAdded事件,包括添加UserProperty到AppointmentItem

private void ThisAddIn_Startup(object sender, EventArgs e) 
{ 
    _calendarMonitor = new CalendarMonitor(Application.ActiveExplorer()); 
    _calendarMonitor.AppointmentAdded += monitor_AppointmentAdded; 
} 

private async void monitor_AppointmentAdded(object sender, EventArgs<AppointmentItem> e) 
{ 
    var item = e.Value; 

    Debug.Print("Outlook Appointment Added: {0}", item.GlobalAppointmentID); 

    try 
    { 
     var result = await GCalUtils.AddEventAsync(item); 

     //store a reference to the GCal Event for later. 
     AddUserProperty(item, Resources.GCalId, result.Id); 

     Debug.Print("GCal Appointment Added: {0}", result.Id); 
    } 
    catch (GoogleApiException ex) 
    { 
     PrintToDebug(ex); 
    } 
    finally 
    { 
     Marshal.ReleaseComObject(item); 
     item = null; 
    } 
} 

错误在这里抛出,在那里我尝试将一个UserProperty添加到AppointmentItem。我跟了我能找到的最好例子:

private void AddUserProperty(AppointmentItem item, string propertyName, object value) 
{ 
    UserProperties userProperties = null; 
    UserProperty userProperty = null; 

    try 
    { 
     userProperties = item.UserProperties; 
     userProperty = userProperties.Add(propertyName, OlUserPropertyType.olText); 
     userProperty.Value = value; 
     item.Save(); 
    } 
    catch (Exception ex) 
    { 
     Debug.Print("Error setting User Properties:"); 
     PrintToDebug(ex); 
    } 
    finally 
    { 
     if (userProperty != null) Marshal.ReleaseComObject(userProperty); 
     if (userProperties != null) Marshal.ReleaseComObject(userProperties); 
     userProperty = null; 
     userProperties = null; 
    } 
} 

...但它扼流圈,当我试图将UserProperty添加到AppointmentItem。我得到了一个非常流行的错误:COM object that has been separated from its underlying RCW cannot be used.诚实地说,我不知道我在做什么;所以我拼命向我的Padawan寻找绝地大师。

+0

Marshal.ReleaseComObject()是非常难看的手动内存管理。 C程序员必须编写并永远错误的那种。垃圾收集是为了防止这种错误一直发生而发明的。这里的情况大致相同,您将其称为未创建的对象。代码在不知情的情况下会继续使用被破坏的对象kaboom。停止帮助。了解垃圾收集器如何工作并学会信任它,[阅读本文](http://stackoverflow.com/a/17131389/17034)。 –

+0

'处理定期预约项目时,应先释放任何事前参考,在访问或修改项目前获取定期预约项目的新参考,并在完成并保存更改后立即发布这些参考。这种做法适用于定期的AppointmentItem对象,以及任何Exception或RecurrencePattern对象。“ - 只是试图遵循该指导。相信我..所有这些'Marshal.ReleaseComObject()'东西都不是选择。 –

+0

@HansPassant - 我读过你非常优秀的文章,但我仍然有点模糊。你可以从上面选择一种方法,并将其正确地重写为答案? –

回答

1

这里的主要问题是使用Marshal.ReleaseComObject用于受管理运行时在多个位置使用的RCW。

事实上,这段代码引发了这个问题。让我们来看看class CalendarMonitor

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

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

的情况下返回后,它告诉管理运行时释放COM对象(但从整个管理运行时的点,但没有进一步的)。

  appointment = null; 
     } 
    } 

然后,async事件附加,将使用appointment其实之前返回,就在await行:

private async void monitor_AppointmentAdded(object sender, EventArgs<AppointmentItem> e) 
{ 
    var item = e.Value; 

    Debug.Print("Outlook Appointment Added: {0}", item.GlobalAppointmentID); 

    try 
    { 
     var result = await GCalUtils.AddEventAsync(item); 

这种方法实际上返回这里。 C#的异步代码生成在await点处打破async方法,为每个处理await ed结果的代码块生成continuation passing style (CPS)匿名方法。

 //store a reference to the GCal Event for later. 
     AddUserProperty(item, Resources.GCalId, result.Id); 

     Debug.Print("GCal Appointment Added: {0}", result.Id); 
    } 
    catch (GoogleApiException ex) 
    { 
     PrintToDebug(ex); 
    } 
    finally 
    { 
     Marshal.ReleaseComObject(item); 

看,它再次释放COM对象。没问题,但不是最优的。这是通过使用ReleaseComObject不知道发生了什么的指示器,除非证明有必要,否则最好避免它。

 item = null; 
    } 
} 

在本质上使用的ReleaseComObject应受以下几点进行了全面审查:

  • 我需要真正确保管理的环境,现在释放对象而不是在不确定的时间?

    有时,某些本地对象需要被释放,以使相关的副作用。例如,在分布式事务下确保对象提交,但如果您发现需要这样做,那么也许您正在开发一个服务组件,并且您没有正确地在手动事务中注册对象。

    其他时候,你遍历一个巨大的对象集合,无论每个对象是多么小,你可能需要释放他们为了不使任何应用程序或远程应用程序了。有时,更频繁的GC切换到64位和/或添加RAM可以以某种方式解决问题。

  • 我是唯一拥有/指针但从托管环境的点的对象?

    例如,做我创建它,或在该对象由我创建另一个对象间接提供?

    是否有到该对象或没有进一步引用其容器在管理环境?我绝对使用对象ReleaseComObject后,在它后面,或在其它任何时间(例如,通过确保将其存储在一个领域,或关闭该代码,即使在

  • 上午迭代器方法或async方法)?

    这是为了避免可怕的断开RCW例外。

相关问题