From cfa155abce6eee38e886e0bf7483f9eadd4b5120 Mon Sep 17 00:00:00 2001 From: Daniel Walder Date: Wed, 24 Jul 2013 22:41:29 +1000 Subject: [PATCH] 0.1.0.1 - Proper exception logging - Update check on startup --- Sledge.Common/Mediator/Mediator.cs | 31 ++- Sledge.Editor.Updater/UpdaterForm.cs | 3 +- Sledge.Editor/Documents/Document.cs | 19 +- Sledge.Editor/Editor.Designer.cs | 5 + Sledge.Editor/Editor.cs | 112 +++++++++ .../Logging/ExceptionWindow.Designer.cs | 234 ++++++++++++++++++ Sledge.Editor/Logging/ExceptionWindow.cs | 64 +++++ Sledge.Editor/Logging/ExceptionWindow.resx | 131 ++++++++++ Sledge.Editor/Logging/Logger.cs | 76 ++++++ Sledge.Editor/Milestones.txt | 1 - Sledge.Editor/Program.cs | 33 +++ Sledge.Editor/Sledge.Editor.csproj | 10 + Sledge.Editor/UI/ViewportManager.cs | 8 + Sledge.UI/ViewportBase.cs | 98 ++++++-- 14 files changed, 805 insertions(+), 20 deletions(-) create mode 100644 Sledge.Editor/Logging/ExceptionWindow.Designer.cs create mode 100644 Sledge.Editor/Logging/ExceptionWindow.cs create mode 100644 Sledge.Editor/Logging/ExceptionWindow.resx create mode 100644 Sledge.Editor/Logging/Logger.cs diff --git a/Sledge.Common/Mediator/Mediator.cs b/Sledge.Common/Mediator/Mediator.cs index 9b78ff1f4..cea9a8574 100644 --- a/Sledge.Common/Mediator/Mediator.cs +++ b/Sledge.Common/Mediator/Mediator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; @@ -10,6 +11,24 @@ namespace Sledge.Common.Mediator /// public static class Mediator { + public delegate void MediatorExceptionEventHandler(string message, Exception exception); + public static event MediatorExceptionEventHandler MediatorException; + private static void OnMediatorException(string message, object parameter, Exception ex) + { + if (MediatorException != null) + { + var st = new StackTrace(); + var frames = st.GetFrames() ?? new StackFrame[0]; + var msg = "Mediator exception: " + message + "(" + parameter + ")"; + foreach (var frame in frames) + { + var method = frame.GetMethod(); + msg += "\r\n " + method.ReflectedType.FullName + "." + method.Name; + } + MediatorException(message, new Exception(msg, ex)); + } + } + /// /// Helper method to execute the a function with the same name as the message. Called by the listener if desired. /// @@ -99,7 +118,17 @@ public static void Publish(string message, object parameter = null) else if (reference.Target != null) { var method = reference.Target.GetType().GetMethod("Notify", new[] { typeof(string), typeof(object) }); - if (method != null) method.Invoke(reference.Target, new object[] { message, parameter }); + if (method != null) + { + try + { + method.Invoke(reference.Target, new[] { message, parameter }); + } + catch (Exception ex) + { + OnMediatorException(message, parameter, ex); + } + } } } } diff --git a/Sledge.Editor.Updater/UpdaterForm.cs b/Sledge.Editor.Updater/UpdaterForm.cs index 0cc757d51..5a21d6411 100644 --- a/Sledge.Editor.Updater/UpdaterForm.cs +++ b/Sledge.Editor.Updater/UpdaterForm.cs @@ -79,7 +79,8 @@ private void DoUpdate() { InstallUpdate(directory, download); Directory.Delete(updateDirectory, true); - Close(); + Process.Start(Path.Combine(directory, "Sledge.Editor.exe")); + Application.Exit(); }); } return; diff --git a/Sledge.Editor/Documents/Document.cs b/Sledge.Editor/Documents/Document.cs index 6722e74cf..a9b18188f 100644 --- a/Sledge.Editor/Documents/Document.cs +++ b/Sledge.Editor/Documents/Document.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using OpenTK; using Sledge.Common.Mediator; @@ -133,7 +134,23 @@ public Coordinate Snap(Coordinate c, decimal spacing = 0) /// The action to perform public void PerformAction(string name, IAction action) { - action.Perform(this); + try + { + action.Perform(this); + } + catch (Exception ex) + { + var st = new StackTrace(); + var frames = st.GetFrames() ?? new StackFrame[0]; + var msg = "Action exception: " + name + " (" + action + ")"; + foreach (var frame in frames) + { + var method = frame.GetMethod(); + msg += "\r\n " + method.ReflectedType.FullName + "." + method.Name; + } + Logging.Logger.ShowException(new Exception(msg, ex), "Error performing action"); + } + var history = new HistoryAction(name, action); History.AddHistoryItem(history); } diff --git a/Sledge.Editor/Editor.Designer.cs b/Sledge.Editor/Editor.Designer.cs index 10561eaef..0a30dad69 100644 --- a/Sledge.Editor/Editor.Designer.cs +++ b/Sledge.Editor/Editor.Designer.cs @@ -245,6 +245,8 @@ private void InitializeComponent() this.tblQuadView.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblQuadView.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblQuadView.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tblQuadView.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tblQuadView.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblQuadView.Dock = System.Windows.Forms.DockStyle.Fill; this.tblQuadView.Location = new System.Drawing.Point(0, 0); this.tblQuadView.MinimumViewSize = 2; @@ -318,6 +320,8 @@ private void InitializeComponent() this.tblQuadView.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblQuadView.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblQuadView.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tblQuadView.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tblQuadView.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblQuadView.Size = new System.Drawing.Size(682, 801); this.tblQuadView.TabIndex = 0; // @@ -645,6 +649,7 @@ private void InitializeComponent() this.WindowState = System.Windows.Forms.FormWindowState.Maximized; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.EditorClosing); this.Load += new System.EventHandler(this.EditorLoad); + this.Shown += new System.EventHandler(this.EditorShown); this.stsStatus.ResumeLayout(false); this.stsStatus.PerformLayout(); this.tscToolStrip.ContentPanel.ResumeLayout(false); diff --git a/Sledge.Editor/Editor.cs b/Sledge.Editor/Editor.cs index 667ae1797..64dea6040 100644 --- a/Sledge.Editor/Editor.cs +++ b/Sledge.Editor/Editor.cs @@ -1,6 +1,10 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; +using System.IO; using System.Linq; +using System.Net; using System.Reflection; using System.Windows.Forms; using Microsoft.WindowsAPICodePack.Taskbar; @@ -21,6 +25,7 @@ using Sledge.Settings; using Sledge.Settings.Models; using Hotkeys = Sledge.Editor.UI.Hotkeys; +using Path = Sledge.DataStructures.MapObjects.Path; namespace Sledge.Editor { @@ -110,8 +115,103 @@ private void EditorLoad(object sender, EventArgs e) //TexturePackage.LoadTextureData(TexturePackage.GetLoadedItems().Select(x => x.Name)); Subscribe(); + + Mediator.MediatorException += (msg, ex) => Logging.Logger.ShowException(ex, "Mediator Error: " + msg); + } + + #region Updates + + private void CheckForUpdates() + { + #if DEBUG + return; + #endif + + var sources = GetUpdateSources(); + var version = GetCurrentVersion(); + foreach (var source in sources) + { + var result = GetUpdateCheckResult(source, version); + if (result == null) continue; + if (!String.Equals(result.Version, version, StringComparison.InvariantCultureIgnoreCase)) + { + if (MessageBox.Show("An update is available, would you like to update now?", "New version detected!", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + Process.Start(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(typeof(Editor).Assembly.Location), "Sledge.Editor.Updater.exe")); + Application.Exit(); + } + } + return; + } + } + + private class UpdateSource + { + public string Name { get; set; } + public string Url { get; set; } + + public string GetUrl(string version) + { + return String.Format(Url, version); + } + } + + private class UpdateCheckResult + { + public string Version { get; set; } + public DateTime Date { get; set; } + public string DownloadUrl { get; set; } + } + + private IEnumerable GetUpdateSources() + { + var dir = System.IO.Path.GetDirectoryName(typeof(Editor).Assembly.Location); + if (dir == null) yield break; + var file = System.IO.Path.Combine(dir, "UpdateSources.txt"); + if (!File.Exists(file)) yield break; + var lines = File.ReadAllLines(file); + foreach (var line in lines) + { + if (String.IsNullOrWhiteSpace(line) || line.StartsWith("#")) continue; + var split = line.Split(':'); + if (split.Length < 2) continue; + var us = new UpdateSource + { + Name = split[0], + Url = String.Join(":", split.Skip(1)) + }; + yield return us; + } } + private String GetCurrentVersion() + { + var info = FileVersionInfo.GetVersionInfo(typeof(Editor).Assembly.Location); + return info.FileVersion; + } + + private UpdateCheckResult GetUpdateCheckResult(UpdateSource source, string version) + { + try + { + using (var downloader = new WebClient()) + { + var str = downloader.DownloadString(source.GetUrl(version)).Split('\n', '\r'); + if (str.Length < 3 || String.IsNullOrWhiteSpace(str[0])) + { + return null; + } + return new UpdateCheckResult { Version = str[0], Date = DateTime.Parse(str[1]), DownloadUrl = str[2] }; + } + } + catch + { + return null; + } + } + + #endregion + private void EditorClosing(object sender, FormClosingEventArgs e) { if (DocumentManager.CurrentDocument != null) @@ -164,6 +264,13 @@ private void Subscribe() Mediator.Subscribe(EditorMediator.TextureSelected, this); Mediator.Subscribe(EditorMediator.ToolSelected, this); + + Mediator.Subscribe(EditorMediator.About, this); + } + + private void About() + { + throw new Exception("THIS IS AN EXCEPTION!"); } public static void FileNew() @@ -480,5 +587,10 @@ private void MoveToEntityClicked(object sender, EventArgs e) { Mediator.Publish(HotkeysMediator.TieToEntity); } + + private void EditorShown(object sender, EventArgs e) + { + System.Threading.Tasks.Task.Factory.StartNew(CheckForUpdates); + } } } diff --git a/Sledge.Editor/Logging/ExceptionWindow.Designer.cs b/Sledge.Editor/Logging/ExceptionWindow.Designer.cs new file mode 100644 index 000000000..b3227db22 --- /dev/null +++ b/Sledge.Editor/Logging/ExceptionWindow.Designer.cs @@ -0,0 +1,234 @@ +namespace Sledge.Editor.Logging +{ + partial class ExceptionWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ExceptionWindow)); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.FrameworkVersion = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.OperatingSystem = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.SledgeVersion = new System.Windows.Forms.TextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.FullError = new System.Windows.Forms.TextBox(); + this.label8 = new System.Windows.Forms.Label(); + this.InfoTextBox = new System.Windows.Forms.TextBox(); + this.SubmitButton = new System.Windows.Forms.Button(); + this.CancelButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(411, 25); + this.label1.TabIndex = 0; + this.label1.Text = "Oops! Something went horribly wrong."; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(14, 45); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(431, 117); + this.label2.TabIndex = 1; + this.label2.Text = resources.GetString("label2.Text"); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(14, 180); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(70, 13); + this.label4.TabIndex = 2; + this.label4.Text = ".NET Version"; + // + // FrameworkVersion + // + this.FrameworkVersion.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.FrameworkVersion.Enabled = false; + this.FrameworkVersion.Location = new System.Drawing.Point(184, 177); + this.FrameworkVersion.Name = "FrameworkVersion"; + this.FrameworkVersion.Size = new System.Drawing.Size(288, 20); + this.FrameworkVersion.TabIndex = 3; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(14, 206); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(90, 13); + this.label5.TabIndex = 2; + this.label5.Text = "Operating System"; + // + // OperatingSystem + // + this.OperatingSystem.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.OperatingSystem.Enabled = false; + this.OperatingSystem.Location = new System.Drawing.Point(184, 203); + this.OperatingSystem.Name = "OperatingSystem"; + this.OperatingSystem.Size = new System.Drawing.Size(288, 20); + this.OperatingSystem.TabIndex = 3; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(14, 232); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(78, 13); + this.label6.TabIndex = 2; + this.label6.Text = "Sledge Version"; + // + // SledgeVersion + // + this.SledgeVersion.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.SledgeVersion.Enabled = false; + this.SledgeVersion.Location = new System.Drawing.Point(184, 229); + this.SledgeVersion.Name = "SledgeVersion"; + this.SledgeVersion.Size = new System.Drawing.Size(288, 20); + this.SledgeVersion.TabIndex = 3; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(14, 258); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(94, 13); + this.label7.TabIndex = 2; + this.label7.Text = "Full Error Message"; + // + // FullError + // + this.FullError.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.FullError.Enabled = false; + this.FullError.Location = new System.Drawing.Point(184, 255); + this.FullError.Multiline = true; + this.FullError.Name = "FullError"; + this.FullError.Size = new System.Drawing.Size(288, 64); + this.FullError.TabIndex = 3; + // + // label8 + // + this.label8.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label8.Location = new System.Drawing.Point(14, 325); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(164, 49); + this.label8.TabIndex = 2; + this.label8.Text = "Can you tell us any extra information about what you were doing when this error h" + + "appened?"; + // + // InfoTextBox + // + this.InfoTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.InfoTextBox.Location = new System.Drawing.Point(184, 325); + this.InfoTextBox.Multiline = true; + this.InfoTextBox.Name = "InfoTextBox"; + this.InfoTextBox.Size = new System.Drawing.Size(288, 63); + this.InfoTextBox.TabIndex = 3; + // + // SubmitButton + // + this.SubmitButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.SubmitButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.SubmitButton.Location = new System.Drawing.Point(316, 394); + this.SubmitButton.Name = "SubmitButton"; + this.SubmitButton.Size = new System.Drawing.Size(156, 42); + this.SubmitButton.TabIndex = 4; + this.SubmitButton.Text = "Submit Error!"; + this.SubmitButton.UseVisualStyleBackColor = true; + this.SubmitButton.Click += new System.EventHandler(this.SubmitButtonClicked); + // + // CancelButton + // + this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.CancelButton.Location = new System.Drawing.Point(12, 413); + this.CancelButton.Name = "CancelButton"; + this.CancelButton.Size = new System.Drawing.Size(121, 23); + this.CancelButton.TabIndex = 4; + this.CancelButton.Text = "Don\'t Submit Error :("; + this.CancelButton.UseVisualStyleBackColor = true; + this.CancelButton.Click += new System.EventHandler(this.CancelButtonClicked); + // + // ExceptionWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(484, 448); + this.Controls.Add(this.CancelButton); + this.Controls.Add(this.SubmitButton); + this.Controls.Add(this.InfoTextBox); + this.Controls.Add(this.label8); + this.Controls.Add(this.FullError); + this.Controls.Add(this.label7); + this.Controls.Add(this.SledgeVersion); + this.Controls.Add(this.label6); + this.Controls.Add(this.OperatingSystem); + this.Controls.Add(this.label5); + this.Controls.Add(this.FrameworkVersion); + this.Controls.Add(this.label4); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(500, 486); + this.Name = "ExceptionWindow"; + this.Text = "This isn\'t good!"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox FrameworkVersion; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox OperatingSystem; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox SledgeVersion; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.TextBox FullError; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TextBox InfoTextBox; + private System.Windows.Forms.Button SubmitButton; + private System.Windows.Forms.Button CancelButton; + } +} \ No newline at end of file diff --git a/Sledge.Editor/Logging/ExceptionWindow.cs b/Sledge.Editor/Logging/ExceptionWindow.cs new file mode 100644 index 000000000..0e8b4372e --- /dev/null +++ b/Sledge.Editor/Logging/ExceptionWindow.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Net; +using System.Text; +using System.Windows.Forms; + +namespace Sledge.Editor.Logging +{ + public partial class ExceptionWindow : Form + { + public ExceptionInfo ExceptionInfo { get; set; } + + public ExceptionWindow(ExceptionInfo info) + { + ExceptionInfo = info; + InitializeComponent(); + FrameworkVersion.Text = info.RuntimeVersion; + OperatingSystem.Text = info.OperatingSystem; + SledgeVersion.Text = info.ApplicationVersion; + FullError.Text = info.FullStackTrace; + } + + private void SubmitButtonClicked(object sender, EventArgs e) + { + Submit(); + Close(); + } + + private void Submit() + { + try + { + ExceptionInfo.UserEnteredInformation = InfoTextBox.Text; + using (var client = new WebClient()) + { + var values = new NameValueCollection(); + values["Message"] = ExceptionInfo.Message; + values["Runtime"] = ExceptionInfo.RuntimeVersion; + values["OS"] = ExceptionInfo.OperatingSystem; + values["Version"] = ExceptionInfo.ApplicationVersion; + values["StackTrace"] = ExceptionInfo.FullStackTrace; + values["UserInfo"] = ExceptionInfo.UserEnteredInformation; + values["Source"] = ExceptionInfo.Source; + values["Date"] = ExceptionInfo.Date.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ssZ"); + client.UploadValues("http://bugs.sledge-editor.com/Bug/AutoSubmit", "POST", values); + } + } + catch (Exception ex) + { + MessageBox.Show("Error sending bug report: " + ex.Message); + } + } + + private void CancelButtonClicked(object sender, EventArgs e) + { + Close(); + } + } +} diff --git a/Sledge.Editor/Logging/ExceptionWindow.resx b/Sledge.Editor/Logging/ExceptionWindow.resx new file mode 100644 index 000000000..61073aedb --- /dev/null +++ b/Sledge.Editor/Logging/ExceptionWindow.resx @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Sledge is still in alpha, so unfortunately we will experience some bugs every now and then! +Help us fix the issues by submitting this bug to our reporting system! + +Your privacy is important - the only information we upload is: + - The error message + - The version of Sledge you are running + - Your version of .NET and your operating system + - The current time + - Whatever you type below! + + \ No newline at end of file diff --git a/Sledge.Editor/Logging/Logger.cs b/Sledge.Editor/Logging/Logger.cs new file mode 100644 index 000000000..591834026 --- /dev/null +++ b/Sledge.Editor/Logging/Logger.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Sledge.Editor.Logging +{ + public static class Logger + { + public static void ShowException(Exception ex, string message = "") + { + var info = new ExceptionInfo(ex, message); + var window = new ExceptionWindow(info); + window.Show(Editor.Instance); + } + } + + public class ExceptionInfo + { + public Exception Exception { get; set; } + public string RuntimeVersion { get; set; } + public string OperatingSystem { get; set; } + public string ApplicationVersion { get; set; } + public DateTime Date { get; set; } + public string InformationMessage { get; set; } + public string UserEnteredInformation { get; set; } + + public string Source + { + get { return Exception.Source; } + } + + public string Message + { + get + { + var msg = String.IsNullOrWhiteSpace(InformationMessage) ? Exception.Message : InformationMessage; + return msg.Split('\n').Select(x => x.Trim()).FirstOrDefault(x => !String.IsNullOrWhiteSpace(x)); + } + } + + public string StackTrace + { + get { return Exception.StackTrace; } + } + + public string FullStackTrace { get; set; } + + public ExceptionInfo(Exception exception, string info) + { + Exception = exception; + RuntimeVersion = Environment.Version.ToString(); + Date = DateTime.Now; + InformationMessage = info; + ApplicationVersion = FileVersionInfo.GetVersionInfo(typeof(Logger).Assembly.Location).FileVersion; + OperatingSystem = Environment.OSVersion.VersionString; + + var list = new List(); + do + { + list.Add(exception); + exception = exception.InnerException; + } while (exception != null); + + FullStackTrace = (info + "\r\n").Trim(); + foreach (var ex in Enumerable.Reverse(list)) + { + FullStackTrace += "\r\n" + ex.Message + " (" + ex.GetType().FullName + ")\r\n" + ex.StackTrace; + } + FullStackTrace = FullStackTrace.Trim(); + } + } +} diff --git a/Sledge.Editor/Milestones.txt b/Sledge.Editor/Milestones.txt index ae87a7dda..a4f9efb63 100644 --- a/Sledge.Editor/Milestones.txt +++ b/Sledge.Editor/Milestones.txt @@ -11,7 +11,6 @@ Priorities Toobar & tool icons Cordon compile Finish the menu/toolbar actions - Proper error handling / stacktrace reporting Post-Alpha Priorities TDI diff --git a/Sledge.Editor/Program.cs b/Sledge.Editor/Program.cs index bc357e590..6589f7b64 100644 --- a/Sledge.Editor/Program.cs +++ b/Sledge.Editor/Program.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using System.Threading; using System.Windows.Forms; namespace Sledge.Editor @@ -15,7 +17,38 @@ static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); + RegisterHandlers(); Application.Run(new Editor()); } + + private static void RegisterHandlers() + { + Application.ThreadException += ThreadException; + Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); + AppDomain.CurrentDomain.UnhandledException += UnhandledException; + } + + private static void UnhandledException(object sender, UnhandledExceptionEventArgs args) + { + LogException((Exception) args.ExceptionObject); + } + + private static void ThreadException(object sender, ThreadExceptionEventArgs args) + { + LogException(args.Exception); + } + + private static void LogException(Exception ex) + { + var st = new StackTrace(); + var frames = st.GetFrames() ?? new StackFrame[0]; + var msg = "Unhandled exception"; + foreach (var frame in frames) + { + var method = frame.GetMethod(); + msg += "\r\n " + method.ReflectedType.FullName + "." + method.Name; + } + Logging.Logger.ShowException(new Exception(msg, ex), "Unhandled exception"); + } } } diff --git a/Sledge.Editor/Sledge.Editor.csproj b/Sledge.Editor/Sledge.Editor.csproj index d7d8e8ada..9c255d4ab 100644 --- a/Sledge.Editor/Sledge.Editor.csproj +++ b/Sledge.Editor/Sledge.Editor.csproj @@ -130,6 +130,13 @@ + + Form + + + ExceptionWindow.cs + + @@ -357,6 +364,9 @@ GameSelectionForm.cs + + ExceptionWindow.cs + ResXFileCodeGenerator Resources.Designer.cs diff --git a/Sledge.Editor/UI/ViewportManager.cs b/Sledge.Editor/UI/ViewportManager.cs index 945f3c4a5..980994b10 100644 --- a/Sledge.Editor/UI/ViewportManager.cs +++ b/Sledge.Editor/UI/ViewportManager.cs @@ -32,6 +32,8 @@ public static void Init(TableLayoutPanel tlp) Viewports.Add(bl); Viewports.Add(br); + Viewports.ForEach(SubscribeExceptions); + MainWindowGrid.Controls.Clear(); MainWindowGrid.ColumnCount = 2; MainWindowGrid.RowCount = 2; @@ -44,6 +46,12 @@ public static void Init(TableLayoutPanel tlp) RunAll(); } + private static void SubscribeExceptions(ViewportBase vp) + { + vp.ListenerException += (sender, ex) => Logging.Logger.ShowException(ex, "Viewport Listener Exception"); + vp.RenderException += (sender, ex) => Logging.Logger.ShowException(ex, "Viewport Render Exception"); + } + private static void RunAll() { Viewports.ForEach(v => v.Run()); diff --git a/Sledge.UI/ViewportBase.cs b/Sledge.UI/ViewportBase.cs index d77840d75..896917931 100644 --- a/Sledge.UI/ViewportBase.cs +++ b/Sledge.UI/ViewportBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using OpenTK; using OpenTK.Graphics.OpenGL; using Sledge.DataStructures.Geometric; @@ -18,6 +19,42 @@ public class ViewportBase : GLControl public bool IsFocused { get; private set; } private int UnfocusedUpdateCounter { get; set; } + public delegate void RenderExceptionEventHandler(object sender, Exception exception); + public event RenderExceptionEventHandler RenderException; + protected void OnRenderException(Exception ex) + { + if (RenderException != null) + { + var st = new StackTrace(); + var frames = st.GetFrames() ?? new StackFrame[0]; + var msg = "Rendering exception: " + ex.Message; + foreach (var frame in frames) + { + var method = frame.GetMethod(); + msg += "\r\n " + method.ReflectedType.FullName + "." + method.Name; + } + RenderException(this, new Exception(msg, ex)); + } + } + + public delegate void ListenerExceptionEventHandler(object sender, Exception exception); + public event ListenerExceptionEventHandler ListenerException; + protected void OnListenerException(Exception ex) + { + if (ListenerException != null) + { + var st = new StackTrace(); + var frames = st.GetFrames() ?? new StackFrame[0]; + var msg = "Listener exception: " + ex.Message; + foreach (var frame in frames) + { + var method = frame.GetMethod(); + msg += "\r\n " + method.ReflectedType.FullName + "." + method.Name; + } + ListenerException(this, new Exception(msg, ex)); + } + } + protected ViewportBase() { RenderContext = new RenderContext(); @@ -98,12 +135,19 @@ protected void UpdateFrame() } UnfocusedUpdateCounter = 0; - if (!Context.IsCurrent) + try { - MakeCurrent(); + if (!Context.IsCurrent) + { + MakeCurrent(); + } + } + catch (Exception ex) + { + OnRenderException(ex); } - Listeners.ForEach(l => l.UpdateFrame()); + ListenerDo(x => x.UpdateFrame()); LoadIdentity(); UpdateAfterLoadIdentity(); @@ -114,9 +158,16 @@ protected void UpdateFrame() UpdateBeforeClearViewport(); ClearViewport(); - UpdateBeforeRender(); - RenderContext.Render(this); - UpdateAfterRender(); + try + { + UpdateBeforeRender(); + RenderContext.Render(this); + UpdateAfterRender(); + } + catch(Exception ex) + { + OnRenderException(ex); + } SwapBuffers(); } @@ -126,6 +177,21 @@ public void LoadIdentity() GL.LoadIdentity(); } + private void ListenerDo(Action action) + { + foreach (var listener in Listeners) + { + try + { + action(listener); + } + catch (Exception ex) + { + OnListenerException(ex); + } + } + } + protected virtual void UpdateAfterLoadIdentity() { @@ -138,7 +204,7 @@ protected virtual void UpdateBeforeSetViewport() protected virtual void UpdateBeforeClearViewport() { - Listeners.ForEach(x => x.PreRender()); + ListenerDo(x => x.PreRender()); } protected virtual void UpdateBeforeRender() @@ -153,49 +219,49 @@ protected virtual void UpdateAfterRender() protected override void OnMouseWheel(MouseEventArgs e) { - Listeners.ForEach(l => l.MouseWheel(e)); + ListenerDo(l => l.MouseWheel(e)); } protected override void OnMouseEnter(EventArgs e) { Focus(); IsFocused = true; - Listeners.ForEach(l => l.MouseEnter(e)); + ListenerDo(l => l.MouseEnter(e)); } protected override void OnMouseLeave(EventArgs e) { - Listeners.ForEach(l => l.MouseLeave(e)); + ListenerDo(l => l.MouseLeave(e)); } protected override void OnMouseMove(MouseEventArgs e) { - Listeners.ForEach(l => l.MouseMove(e)); + ListenerDo(l => l.MouseMove(e)); } protected override void OnMouseUp(MouseEventArgs e) { - Listeners.ForEach(l => l.MouseUp(e)); + ListenerDo(l => l.MouseUp(e)); } protected override void OnMouseDown(MouseEventArgs e) { - Listeners.ForEach(l => l.MouseDown(e)); + ListenerDo(l => l.MouseDown(e)); } protected override void OnKeyDown(KeyEventArgs e) { - Listeners.ForEach(l => l.KeyDown(e)); + ListenerDo(l => l.KeyDown(e)); } protected override void OnKeyPress(KeyPressEventArgs e) { - Listeners.ForEach(l => l.KeyPress(e)); + ListenerDo(l => l.KeyPress(e)); } protected override void OnKeyUp(KeyEventArgs e) { - Listeners.ForEach(l => l.KeyUp(e)); + ListenerDo(l => l.KeyUp(e)); } } }