Compare commits

..

3 Commits

Author SHA1 Message Date
Muyuan Li (from Dev Box)
f9679b937d Address review: handle NavigationFailed gracefully without rethrowing
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-14 16:20:42 +08:00
copilot-swe-agent[bot]
36300d3c75 Fix NullReferenceException in Frame_NavigationFailed when e.Exception is null
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/af982aa1-504b-47f1-9ec0-93b29602b2af

Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
2026-04-29 08:49:32 +00:00
copilot-swe-agent[bot]
1cde68ae04 Initial plan 2026-04-29 08:48:31 +00:00
2 changed files with 13 additions and 69 deletions

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;
@@ -67,79 +66,12 @@ namespace PowerLauncher.Helper
var logger = LogManager.GetLogger(LoggerName);
logger.Error($"From {(isNotUIThread ? "non" : string.Empty)} UI thread's exception: {ExceptionFormatter.FormatException(e)}");
}
else if (IsExceptionFromUserPlugin(e))
{
// Exceptions thrown by user-installed third-party plugins should be logged
// but must not show the crash report window, since the exception is not
// caused by PowerToys itself and the popup is confusing and alarming to the user.
var logger = LogManager.GetLogger(LoggerName);
logger.Error($"From {(isNotUIThread ? "non" : string.Empty)} UI thread - Exception from user plugin: {ExceptionFormatter.FormatException(e)}");
}
else
{
Report(e, isNotUIThread);
}
}
/// <summary>
/// Determines whether an exception originated from a user-installed third-party plugin
/// (i.e., an assembly loaded from <see cref="Constant.PluginsDirectory"/>).
/// </summary>
private static bool IsExceptionFromUserPlugin(Exception e)
{
var current = e;
while (current != null)
{
if (HasPluginStackFrames(current))
{
return true;
}
if (current is AggregateException aggregateException)
{
foreach (var innerException in aggregateException.InnerExceptions)
{
if (IsExceptionFromUserPlugin(innerException))
{
return true;
}
}
}
current = current.InnerException;
}
return false;
}
private static bool HasPluginStackFrames(Exception e)
{
try
{
var pluginsDir = Constant.PluginsDirectory;
var stackTrace = new StackTrace(e, fNeedFileInfo: false);
foreach (var frame in stackTrace.GetFrames() ?? [])
{
var assemblyLocation = frame.GetMethod()?.DeclaringType?.Assembly?.Location;
if (!string.IsNullOrEmpty(assemblyLocation) &&
assemblyLocation.StartsWith(pluginsDir, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
catch (System.Exception)
{
// Reflection-based stack inspection can throw a variety of exceptions
// (e.g., SecurityException, TypeLoadException, BadImageFormatException).
// In every failure case the safe fallback is to treat the exception as
// NOT originating from a plugin so that it still surfaces as a crash report.
}
return false;
}
private static void Report(Exception e, bool waitForClose)
{
if (e != null)

View File

@@ -9,6 +9,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
@@ -135,7 +136,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw e.Exception;
var sourcePage = e.SourcePageType?.FullName ?? "<unknown>";
if (e.Exception is null)
{
Logger.LogWarning($"Navigation to '{sourcePage}' failed without an exception.");
}
else
{
Logger.LogError($"Navigation to '{sourcePage}' failed.", e.Exception);
}
e.Handled = true;
}
private void Frame_Navigated(object sender, NavigationEventArgs e)