Browse Source

Report PropertyChanged call from non UI thread and fixed fatal error message box at App.OnStartup

CPKreuz 1 year ago
parent
commit
8b88985230

+ 7 - 2
src/PixiEditor/App.xaml.cs

@@ -48,13 +48,18 @@ internal partial class App : Application
             }
             catch (Exception exception)
             {
+                Task sendReport = null;
+                
                 try
                 {
-                    CrashHelper.SendExceptionInfoToWebhook(exception);
+                    sendReport = CrashHelper.SendExceptionInfoToWebhook(exception);
                 }
                 finally
                 {
-                    MessageBox.Show("Fatal error", $"Fatal error while trying to open crash report in App.OnStartup()\n{exception}");
+                    MessageBox.Show($"Fatal error while trying to open crash report in App.OnStartup()\nPlease report this https://pixieditor.net/help\n{exception}", "Fatal error");
+                    if (sendReport != null && sendReport.Status != TaskStatus.RanToCompletion)
+                        sendReport.Wait(8000);
+                    Shutdown();
                 }
             }
 

+ 35 - 0
src/PixiEditor/Exceptions/CustomStackTraceException.cs

@@ -0,0 +1,35 @@
+using System.Diagnostics;
+using System.Runtime.Serialization;
+
+namespace PixiEditor.Exceptions;
+
+/// <summary>
+/// Allows to create a exception with a stacktrace without the exception having to be thrown
+/// </summary>
+public class CustomStackTraceException : Exception
+{
+    private string? stackTrace;
+    
+    public CustomStackTraceException()
+    {
+    }
+
+    protected CustomStackTraceException(SerializationInfo info, StreamingContext context) : base(info, context)
+    {
+    }
+
+    public CustomStackTraceException(string message) : base(message)
+    {
+    }
+
+    public CustomStackTraceException(string message, Exception innerException) : base(message, innerException)
+    {
+    }
+
+    public void GenerateStackTrace()
+    {
+        stackTrace = new StackTrace(1, true).ToString();
+    }
+
+    public override string? StackTrace => stackTrace ?? base.StackTrace;
+}

+ 28 - 0
src/PixiEditor/NotifyableObject.cs

@@ -1,11 +1,15 @@
 using System.ComponentModel;
 using System.Runtime.CompilerServices;
+using System.Windows;
+using PixiEditor.Exceptions;
 
 namespace PixiEditor;
 
 [Serializable]
 internal class NotifyableObject : INotifyPropertyChanged
 {
+    private static Thread uiThread;
+    
     [field: NonSerialized]
     public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
 
@@ -33,6 +37,8 @@ internal class NotifyableObject : INotifyPropertyChanged
 
     public void RaisePropertyChanged(string property)
     {
+        ReportNonUiThread(property);
+        
         if (property != null)
         {
             PropertyChanged(this, new PropertyChangedEventArgs(property));
@@ -44,6 +50,8 @@ internal class NotifyableObject : INotifyPropertyChanged
 
     protected bool SetProperty<T>(ref T backingStore, T value, out T oldValue, Action beforeChange = null, [CallerMemberName] string propertyName = "")
     {
+        ReportNonUiThread(propertyName);
+        
         if (EqualityComparer<T>.Default.Equals(backingStore, value))
         {
             oldValue = backingStore;
@@ -56,4 +64,24 @@ internal class NotifyableObject : INotifyPropertyChanged
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
         return true;
     }
+
+    private static void ReportNonUiThread(string property, [CallerMemberName] string methodName = "")
+    {
+        uiThread ??= Application.Current.Dispatcher.Thread;
+        var currentThread = Thread.CurrentThread;
+
+        if (currentThread == uiThread)
+        {
+            return;
+        }
+
+        var exception = new CustomStackTraceException($"Do not call {methodName}() from a thread other than the UI thread. Calling for property '{property ?? "{ property name null }"}'. (Calling from '{currentThread.Name ?? "{ name null }"}' @{currentThread.ManagedThreadId})");
+        
+#if DEBUG
+        throw exception;
+#else
+        exception.GenerateStackTrace();
+        Task.Run(() => CrashHelper.SendExceptionInfoToWebhook(exception));
+#endif
+    }
 }