Compare commits

..

3 Commits

Author SHA1 Message Date
Muyuan Li (from Dev Box)
bb4356bdee Address review: use prefix constants instead of magic numbers, normalize paths at source
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-14 15:55:43 +08:00
copilot-swe-agent[bot]
c93ecb49c5 Fix UriFormatException in ImageLoader when PowerToys is run from extended-length UNC path
When PowerToys is installed on or accessed from a network share (e.g. via
Remote Desktop), the assembly location reported by the runtime uses the
Windows extended-length UNC path prefix (\\?\UNC\server\share\...). The
System.Uri class cannot parse this prefix and throws UriFormatException
with 'The hostname could not be parsed' in ImageLoader.Initialize().

Add GetNormalizedPath() helper that strips \\?\UNC\ (converts to \\) or
\\?\ prefix before creating Uri objects, and apply it in Initialize() and
LoadFullImage(). Also add unit tests for the new helper.

Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/ea947576-4e79-40ac-bf65-d6b35bc75ad7

Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
2026-04-29 09:03:00 +00:00
copilot-swe-agent[bot]
2db145c56a Initial plan 2026-04-29 08:49:43 +00:00
5 changed files with 80 additions and 17 deletions

View File

@@ -55,13 +55,18 @@ namespace Wox.Infrastructure.Image
return fs.Read(buffer, 0, buffer.Length) == buffer.Length && pngSignature.SequenceEqual(buffer);
}
internal static string GetNormalizedPath(string path)
{
return PathNormalization.NormalizePath(path);
}
public static void Initialize()
{
_hashGenerator = new ImageHashGenerator();
foreach (var icon in new[] { Constant.ErrorIcon, Constant.LightThemedErrorIcon })
{
var uri = new Uri(icon);
var uri = new Uri(GetNormalizedPath(icon));
try
{
@@ -298,7 +303,7 @@ namespace Wox.Infrastructure.Image
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(path);
image.UriSource = new Uri(GetNormalizedPath(path));
image.EndInit();
return image;
}

View File

@@ -64,7 +64,7 @@ namespace Wox.Plugin
public static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location.NonNull()).ProductVersion;
public static readonly int ThumbnailSize = 64;
public static readonly string ErrorIcon = Path.Combine(ProgramDirectory, "Assets", "PowerLauncher", "app_error.dark.png");
public static readonly string LightThemedErrorIcon = Path.Combine(ProgramDirectory, "Assets", "PowerLauncher", "app_error.light.png");
public static readonly string ErrorIcon = PathNormalization.NormalizePath(Path.Combine(ProgramDirectory, "Assets", "PowerLauncher", "app_error.dark.png"));
public static readonly string LightThemedErrorIcon = PathNormalization.NormalizePath(Path.Combine(ProgramDirectory, "Assets", "PowerLauncher", "app_error.light.png"));
}
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Wox.Plugin
{
public static class PathNormalization
{
private const string UncPrefix = @"\\?\UNC\";
private const string ExtendedPrefix = @"\\?\";
public static string NormalizePath(string path)
{
if (path.StartsWith(UncPrefix, StringComparison.OrdinalIgnoreCase))
{
return @"\\" + path.Substring(UncPrefix.Length);
}
if (path.StartsWith(ExtendedPrefix, StringComparison.OrdinalIgnoreCase))
{
return path.Substring(ExtendedPrefix.Length);
}
return path;
}
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wox.Plugin;
namespace Wox.Test
{
[TestClass]
public class ImageLoaderTest
{
[DataTestMethod]
// Regular Windows paths should be returned unchanged
[DataRow(@"C:\path\to\file.png", @"C:\path\to\file.png")]
[DataRow(@"C:\Program Files\PowerToys\Assets\PowerLauncher\app_error.dark.png", @"C:\Program Files\PowerToys\Assets\PowerLauncher\app_error.dark.png")]
// UNC paths should be returned unchanged
[DataRow(@"\\server\share\path\file.png", @"\\server\share\path\file.png")]
// Extended-length local paths (\\?\C:\...) should have the \\?\ prefix stripped
[DataRow(@"\\?\C:\path\to\file.png", @"C:\path\to\file.png")]
[DataRow(@"\\?\C:\Program Files\PowerToys\Assets\app_error.dark.png", @"C:\Program Files\PowerToys\Assets\app_error.dark.png")]
// Extended-length UNC paths (\\?\UNC\server\...) should be converted to \\server\...
[DataRow(@"\\?\UNC\server\share\path\file.png", @"\\server\share\path\file.png")]
[DataRow(@"\\?\UNC\TH50\TH50_c\Program Files\PowerToys\Assets\PowerLauncher\app_error.dark.png", @"\\TH50\TH50_c\Program Files\PowerToys\Assets\PowerLauncher\app_error.dark.png")]
// Case-insensitive matching for the prefix
[DataRow(@"\\?\unc\server\share\path\file.png", @"\\server\share\path\file.png")]
public void GetNormalizedPath_ShouldStripExtendedLengthPrefix(string input, string expected)
{
// Act
string result = PathNormalization.NormalizePath(input);
// Assert
Assert.AreEqual(expected, result);
}
}
}

View File

@@ -9,7 +9,6 @@ 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;
@@ -136,18 +135,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
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;
throw e.Exception;
}
private void Frame_Navigated(object sender, NavigationEventArgs e)