Compare commits

..

1 Commits

Author SHA1 Message Date
Muyuan Li (from Dev Box)
12e9ee42ca fix(Settings): handle null Exception in Frame_NavigationFailed to prevent NullReferenceException
When NavigationFailedEventArgs.Exception is null, the original code threw a
NullReferenceException instead of a meaningful error. Use null-coalescing to
throw an InvalidOperationException with context about the failed page.
2026-04-29 17:56:18 +08:00
2 changed files with 2 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

@@ -135,7 +135,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw e.Exception;
throw e.Exception ?? new InvalidOperationException(
$"Navigation to {e.SourcePageType?.FullName ?? "unknown page"} failed.");
}
private void Frame_Navigated(object sender, NavigationEventArgs e)