Files
duplicati/Duplicati/GUI/Duplicati.GUI.TrayIcon/ErrorDialog.cs
T
Kenneth Skovhede 2b192e8d90 Add crash dialog
This PR adds a dialog window that is displayed if the TrayIcon process, so the cause is immediately visible.

Prior to this PR, the user was required to locate the crashlog file and open this to figure out why Duplicati silently crashed.
2026-04-16 13:13:36 +02:00

166 lines
5.8 KiB
C#

// Copyright (C) 2025, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Media;
using Avalonia.Layout;
using Avalonia.Themes.Fluent;
using ScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility;
namespace Duplicati.GUI.TrayIcon
{
/// <summary>
/// Helper class for showing error dialogs
/// </summary>
public static class ErrorDialog
{
/// <summary>
/// Shows an error dialog for the crash, being very defensive to avoid additional crashes.
/// This should only be called when the hosted server instance is crashing.
/// </summary>
/// <param name="ex">The exception to show</param>
public static void ShowErrorDialog(Exception ex)
{
try
{
// Build a simple Avalonia application to show the dialog
var builder = AppBuilder
.Configure<Application>()
.UsePlatformDetect();
var app = builder.SetupWithoutStarting().Instance;
if (app == null)
return;
// Apply a simple theme so controls look native
app.Styles.Add(new FluentTheme());
// Create the message box window
var window = new Window
{
Title = "Duplicati TrayIcon Error",
Width = 600,
Height = 400,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
CanResize = true,
ShowInTaskbar = false,
Topmost = true
};
// Create the content
var stackPanel = new StackPanel
{
Margin = new Thickness(20),
Spacing = 10
};
var titleBlock = new TextBlock
{
Text = "A fatal error occurred",
FontSize = 16,
FontWeight = FontWeight.Bold,
TextWrapping = TextWrapping.Wrap
};
var messageBlock = new TextBlock
{
Text = ex.Message,
TextWrapping = TextWrapping.Wrap
};
// Details label
var detailsLabel = new TextBlock
{
Text = "Details:",
FontWeight = FontWeight.Bold,
Margin = new Thickness(0, 10, 0, 0)
};
// Scrollable text area with full exception details
var exceptionText = new TextBox
{
Text = ex.ToString(),
IsReadOnly = true,
TextWrapping = TextWrapping.NoWrap,
AcceptsReturn = true,
FontFamily = new FontFamily("Consolas, Monaco, Courier New, monospace"),
FontSize = 10
};
// Explicitly wrap in a ScrollViewer for proper scrolling
var scrollViewer = new ScrollViewer
{
Content = exceptionText,
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Height = 200
};
var buttonPanel = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 20, 0, 0)
};
var okButton = new Button
{
Content = "OK",
Width = 100,
HorizontalContentAlignment = HorizontalAlignment.Center
};
okButton.Click += (_, _) =>
{
try
{
window.Close();
}
catch
{
// Ignore errors closing window
}
};
buttonPanel.Children.Add(okButton);
stackPanel.Children.Add(titleBlock);
stackPanel.Children.Add(messageBlock);
stackPanel.Children.Add(detailsLabel);
stackPanel.Children.Add(scrollViewer);
stackPanel.Children.Add(buttonPanel);
window.Content = stackPanel;
app.Run(window);
}
catch
{
}
}
}
}