Fixed double release of item event objects

This commit is contained in:
Patrick Simpson 2017-02-08 16:03:43 +01:00
parent 58cd79fdb6
commit b4d84ef303
3 changed files with 47 additions and 28 deletions

View File

@ -38,14 +38,14 @@ namespace Acacia.Stubs.OutlookWrappers
if (o == null) if (o == null)
return null; return null;
IBase wrapper = CreateWrapper(o); IBase wrapper = CreateWrapper(o, mustRelease);
if (wrapper != null) if (wrapper != null)
wrapper.MustRelease = mustRelease; wrapper.MustRelease = mustRelease;
ComRelease.LogWrapper(o, wrapper); ComRelease.LogWrapper(o, wrapper);
return wrapper; return wrapper;
} }
private static IBase CreateWrapper(object o) private static IBase CreateWrapper(object o, bool mustRelease)
{ {
// TODO: switch on o.Class // TODO: switch on o.Class
if (o is NSOutlook.MailItem) if (o is NSOutlook.MailItem)
@ -64,9 +64,11 @@ namespace Acacia.Stubs.OutlookWrappers
return new TaskItemWrapper((NSOutlook.TaskItem)o); return new TaskItemWrapper((NSOutlook.TaskItem)o);
// TODO: support others? // TODO: support others?
// The caller assumes a wrapper will be returned, so any lingering object here will never be released. if (mustRelease)
// TODO: do this only if caller has mustRelease {
ComRelease.Release(o); // The caller assumes a wrapper will be returned, so any lingering object here will never be released.
ComRelease.Release(o);
}
return null; return null;
} }
@ -75,6 +77,7 @@ namespace Acacia.Stubs.OutlookWrappers
{ {
return (Type)Wrap(o, mustRelease); return (Type)Wrap(o, mustRelease);
} }
// TODO: are these not the same now? Differ only on wrong type? // TODO: are these not the same now? Differ only on wrong type?
public static Type WrapOrDefault<Type>(object o, bool mustRelease = true) public static Type WrapOrDefault<Type>(object o, bool mustRelease = true)
where Type : IBase where Type : IBase

View File

@ -73,7 +73,7 @@ namespace Acacia.Utils
Logger.Instance.TraceExtra(typeof(ComRelease), "Releasing object: {0:X} @ {1}", GetObjAddress(o), Logger.Instance.TraceExtra(typeof(ComRelease), "Releasing object: {0:X} @ {1}", GetObjAddress(o),
new System.Diagnostics.StackTrace()); new System.Diagnostics.StackTrace());
} }
Marshal.ReleaseComObject(o); Marshal.FinalReleaseComObject(o);
} }
private static long GetObjAddress(object o) private static long GetObjAddress(object o)

View File

@ -208,6 +208,8 @@ namespace Acacia.Utils
#region Implementation #region Implementation
private readonly HashSet<MailEventHooker> _keepAlives = new HashSet<MailEventHooker>();
public MailEvents(IAddIn app) public MailEvents(IAddIn app)
{ {
app.ItemLoad += OnItemLoad; app.ItemLoad += OnItemLoad;
@ -219,21 +221,25 @@ namespace Acacia.Utils
NSOutlook.ItemEvents_10_Event hasEvents = item as NSOutlook.ItemEvents_10_Event; NSOutlook.ItemEvents_10_Event hasEvents = item as NSOutlook.ItemEvents_10_Event;
if (hasEvents != null) if (hasEvents != null)
{ {
new MailEventHooker(hasEvents, this); _keepAlives.Add(new MailEventHooker(item, hasEvents, this));
} }
else ComRelease.Release(item);
} }
private class MailEventHooker : ComWrapper private class MailEventHooker : ComWrapper
{ {
private NSOutlook.ItemEvents_10_Event _item; private object _item;
private NSOutlook.ItemEvents_10_Event _itemEvents;
private readonly MailEvents _events; private readonly MailEvents _events;
// TODO: remove id and debug logging
private int _id; private int _id;
private static int nextId; private static int nextId;
public MailEventHooker(NSOutlook.ItemEvents_10_Event item, MailEvents events) public MailEventHooker(object item, NSOutlook.ItemEvents_10_Event itemEvents, MailEvents events)
{ {
this._id = ++nextId; this._id = ++nextId;
this._item = item; this._item = item;
this._itemEvents = itemEvents;
this._events = events; this._events = events;
HookEvents(true); HookEvents(true);
} }
@ -241,57 +247,67 @@ namespace Acacia.Utils
protected override void DoRelease() protected override void DoRelease()
{ {
Logger.Instance.Debug(this, "DoRelease: {0}", _id); Logger.Instance.Debug(this, "DoRelease: {0}", _id);
_events._keepAlives.Remove(this);
ComRelease.Release(_item); ComRelease.Release(_item);
_item = null; _item = null;
ComRelease.Release(_itemEvents);
_itemEvents = null;
} }
private void HookEvents(bool add) private void HookEvents(bool add)
{ {
if (add) if (add)
{ {
_item.BeforeDelete += HandleBeforeDelete; _itemEvents.BeforeDelete += HandleBeforeDelete;
_item.Forward += HandleForward; _itemEvents.Forward += HandleForward;
_item.Read += HandleRead; _itemEvents.Read += HandleRead;
_item.Reply += HandleReply; _itemEvents.Reply += HandleReply;
_item.ReplyAll += HandleReplyAll; _itemEvents.ReplyAll += HandleReplyAll;
_item.Unload += HandleUnload; _itemEvents.Unload += HandleUnload;
_item.Write += HandleWrite; _itemEvents.Write += HandleWrite;
} }
else else
{ {
_item.BeforeDelete -= HandleBeforeDelete; _itemEvents.BeforeDelete -= HandleBeforeDelete;
_item.Forward -= HandleForward; _itemEvents.Forward -= HandleForward;
_item.Read -= HandleRead; _itemEvents.Read -= HandleRead;
_item.Reply -= HandleReply; _itemEvents.Reply -= HandleReply;
_item.ReplyAll -= HandleReplyAll; _itemEvents.ReplyAll -= HandleReplyAll;
_item.Unload -= HandleUnload; _itemEvents.Unload -= HandleUnload;
_item.Write -= HandleWrite; _itemEvents.Write -= HandleWrite;
} }
} }
private void HandleBeforeDelete(object item, ref bool cancel) private void HandleBeforeDelete(object item, ref bool cancel)
{ {
Logger.Instance.Debug(this, "HandleBeforeDelete: {0}", _id);
_events.OnBeforeDelete(item, ref cancel); _events.OnBeforeDelete(item, ref cancel);
} }
private void HandleForward(object response, ref bool cancel) private void HandleForward(object response, ref bool cancel)
{ {
_events.OnForward(_item as NSOutlook.MailItem, response as NSOutlook.MailItem); Logger.Instance.Debug(this, "HandleForward: {0}", _id);
_events.OnForward(_itemEvents as NSOutlook.MailItem, response as NSOutlook.MailItem);
} }
private void HandleRead() private void HandleRead()
{ {
_events.OnRead(_item as NSOutlook.MailItem); Logger.Instance.Debug(this, "HandleRead: {0}", _id);
_events.OnRead(_itemEvents as NSOutlook.MailItem);
} }
private void HandleReply(object response, ref bool cancel) private void HandleReply(object response, ref bool cancel)
{ {
_events.OnReply(_item as NSOutlook.MailItem, response as NSOutlook.MailItem); Logger.Instance.Debug(this, "HandleReply: {0}", _id);
_events.OnReply(_itemEvents as NSOutlook.MailItem, response as NSOutlook.MailItem);
} }
private void HandleReplyAll(object response, ref bool cancel) private void HandleReplyAll(object response, ref bool cancel)
{ {
_events.OnReplyAll(_item as NSOutlook.MailItem, response as NSOutlook.MailItem); Logger.Instance.Debug(this, "HandleReplyAll: {0}", _id);
_events.OnReplyAll(_itemEvents as NSOutlook.MailItem, response as NSOutlook.MailItem);
} }
private void HandleUnload() private void HandleUnload()
@ -305,7 +321,7 @@ namespace Acacia.Utils
private void HandleWrite(ref bool cancel) private void HandleWrite(ref bool cancel)
{ {
Logger.Instance.Debug(this, "HandleWrite: {0}", _id); Logger.Instance.Debug(this, "HandleWrite: {0}", _id);
_events.OnWrite(_item, ref cancel); _events.OnWrite(_itemEvents, ref cancel);
} }
} }