Skip to content

Commit 286b55c

Browse files
committed
Add more high level exception handling
The async void responsible for refreshing the inspection results now has a to level exception handler swallowing all exceptions. This is necessary since bubbling out of there will likely take down the host. Moreover, we now log and swallow COM exceptions coming from the process of changing the status label of the refresh button. This means that COM registration issues for the command bar controls will no abort the calling process. The only effect will be that the label does not change.
1 parent 0c4bb18 commit 286b55c

File tree

2 files changed

+53
-19
lines changed

2 files changed

+53
-19
lines changed

Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Globalization;
44
using System.Linq;
5+
using System.Runtime.InteropServices;
56
using Rubberduck.Resources;
67
using Rubberduck.Parsing;
78
using Rubberduck.Parsing.Symbols;
@@ -99,30 +100,49 @@ public void SetStatusLabelCaption(ParserState state, int? errorCount = null)
99100

100101
private void SetStatusLabelCaption(string caption, int? errorCount = null)
101102
{
102-
var reparseCommandButton = FindChildByTag(typeof(ReparseCommandMenuItem).FullName) as ReparseCommandMenuItem;
103-
if (reparseCommandButton == null) { return; }
104-
105-
var showErrorsCommandButton = FindChildByTag(typeof(ShowParserErrorsCommandMenuItem).FullName) as ShowParserErrorsCommandMenuItem;
106-
if (showErrorsCommandButton == null) { return; }
107-
108-
_uiDispatcher.Invoke(() =>
103+
//This try-catch block guarantees that problems with the COM registration of the command bar classes
104+
//only affect the status label text instead of aborting the status change or even crashing the host.
105+
//See issue #5349 at https://github.com/rubberduck-vba/Rubberduck/issues/5349
106+
try
109107
{
110-
try
108+
var reparseCommandButton =
109+
FindChildByTag(typeof(ReparseCommandMenuItem).FullName) as ReparseCommandMenuItem;
110+
if (reparseCommandButton == null)
111111
{
112-
reparseCommandButton.SetCaption(caption);
113-
reparseCommandButton.SetToolTip(string.Format(RubberduckUI.ReparseToolTipText, caption));
114-
if (errorCount.HasValue && errorCount.Value > 0)
115-
{
116-
showErrorsCommandButton.SetToolTip(
117-
string.Format(RubberduckUI.ParserErrorToolTipText, errorCount.Value));
118-
}
112+
return;
119113
}
120-
catch (Exception exception)
114+
115+
var showErrorsCommandButton =
116+
FindChildByTag(typeof(ShowParserErrorsCommandMenuItem).FullName) as ShowParserErrorsCommandMenuItem;
117+
if (showErrorsCommandButton == null)
121118
{
122-
Logger.Error(exception, "Exception thrown trying to set the status label caption on the UI thread.");
119+
return;
123120
}
124-
});
125-
Localize();
121+
122+
_uiDispatcher.Invoke(() =>
123+
{
124+
try
125+
{
126+
reparseCommandButton.SetCaption(caption);
127+
reparseCommandButton.SetToolTip(string.Format(RubberduckUI.ReparseToolTipText, caption));
128+
if (errorCount.HasValue && errorCount.Value > 0)
129+
{
130+
showErrorsCommandButton.SetToolTip(
131+
string.Format(RubberduckUI.ParserErrorToolTipText, errorCount.Value));
132+
}
133+
}
134+
catch (Exception exception)
135+
{
136+
Logger.Error(exception,
137+
"Exception thrown trying to set the status label caption on the UI thread.");
138+
}
139+
});
140+
Localize();
141+
}
142+
catch (COMException exception)
143+
{
144+
Logger.Error(exception, "COMException thrown trying to set the status label caption.");
145+
}
126146
}
127147

128148
private void SetContextSelectionCaption(string caption, int contextReferenceCount, string description)

Rubberduck.Core/UI/Inspections/InspectionResultsViewModel.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,20 @@ private void HandleStateChanged(object sender, ParserStateEventArgs e)
428428
}
429429

430430
private async void RefreshInspections(CancellationToken token)
431+
{
432+
//We have to catch all exceptions here since this method is a fire-and-forget async action.
433+
//Accordingly, any exception bubbling out of this method will likely take down the runtime and, thus, crash the host.
434+
try
435+
{
436+
await RefreshInspections_Internal(token);
437+
}
438+
catch (Exception ex)
439+
{
440+
Logger.Error(ex,"Unhandled exception when refreshing inspection results.");
441+
}
442+
}
443+
444+
private async Task RefreshInspections_Internal(CancellationToken token)
431445
{
432446
var stopwatch = Stopwatch.StartNew();
433447
IsBusy = true;

0 commit comments

Comments
 (0)