From 04c5e7560bc1eb5c7f3838f27a6263aaf85f0173 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 22 Feb 2013 17:18:31 +0100 Subject: [PATCH] Use custom dialog for assertion failures. The new dialog allows viewing the full stack trace; and ignoring all occurrences of a given assertion. --- .../Gui/Dialogs/SharpDevelopAboutPanels.cs | 3 + .../MessageService/CustomDialog.cs | 2 +- src/Main/SharpDevelop/Logging/ExceptionBox.cs | 21 ++--- .../SharpDevelop/Logging/SDTraceListener.cs | 82 +++++++++++++++++++ src/Main/SharpDevelop/SharpDevelop.csproj | 1 + .../SharpDevelop/Startup/SharpDevelopMain.cs | 1 + 6 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 src/Main/SharpDevelop/Logging/SDTraceListener.cs diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/SharpDevelopAboutPanels.cs b/src/Main/Base/Project/Src/Gui/Dialogs/SharpDevelopAboutPanels.cs index d5adc20479..fb8830ca30 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/SharpDevelopAboutPanels.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/SharpDevelopAboutPanels.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; @@ -79,6 +80,8 @@ namespace ICSharpCode.SharpDevelop.Gui #if DEBUG if (e.KeyData == (Keys.Control | Keys.Shift | Keys.E)) { throw new ClownFishException(); + } else if (e.KeyData == (Keys.Control | Keys.Shift | Keys.A)) { + Trace.Fail("Trace failure"); } else if (e.KeyData == (Keys.Control | Keys.Shift | Keys.G)) { GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); diff --git a/src/Main/ICSharpCode.Core.WinForms/MessageService/CustomDialog.cs b/src/Main/ICSharpCode.Core.WinForms/MessageService/CustomDialog.cs index 41d770a025..9be4687eb3 100644 --- a/src/Main/ICSharpCode.Core.WinForms/MessageService/CustomDialog.cs +++ b/src/Main/ICSharpCode.Core.WinForms/MessageService/CustomDialog.cs @@ -7,7 +7,7 @@ using System.Windows.Forms; namespace ICSharpCode.Core.WinForms { - sealed class CustomDialog : System.Windows.Forms.Form + public sealed class CustomDialog : System.Windows.Forms.Form { System.Windows.Forms.Label label; System.Windows.Forms.Panel panel; diff --git a/src/Main/SharpDevelop/Logging/ExceptionBox.cs b/src/Main/SharpDevelop/Logging/ExceptionBox.cs index 680d442621..3527b55694 100644 --- a/src/Main/SharpDevelop/Logging/ExceptionBox.cs +++ b/src/Main/SharpDevelop/Logging/ExceptionBox.cs @@ -76,18 +76,17 @@ namespace ICSharpCode.SharpDevelop.Logging [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] static void ShowErrorBox(Exception exception, string message, bool mustTerminate) { - if (exception == null) - throw new ArgumentNullException("exception"); - // ignore reentrant calls (e.g. when there's an exception in OnRender) if (showingBox) return; showingBox = true; try { - try { - SD.AnalyticsMonitor.TrackException(exception); - } catch (Exception ex) { - LoggingService.Warn("Error tracking exception", ex); + if (exception != null) { + try { + SD.AnalyticsMonitor.TrackException(exception); + } catch (Exception ex) { + LoggingService.Warn("Error tracking exception", ex); + } } using (ExceptionBox box = new ExceptionBox(exception, message, mustTerminate)) { if (SD.MainThread.InvokeRequired) { @@ -98,7 +97,7 @@ namespace ICSharpCode.SharpDevelop.Logging } } catch (Exception ex) { LoggingService.Warn("Error showing ExceptionBox", ex); - MessageBox.Show(exception.ToString(), message, MessageBoxButtons.OK, + MessageBox.Show(exception != null ? exception.ToString() : "Error", message, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); } finally { showingBox = false; @@ -155,8 +154,10 @@ namespace ICSharpCode.SharpDevelop.Logging if (message != null) { sb.AppendLine(message); } - sb.AppendLine("Exception thrown:"); - sb.AppendLine(exceptionThrown.ToString()); + if (exceptionThrown != null) { + sb.AppendLine("Exception thrown:"); + sb.AppendLine(exceptionThrown.ToString()); + } sb.AppendLine(); sb.AppendLine("---- Recent log messages:"); try { diff --git a/src/Main/SharpDevelop/Logging/SDTraceListener.cs b/src/Main/SharpDevelop/Logging/SDTraceListener.cs new file mode 100644 index 0000000000..1063556b1b --- /dev/null +++ b/src/Main/SharpDevelop/Logging/SDTraceListener.cs @@ -0,0 +1,82 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net.Mime; +using System.Threading; +using System.Windows; +using System.Windows.Threading; +using ICSharpCode.Core.WinForms; + +namespace ICSharpCode.SharpDevelop.Logging +{ + sealed class SDTraceListener : DefaultTraceListener + { + [Conditional("DEBUG")] + public static void Install() + { + Debug.Listeners.Clear(); + Debug.Listeners.Add(new SDTraceListener()); + } + + public SDTraceListener() + { + base.AssertUiEnabled = false; + } + + HashSet ignoredStacks = new HashSet(); + bool dialogIsOpen; + + public override void Fail(string message) + { + this.Fail(message, null); + } + + public override void Fail(string message, string detailMessage) + { + base.Fail(message, detailMessage); // let base class write the assert to the debug console + if (dialogIsOpen) + return; + string stackTrace = ""; + try { + stackTrace = new StackTrace(true).ToString(); + } catch {} + lock (ignoredStacks) { + if (ignoredStacks.Contains(stackTrace)) + return; + } + message = message + Environment.NewLine + detailMessage + Environment.NewLine + stackTrace; + dialogIsOpen = true; + CustomDialog inputBox = new CustomDialog("Assertion Failed", message.TakeStartEllipsis(750), -1, 2, new[] { "Show Stacktrace", "Debug", "Ignore", "Ignore All" }); + try { + while (true) { // show the dialog repeatedly until an option other than 'Show Stacktrace' is selected + if (SD.MainThread.InvokeRequired) { + inputBox.ShowDialog(); + } else { + inputBox.ShowDialog(SD.WinForms.MainWin32Window); + } + switch (inputBox.Result) { + case 0: + ExceptionBox.ShowErrorBox(null, message); + break; // show the custom dialog again + case 1: + Debugger.Break(); + return; + case 2: + return; + case 3: + lock (ignoredStacks) { + ignoredStacks.Add(stackTrace); + } + return; + } + } + } finally { + dialogIsOpen = false; + inputBox.Dispose(); + } + } + } +} diff --git a/src/Main/SharpDevelop/SharpDevelop.csproj b/src/Main/SharpDevelop/SharpDevelop.csproj index bcaf1639fb..20b4592d6d 100644 --- a/src/Main/SharpDevelop/SharpDevelop.csproj +++ b/src/Main/SharpDevelop/SharpDevelop.csproj @@ -91,6 +91,7 @@ + LoadSaveOptions.xaml Code diff --git a/src/Main/SharpDevelop/Startup/SharpDevelopMain.cs b/src/Main/SharpDevelop/Startup/SharpDevelopMain.cs index dae2eb46ee..23e3480a71 100644 --- a/src/Main/SharpDevelop/Startup/SharpDevelopMain.cs +++ b/src/Main/SharpDevelop/Startup/SharpDevelopMain.cs @@ -207,6 +207,7 @@ namespace ICSharpCode.SharpDevelop.Startup for (int i = 0; i < fileList.Length; i++) { workbenchSettings.InitialFileList.Add(fileList[i]); } + SDTraceListener.Install(); host.RunWorkbench(workbenchSettings); } finally { LoggingService.Info("Leaving RunApplication()");