From 9bc4b7488883298ff329045fc8a055dffe38bd69 Mon Sep 17 00:00:00 2001 From: Yeechan Lu Date: Thu, 27 Mar 2014 12:58:36 +0800 Subject: [PATCH] Add error report (however it has no sending feature at this commit) --- Wox/App.xaml.cs | 21 ++ Wox/Helper/ErrorReporting/ErrorReporting.cs | 294 ++++++++++++++++++ .../WPFErrorReportingDialog.xaml | 9 + .../WPFErrorReportingDialog.xaml.cs | 32 ++ Wox/MainWindow.xaml.cs | 19 -- Wox/Wox.csproj | 8 + 6 files changed, 364 insertions(+), 19 deletions(-) create mode 100644 Wox/Helper/ErrorReporting/ErrorReporting.cs create mode 100644 Wox/Helper/ErrorReporting/WPFErrorReportingDialog.xaml create mode 100644 Wox/Helper/ErrorReporting/WPFErrorReportingDialog.xaml.cs diff --git a/Wox/App.xaml.cs b/Wox/App.xaml.cs index b3a8d85588..478adc5b5a 100644 --- a/Wox/App.xaml.cs +++ b/Wox/App.xaml.cs @@ -2,12 +2,21 @@ using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Windows; +using System.Windows.Forms; +using System.Windows.Threading; using Microsoft.VisualBasic.ApplicationServices; +using Wox; using Wox.Commands; using Wox.Helper; +using Wox.Helper.ErrorReporting; +using Application = System.Windows.Application; +using MessageBox = System.Windows.MessageBox; +using MessageBoxOptions = System.Windows.Forms.MessageBoxOptions; using StartupEventArgs = System.Windows.StartupEventArgs; +using UnhandledExceptionEventArgs = System.UnhandledExceptionEventArgs; namespace Wox { @@ -16,6 +25,16 @@ namespace Wox [STAThread] public static void Main(string[] args) { + AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle; + System.Windows.Forms.Application.ThreadException += ErrorReporting.ThreadException; + + // don't combine Main and Entry since Microsoft.VisualBasic may be unable to load + // seperating them into two methods can make error reporting have the chance to catch exception + Entry(args); + } + + + private static void Entry(string[] args){ SingleInstanceManager manager = new SingleInstanceManager(); manager.Run(args); } @@ -66,6 +85,8 @@ namespace Wox protected override void OnStartup(StartupEventArgs e) { + this.DispatcherUnhandledException += ErrorReporting.DispatcherUnhandledException; + base.OnStartup(e); //for install plugin command when wox didn't start up diff --git a/Wox/Helper/ErrorReporting/ErrorReporting.cs b/Wox/Helper/ErrorReporting/ErrorReporting.cs new file mode 100644 index 0000000000..10ad4a6c0b --- /dev/null +++ b/Wox/Helper/ErrorReporting/ErrorReporting.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Threading; +using System.Xml; +using Microsoft.Win32; + +namespace Wox.Helper.ErrorReporting +{ + public static class ErrorReporting + { + public static void UnhandledExceptionHandle(object sender, System.UnhandledExceptionEventArgs e) + { + if (System.Diagnostics.Debugger.IsAttached) return; + + string error = CreateExceptionReport("System.AppDomain.UnhandledException", e.ExceptionObject); + + if (e.IsTerminating) + { + Log.Fatal(error); + TryShowErrorMessageBox(error, e.ExceptionObject, true); + Environment.Exit(0); + } + else + { + Log.Error(error); + } + } + public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) + { + if (System.Diagnostics.Debugger.IsAttached) return; + + e.Handled = true; + string error = CreateExceptionReport("System.Windows.Application.DispatcherUnhandledException", e.Exception); + + Log.Error(error); + if (TryShowErrorMessageBox(error, e.Exception, false)) + { + Environment.Exit(0); + } + } + public static void ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) + { + if (System.Diagnostics.Debugger.IsAttached) return; + + string error = CreateExceptionReport("System.Windows.Forms.Application.ThreadException", e.Exception); + + Log.Fatal(error); + TryShowErrorMessageBox(error, e.Exception, true); + Environment.Exit(0); + } + + private static string CreateExceptionReport(string ev, object exceptionObject) + { + var sb = new StringBuilder(); + sb.AppendLine("## Exception"); + sb.AppendLine(); + sb.AppendLine("```"); + + var ex = exceptionObject as Exception; + if (ex != null) + { + var exlist = new List(); + + while (ex != null) + { + var exsb = new StringBuilder(); + exsb.Append(ex.GetType().FullName); + exsb.Append(": "); + exsb.AppendLine(ex.Message); + if (ex.Source != null) + { + exsb.Append(" Source: "); + exsb.AppendLine(ex.Source); + } + if (ex.TargetSite != null) + { + exsb.Append(" TargetAssembly: "); + exsb.AppendLine(ex.TargetSite.Module.Assembly.ToString()); + exsb.Append(" TargetModule: "); + exsb.AppendLine(ex.TargetSite.Module.ToString()); + exsb.Append(" TargetSite: "); + exsb.AppendLine(ex.TargetSite.ToString()); + } + exsb.AppendLine(ex.StackTrace); + exlist.Add(exsb); + + ex = ex.InnerException; + } + + foreach (var result in exlist.Select(o => o.ToString()).Reverse()) + { + sb.AppendLine(result); + } + sb.AppendLine("```"); + sb.AppendLine(); + } + else + { + sb.AppendLine(exceptionObject.GetType().FullName); + sb.AppendLine(new System.Diagnostics.StackTrace().ToString()); + sb.AppendLine("```"); + sb.AppendLine(); + } + + sb.AppendLine("## Environment"); + sb.AppendLine(); + sb.Append("* Command Line: "); + sb.AppendLine(Environment.CommandLine); + sb.Append("* Exception Handle: "); + sb.AppendLine(ev); + sb.Append("* Timestamp: "); + sb.AppendLine(XmlConvert.ToString(DateTime.Now)); + sb.Append("* IntPtr Length: "); + sb.AppendLine(IntPtr.Size.ToString()); + sb.Append("* System Version: "); + sb.AppendLine(Environment.OSVersion.VersionString); + sb.Append("* CLR Version: "); + sb.AppendLine(Environment.Version.ToString()); + sb.AppendLine("* Installed .NET Framework: "); + foreach (var result in GetFrameworkVersionFromRegistry()) + { + sb.Append(" * "); + sb.AppendLine(result); + } + + sb.AppendLine(); + sb.AppendLine("## Assemblies - " + System.AppDomain.CurrentDomain.FriendlyName); + sb.AppendLine(); + foreach (var ass in System.AppDomain.CurrentDomain.GetAssemblies().OrderBy(o => o.GlobalAssemblyCache ? 100 : 0)) + { + sb.Append("* "); + sb.Append(ass.FullName); + sb.Append(" ("); + sb.Append(SyntaxSugars.CallOrRescueDefault(() => ass.Location, "not supported")); + sb.AppendLine(")"); + } + + var process = System.Diagnostics.Process.GetCurrentProcess(); + sb.AppendLine(); + sb.AppendLine("## Modules - " + process.ProcessName); + sb.AppendLine(); + foreach (ProcessModule mod in process.Modules) + { + sb.Append("* "); + sb.Append(mod.FileName); + sb.Append(" ("); + sb.Append(mod.FileVersionInfo.FileDescription); + sb.Append(", "); + sb.Append(mod.FileVersionInfo.FileVersion); + sb.Append(", "); + sb.Append(mod.FileVersionInfo.ProductName); + sb.Append(", "); + sb.Append(mod.FileVersionInfo.ProductVersion); + sb.Append(", "); + sb.Append(mod.FileVersionInfo.CompanyName); + sb.Append("), "); + sb.Append(string.Format("0x{0:X16}", mod.BaseAddress.ToInt64())); + sb.AppendLine(); + } + + sb.AppendLine(); + sb.AppendLine("## Threads - " + process.Threads.Count); + sb.AppendLine(); + foreach (ProcessThread th in process.Threads) + { + sb.Append("* "); + sb.AppendLine(string.Format("{0}, {1} {2}, Started: {3}, StartAddress: 0x{4:X16}", th.Id, th.ThreadState,th.PriorityLevel, th.StartTime, th.StartAddress.ToInt64())); + } + + return sb.ToString(); + } + + // http://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx + private static List GetFrameworkVersionFromRegistry() + { + try + { + var result = new List(); + using (RegistryKey ndpKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\")) + { + foreach (string versionKeyName in ndpKey.GetSubKeyNames()) + { + if (versionKeyName.StartsWith("v")) + { + RegistryKey versionKey = ndpKey.OpenSubKey(versionKeyName); + string name = (string)versionKey.GetValue("Version", ""); + string sp = versionKey.GetValue("SP", "").ToString(); + string install = versionKey.GetValue("Install", "").ToString(); + if (install != "") + if (sp != "" && install == "1") + result.Add(string.Format("{0} {1} SP{2}", versionKeyName, name, sp)); + else + result.Add(string.Format("{0} {1}", versionKeyName, name)); + + if (name != "") + { + continue; + } + foreach (string subKeyName in versionKey.GetSubKeyNames()) + { + RegistryKey subKey = versionKey.OpenSubKey(subKeyName); + name = (string)subKey.GetValue("Version", ""); + if (name != "") + sp = subKey.GetValue("SP", "").ToString(); + install = subKey.GetValue("Install", "").ToString(); + if (install != "") + { + if (sp != "" && install == "1") + result.Add(string.Format("{0} {1} {2} SP{3}", versionKeyName, subKeyName, name, sp)); + else if (install == "1") + result.Add(string.Format("{0} {1} {2}", versionKeyName, subKeyName, name)); + } + + } + + } + } + } + using (RegistryKey ndpKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\")) + { + int releaseKey = (int)ndpKey.GetValue("Release"); + { + if (releaseKey == 378389) + result.Add("v4.5"); + + if (releaseKey == 378675) + result.Add("v4.5.1 installed with Windows 8.1"); + + if (releaseKey == 378758) + result.Add("4.5.1 installed on Windows 8, Windows 7 SP1, or Windows Vista SP2"); + } + } + return result; + } + catch (Exception e) + { + return new List(); + } + + } + + private static bool TryShowErrorMessageBox(string error, object exceptionObject, bool isTerminating = true) + { + var title = "Wox - Unhandled Exception"; + + try + { + ShowWPFDialog(error, title, exceptionObject); + return true; + } + catch { } + + error = "Wox has occured an error that can't be handled. " + Environment.NewLine + Environment.NewLine + + error; + + try + { + ShowWPFMessageBox(error, title); + return true; + } + catch { } + + try + { + ShowWindowsFormsMessageBox(error, title); + return true; + } + catch { } + + return true; + } + + private static void ShowWPFDialog(string error, string title, object exceptionObject) + { + var dialog = new WPFErrorReportingDialog(error, title, exceptionObject); + dialog.ShowDialog(); + } + private static void ShowWPFMessageBox(string error, string title) + { + System.Windows.MessageBox.Show(error, title, MessageBoxButton.OK, MessageBoxImage.Error, + MessageBoxResult.OK, System.Windows.MessageBoxOptions.None); + } + private static void ShowWindowsFormsMessageBox(string error, string title) + { + System.Windows.Forms.MessageBox.Show(error, title, MessageBoxButtons.OK, + MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + } + } +} diff --git a/Wox/Helper/ErrorReporting/WPFErrorReportingDialog.xaml b/Wox/Helper/ErrorReporting/WPFErrorReportingDialog.xaml new file mode 100644 index 0000000000..54d0f04bd9 --- /dev/null +++ b/Wox/Helper/ErrorReporting/WPFErrorReportingDialog.xaml @@ -0,0 +1,9 @@ + + + + + + diff --git a/Wox/Helper/ErrorReporting/WPFErrorReportingDialog.xaml.cs b/Wox/Helper/ErrorReporting/WPFErrorReportingDialog.xaml.cs new file mode 100644 index 0000000000..f16669bd36 --- /dev/null +++ b/Wox/Helper/ErrorReporting/WPFErrorReportingDialog.xaml.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Wox.Helper.ErrorReporting +{ + /// + /// Interaction logic for WPFErrorReportingDialog.xaml + /// + public partial class WPFErrorReportingDialog : Window + { + private object exceptionObject; + + public WPFErrorReportingDialog(string error, string title, object exceptionObject) + { + InitializeComponent(); + + this.tbErrorReport.Text = error; + this.Title = title; + this.exceptionObject = exceptionObject; + } + } +} diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index 3c47eb06e6..a80ff383a4 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -56,7 +56,6 @@ namespace Wox this.AllowsTransparency = true; System.Net.WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); - AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; progressBar.ToolTip = toolTip; InitialTray(); @@ -104,24 +103,6 @@ namespace Wox WindowIntelopHelper.DisableControlBox(this); } - private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) - { - if (!System.Diagnostics.Debugger.IsAttached) - { - string error = "Wox has an error that can't be handled. " + e.ExceptionObject; - if (e.IsTerminating) - { - notifyIcon.Visible = false; - MessageBox.Show(error); - Log.Fatal(error); - } - else - { - Log.Error(error); - } - } - } - public void SetHotkey(string hotkeyStr, EventHandler action) { var hotkey = new HotkeyModel(hotkeyStr); diff --git a/Wox/Wox.csproj b/Wox/Wox.csproj index 9491bd5f6a..3ba8509bce 100644 --- a/Wox/Wox.csproj +++ b/Wox/Wox.csproj @@ -110,6 +110,10 @@ + + + WPFErrorReportingDialog.xaml + @@ -152,6 +156,10 @@ WebSearchSetting.xaml + + Designer + MSBuild:Compile + MSBuild:Compile Designer