Skip to content

Commit 8f05c01

Browse files
MDoernerretailcoder
authored andcommitted
First step towards clean exit (#3277)
This PR is a first step towards a clean exist; it fixes one reason of the crash on exit: we have not been unregistering the Click events from the CommandBarButtons, neither in the command bar nor in the menus. This leads to a fatal error on shutdown. This PR changes the CommandBarControls to be added permanently and to be removed explicitly on shutdown, removing the Click handlers as well. Moreover, as a safety measure, the GC gets invoked at the end of the shutdown. There are more problematic sections I have identified and I am looking into. However, although there are still plenty of COM access violations on shutdown, I get a clean exit in Excel in production now, but not when debugging. ref. #3213 * Make menus and the command bar permanent and clean them up on shutdown. * Fix event handler registration in CommandBarButton. * Fixed RemoveChildren on the parent menus and the command bar. * Add garbage collection on shutdown. * Rewired deleting the command bar and menus. * Fixed three tests with illegal code as input.
1 parent e8eb2f7 commit 8f05c01

File tree

13 files changed

+78
-48
lines changed

13 files changed

+78
-48
lines changed

RetailCoder.VBE/AppMenu.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,15 @@ public void Dispose()
7070
_parser.State.StateChanged -= OnParserStateChanged;
7171
_selectionService.SelectedDeclarationChanged -= OnSelectedDeclarationChange;
7272

73-
// note: doing this wrecks the teardown process. counter-intuitive? sure. but hey it works.
74-
//foreach (var menu in _menus.Where(menu => menu.Item != null))
75-
//{
76-
// menu.RemoveChildren();
77-
// menu.Item.Delete();
78-
//}
73+
RemoveMenus();
74+
}
75+
76+
private void RemoveMenus()
77+
{
78+
foreach (var menu in _menus.Where(menu => menu.Item != null))
79+
{
80+
menu.RemoveMenu();
81+
}
7982
}
8083
}
8184
}

RetailCoder.VBE/Extension.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,16 @@ private void ShutdownAddIn()
243243
_kernel = null;
244244
}
245245

246+
Debug.WriteLine("Extension: Initiating garbage collection.");
247+
248+
GC.Collect();
249+
250+
Debug.WriteLine("Extension: Initiated garbage collection.");
251+
252+
GC.WaitForPendingFinalizers();
253+
254+
Debug.WriteLine("Extension: Finalizers have run.");
255+
246256
_isInitialized = false;
247257
}
248258

RetailCoder.VBE/UI/Command/MenuItems/CommandBars/AppCommandBarBase.cs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -109,41 +109,39 @@ public void EvaluateCanExecute(RubberduckParserState state)
109109

110110
public ICommandBars Parent { get; set; }
111111
public ICommandBar Item { get; private set; }
112-
public void RemoveChildren()
112+
113+
public void RemoveCommandBar()
114+
{
115+
Logger.Debug("Removing commandbar.");
116+
RemoveChildren();
117+
Item?.Delete();
118+
Item = null;
119+
Parent = null;
120+
}
121+
122+
private void RemoveChildren()
113123
{
114-
Logger.Debug("Removing child controls from commandbar.");
115124
if (Parent == null || Parent.IsWrappingNullReference)
116125
{
117126
return;
118127
}
119128

120129
try
121130
{
122-
foreach (var child in _items.Values.Select(item => item as CommandBarButton).Where(child => child != null))
131+
foreach (var button in _items.Values.Select(item => item as ICommandBarButton).Where(child => child != null))
123132
{
124-
var button = Parent.FindControl(child.Id);
125-
if (button is ICommandBarButton && !button.IsWrappingNullReference)
133+
if (!button.IsWrappingNullReference)
126134
{
127-
(button as ICommandBarButton).Click -= child_Click;
135+
button.Click -= child_Click;
128136
}
129137
button.Delete();
130138
}
131139
}
132140
catch (COMException exception)
133141
{
134-
/*
135-
Application: EXCEL.EXE
136-
Framework Version: v4.0.30319
137-
Description: The process was terminated due to an unhandled exception.
138-
Exception Info: System.Runtime.InteropServices.COMException
139-
at Microsoft.Office.Core.CommandBarControl.get_Parent()
140-
at Rubberduck.VBEditor.SafeComWrappers.Office.Core.CommandBarControl.get_Parent()
141-
at Rubberduck.VBEditor.SafeComWrappers.Office.Core.CommandBarButton.remove_Click(System.EventHandler`1<Rubberduck.VBEditor.SafeComWrappers.Office.Core.CommandBarButtonClickEventArgs>)
142-
at Rubberduck.UI.Command.MenuItems.CommandBars.AppCommandBarBase.RemoveChildren()
143-
at Rubberduck.UI.Command.MenuItems.CommandBars.RubberduckCommandBar.Dispose()
144-
*/
145142
Logger.Error(exception, "Error removing child controls from commandbar.");
146143
}
144+
_items.Clear();
147145
}
148146

149147
// note: HAAAAACK!!!

RetailCoder.VBE/UI/Command/MenuItems/CommandBars/IAppCommandBar.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ public interface IAppCommandBar : IAppMenu
66
{
77
ICommandBars Parent { get; set; }
88
ICommandBar Item { get; }
9-
void RemoveChildren();
9+
void RemoveCommandBar();
1010
}
1111
}

RetailCoder.VBE/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,7 @@ public void Dispose()
138138
_parser.State.StateChanged -= OnParserStateChanged;
139139
_parser.State.StatusMessageUpdate -= OnParserStatusMessageUpdate;
140140

141-
//note: doing this wrecks the teardown process. counter-intuitive? sure. but hey it works.
142-
//RemoveChildren();
143-
//Item.Delete();
144-
//Item.Release(true);
141+
RemoveCommandBar();
145142
}
146143
}
147144

RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/IParentMenuItem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ public interface IParentMenuItem : IMenuItem, IAppMenu
66
{
77
ICommandBarControls Parent { get; set; }
88
ICommandBarPopup Item { get; }
9-
void RemoveChildren();
9+
void RemoveMenu();
1010
}
1111
}

RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Globalization;
45
using System.Linq;
56
using Rubberduck.Parsing.VBA;
@@ -88,19 +89,24 @@ public void Initialize()
8889
EvaluateCanExecute(null);
8990
}
9091

91-
public void RemoveChildren()
92+
public void RemoveMenu()
93+
{
94+
Logger.Debug($"Removing menu {_key}.");
95+
RemoveChildren();
96+
Item?.Delete();
97+
Item = null;
98+
}
99+
100+
private void RemoveChildren()
92101
{
93102
foreach (var child in _items.Keys.Select(item => item as IParentMenuItem).Where(child => child != null))
94103
{
95-
child.RemoveChildren();
96-
//var item = _items[child];
97-
//Debug.Assert(item is CommandBarPopup);
98-
//(item as CommandBarPopup).Delete();
104+
child.RemoveMenu();
99105
}
100-
foreach (var child in _items.Values.Select(item => item as CommandBarButton).Where(child => child != null))
106+
foreach (var child in _items.Values.Select(item => item as ICommandBarButton).Where(child => child != null))
101107
{
102108
child.Click -= child_Click;
103-
//child.Delete();
109+
child.Delete();
104110
}
105111
}
106112

Rubberduck.Parsing/VBA/ParseCoordinator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ private void ParseAll(object requestor, CancellationToken token)
320320
if (watch != null && watch.IsRunning) watch.Stop();
321321
if (lockTaken) Monitor.Exit(_parsingRunSyncObject);
322322
}
323-
if (watch != null) Logger.Debug("Parsing run finished after {0}s. (thread {1}).", watch.Elapsed.Seconds, Thread.CurrentThread.ManagedThreadId);
323+
if (watch != null) Logger.Debug("Parsing run finished after {0}s. (thread {1}).", watch.Elapsed.TotalSeconds, Thread.CurrentThread.ManagedThreadId);
324324
}
325325

326326
private void ParseAllInternal(object requestor, CancellationToken token)

Rubberduck.VBEEditor/SafeComWrappers/Office.Core/CommandBarButton.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,27 @@ public event EventHandler<CommandBarButtonClickEventArgs> Click
3131
{
3232
add
3333
{
34-
((Microsoft.Office.Core.CommandBarButton)Target).Click += Target_Click;
34+
if (_clickHandler == null)
35+
{
36+
((Microsoft.Office.Core.CommandBarButton)Target).Click += Target_Click;
37+
}
3538
_clickHandler += value;
3639
System.Diagnostics.Debug.WriteLine("Added handler for: {0} '{1}' (tag: {2}, hashcode:{3})", Parent.Name, Target.Caption, Tag, Target.GetHashCode());
3740
}
3841
remove
3942
{
43+
_clickHandler -= value;
4044
try
4145
{
42-
((Microsoft.Office.Core.CommandBarButton)Target).Click -= Target_Click;
46+
if (_clickHandler == null)
47+
{
48+
((Microsoft.Office.Core.CommandBarButton)Target).Click -= Target_Click;
49+
}
4350
}
4451
catch
4552
{
4653
// he's gone, dave.
4754
}
48-
_clickHandler -= value;
4955
System.Diagnostics.Debug.WriteLine("Removed handler for: {0} '{1}' (tag: {2}, hashcode:{3})", Parent.GetType().Name, Target.Caption, Tag, Target.GetHashCode());
5056
}
5157
}

Rubberduck.VBEEditor/SafeComWrappers/Office.Core/CommandBarControl.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ namespace Rubberduck.VBEditor.SafeComWrappers.Office.Core
66
{
77
public class CommandBarControl : SafeComWrapper<Microsoft.Office.Core.CommandBarControl>, ICommandBarControl
88
{
9+
public const bool AddCommandBarControlsTemporarily = false;
10+
911
public CommandBarControl(Microsoft.Office.Core.CommandBarControl target)
1012
: base(target)
1113
{
@@ -125,7 +127,7 @@ public bool IsPriorityDropped
125127

126128
public void Delete()
127129
{
128-
if (!IsWrappingNullReference) Target.Delete(true);
130+
if (!IsWrappingNullReference) Target.Delete(AddCommandBarControlsTemporarily);
129131
}
130132

131133
public void Execute()

0 commit comments

Comments
 (0)