Skip to content

Commit

Permalink
Add non-disposal decorators for com wrappers
Browse files Browse the repository at this point in the history
This adds decorators for several com wrapper types that suppress disposal attempts on them.
This is relevant in two places. First, the wrappers handed out by the ProjectsRepository are references to cashed wrappers; only the repository itself should be allowed to dispose the instances when it clears its cache. Second, disposal of the com wrappers bound in CW is non-deterministic. In order to avoid running into concurrency issues on shutdown, CW should not be allowed to dispose the wrappers. This will than happen deterministically when disposing the com safe.
  • Loading branch information
MDoerner committed Nov 15, 2021
1 parent 7224620 commit 41299d3
Show file tree
Hide file tree
Showing 10 changed files with 608 additions and 12 deletions.
17 changes: 13 additions & 4 deletions Rubberduck.Main/Root/RubberduckIoCInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
using System.IO.Abstractions;
using Rubberduck.Navigation.CodeExplorer;
using Rubberduck.UI.Command.ComCommands;
using Rubberduck.VBEditor.ComManagement.NonDisposalDecorators;

namespace Rubberduck.Root
{
Expand Down Expand Up @@ -1156,16 +1157,24 @@ private void RegisterFileSystem(IWindsorContainer container)

private void RegisterInstances(IWindsorContainer container)
{
container.Register(Component.For<IVBE>().Instance(_vbe));
container.Register(Component.For<IAddIn>().Instance(_addin));
//note: This registration makes Castle Windsor inject _vbe_CommandBars in all ICommandBars Parent properties.
container.Register(Component.For<ICommandBars>().Instance(_vbe.CommandBars));
RegisterComWrapperInstances(container);

container.Register(Component.For<IUiContextProvider>().Instance(UiContextProvider.Instance()).LifestyleSingleton());
container.Register(Component.For<IVbeEvents>().Instance(VbeEvents.Initialize(_vbe)).LifestyleSingleton());
container.Register(Component.For<ITempSourceFileHandler>().Instance(_vbe.TempSourceFileHandler).LifestyleSingleton());
container.Register(Component.For<IPersistencePathProvider>().Instance(PersistencePathProvider.Instance).LifestyleSingleton());
container.Register(Component.For<IVbeNativeApi>().Instance(_vbeNativeApi).LifestyleSingleton());
container.Register(Component.For<IBeepInterceptor>().Instance(_beepInterceptor).LifestyleSingleton());
}

private void RegisterComWrapperInstances(IWindsorContainer container)
{
//note: We register safe com wrappers inside non-disposal decorators to ensure that the disposal is not executed by CW on some random thread.
//Instead, the disposal will happen via the ComSafe on the thread executing the add in termination.
container.Register(Component.For<IVBE>().Instance(new VbeNonDisposalDecorator<IVBE>(_vbe)));
container.Register(Component.For<IAddIn>().Instance(new AddInNonDisposalDecorator<IAddIn>(_addin)));
//note: This registration makes Castle Windsor inject _vbe_CommandBars in all ICommandBars Parent properties.
container.Register(Component.For<ICommandBars>().Instance(new CommandBarsNonDisposalDecorator<ICommandBars>(_vbe.CommandBars)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Generic;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;

namespace Rubberduck.VBEditor.ComManagement.NonDisposalDecorators
{
public class AddInNonDisposalDecorator<T> : NonDisposalDecoratorBase<T>, IAddIn
where T : IAddIn
{
public AddInNonDisposalDecorator(T addIn)
: base(addIn)
{ }

public bool Equals(IAddIn other)
{
return WrappedItem.Equals(other);
}

public string ProgId => WrappedItem.ProgId;

public string Guid => WrappedItem.Guid;

public string Description
{
get => WrappedItem.Description;
set => WrappedItem.Description = value;
}

public bool Connect
{
get => WrappedItem.Connect;
set => WrappedItem.Connect = value;
}

public object Object
{
get => WrappedItem.Object;
set => WrappedItem.Object = value;
}

public IVBE VBE => WrappedItem.VBE;

public IAddIns Collection => WrappedItem.Collection;

public IReadOnlyDictionary<CommandBarSite, CommandBarLocation> CommandBarLocations => WrappedItem.CommandBarLocations;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Collections;
using System.Collections.Generic;
using Rubberduck.VBEditor.SafeComWrappers;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;

namespace Rubberduck.VBEditor.ComManagement.NonDisposalDecorators
{
public class CommandBarsNonDisposalDecorator<T> : NonDisposalDecoratorBase<T>, ICommandBars
where T : ICommandBars
{
public CommandBarsNonDisposalDecorator(T commandBars)
: base(commandBars)
{ }

public IEnumerator<ICommandBar> GetEnumerator()
{
return WrappedItem.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable) WrappedItem).GetEnumerator();
}

public int Count => WrappedItem.Count;

public ICommandBar this[object index] => WrappedItem[index];

public ICommandBar Add(string name)
{
return WrappedItem.Add(name);
}

public ICommandBar Add(string name, CommandBarPosition position)
{
return WrappedItem.Add(name, position);
}

public ICommandBarControl FindControl(int id)
{
return WrappedItem.FindControl(id);
}

public ICommandBarControl FindControl(ControlType type, int id)
{
return WrappedItem.FindControl(type, id);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Rubberduck.VBEditor.SafeComWrappers.Abstract;

namespace Rubberduck.VBEditor.ComManagement.NonDisposalDecorators
{
/// <summary>
/// Decorator for SafeComWrappers to safely hand out references that must not be disposed
/// </summary>
/// <typeparam name="T">Concrete type of the safe com wrapper to decorate</typeparam>
public class NonDisposalDecoratorBase<T> : ISafeComWrapper
where T :ISafeComWrapper
{
protected readonly T WrappedItem;

public NonDisposalDecoratorBase(T wrappedItem)
{
WrappedItem = wrappedItem;
}

public object Target => WrappedItem.Target;
public bool IsWrappingNullReference => WrappedItem.IsWrappingNullReference;
public void Dispose()
{
//Do nothing
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using Rubberduck.VBEditor.SafeComWrappers;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;

namespace Rubberduck.VBEditor.ComManagement.NonDisposalDecorators
{
public class VBComponentNonDisposalDecorator<T> : NonDisposalDecoratorBase<T>, IVBComponent
where T : IVBComponent
{
public VBComponentNonDisposalDecorator(T component)
: base(component)
{ }

public bool Equals(IVBComponent other)
{
return WrappedItem.Equals(other);
}

public ComponentType Type => WrappedItem.Type;

public bool HasCodeModule => WrappedItem.HasCodeModule;

public ICodeModule CodeModule => WrappedItem.CodeModule;

public IVBE VBE => WrappedItem.VBE;

public IVBComponents Collection => WrappedItem.Collection;

public IProperties Properties => WrappedItem.Properties;

public IControls Controls => WrappedItem.Controls;

public IControls SelectedControls => WrappedItem.SelectedControls;

public bool IsSaved => WrappedItem.IsSaved;

public bool HasDesigner => WrappedItem.HasDesigner;

public bool HasOpenDesigner => WrappedItem.HasOpenDesigner;

public string DesignerId => WrappedItem.DesignerId;

public string Name
{
get => WrappedItem.Name;
set => WrappedItem.Name = value;
}

public IWindow DesignerWindow()
{
return WrappedItem.DesignerWindow();
}

public void Activate()
{
WrappedItem.Activate();
}

public void Export(string path)
{
WrappedItem.Export(path);
}

public string ExportAsSourceFile(string folder, bool isTempFile = false, bool specialCaseDocumentModules = true)
{
return WrappedItem.ExportAsSourceFile(folder, isTempFile, specialCaseDocumentModules);
}

public int FileCount => WrappedItem.FileCount;

public string GetFileName(short index)
{
return WrappedItem.GetFileName(index);
}

public IVBProject ParentProject => WrappedItem.ParentProject;

public int ContentHash()
{
return WrappedItem.ContentHash();
}

public QualifiedModuleName QualifiedModuleName => WrappedItem.QualifiedModuleName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Rubberduck.VBEditor.Events;
using Rubberduck.VBEditor.SafeComWrappers;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;

namespace Rubberduck.VBEditor.ComManagement.NonDisposalDecorators
{
public class VBComponentsNonDisposalDecorator<T> : NonDisposalDecoratorBase<T>, IVBComponents
where T : IVBComponents
{
public VBComponentsNonDisposalDecorator(T components)
: base(components)
{ }

public void AttachEvents()
{
WrappedItem.AttachEvents();
}

public void DetachEvents()
{
WrappedItem.DetachEvents();
}

public IEnumerator<IVBComponent> GetEnumerator()
{
return WrappedItem.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable) WrappedItem).GetEnumerator();
}

public int Count => WrappedItem.Count;

public event EventHandler<ComponentEventArgs> ComponentAdded
{
add => WrappedItem.ComponentAdded += value;
remove => WrappedItem.ComponentAdded -= value;
}

public event EventHandler<ComponentEventArgs> ComponentRemoved
{
add => WrappedItem.ComponentRemoved += value;
remove => WrappedItem.ComponentRemoved -= value;
}

public event EventHandler<ComponentRenamedEventArgs> ComponentRenamed
{
add => WrappedItem.ComponentRenamed += value;
remove => WrappedItem.ComponentRenamed -= value;
}

public event EventHandler<ComponentEventArgs> ComponentSelected
{
add => WrappedItem.ComponentSelected += value;
remove => WrappedItem.ComponentSelected -= value;
}

public event EventHandler<ComponentEventArgs> ComponentActivated
{
add => WrappedItem.ComponentActivated += value;
remove => WrappedItem.ComponentActivated -= value;
}

public event EventHandler<ComponentEventArgs> ComponentReloaded
{
add => WrappedItem.ComponentReloaded += value;
remove => WrappedItem.ComponentReloaded -= value;
}

public IVBComponent this[object index] => WrappedItem[index];

public IVBE VBE => WrappedItem.VBE;

public IVBProject Parent => WrappedItem.Parent;

public void Remove(IVBComponent item)
{
WrappedItem.Remove(item);
}

public IVBComponent Add(ComponentType type)
{
return WrappedItem.Add(type);
}

public IVBComponent Import(string path)
{
return WrappedItem.Import(path);
}

public IVBComponent AddCustom(string progId)
{
return WrappedItem.AddCustom(progId);
}

public IVBComponent ImportSourceFile(string path)
{
return WrappedItem.ImportSourceFile(path);
}

public void RemoveSafely(IVBComponent component)
{
WrappedItem.RemoveSafely(component);
}

public bool Equals(IVBComponents other)
{
return WrappedItem.Equals(other);
}
}
}
Loading

0 comments on commit 41299d3

Please sign in to comment.