mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
CmdPal: Fix winget in release builds (#41183)
Seems as though that doing `.ToArray` on the `IIterable<>` things we get back from the winget API were not trim-safe. I'm not entirely sure why. But we're totally okay just manually iterating over these things. That works like a charm. Closes #41172 As a drive-by, I wrapped the line from #41025 in a try-catch. If that doesn't fix it, hopefully it at least gives us more logging.
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using ManagedCommon;
|
||||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||||
using Microsoft.CmdPal.Core.ViewModels.Models;
|
using Microsoft.CmdPal.Core.ViewModels.Models;
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
@@ -181,15 +182,15 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
|||||||
if (more is not null)
|
if (more is not null)
|
||||||
{
|
{
|
||||||
MoreCommands = more
|
MoreCommands = more
|
||||||
.Select(item =>
|
.Select<IContextItem, IContextItemViewModel>(item =>
|
||||||
{
|
{
|
||||||
if (item is ICommandContextItem contextItem)
|
if (item is ICommandContextItem contextItem)
|
||||||
{
|
{
|
||||||
return new CommandContextItemViewModel(contextItem, PageContext) as IContextItemViewModel;
|
return new CommandContextItemViewModel(contextItem, PageContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new SeparatorViewModel() as IContextItemViewModel;
|
return new SeparatorViewModel();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -237,8 +238,9 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
|||||||
FastInitializeProperties();
|
FastInitializeProperties();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Logger.LogError("error fast initializing CommandItemViewModel", ex);
|
||||||
Command = new(null, PageContext);
|
Command = new(null, PageContext);
|
||||||
_itemTitle = "Error";
|
_itemTitle = "Error";
|
||||||
Subtitle = "Item failed to load";
|
Subtitle = "Item failed to load";
|
||||||
@@ -257,9 +259,10 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
|||||||
SlowInitializeProperties();
|
SlowInitializeProperties();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Initialized |= InitializedState.Error;
|
Initialized |= InitializedState.Error;
|
||||||
|
Logger.LogError("error slow initializing CommandItemViewModel", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -272,8 +275,9 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
|||||||
InitializeProperties();
|
InitializeProperties();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Logger.LogError("error initializing CommandItemViewModel", ex);
|
||||||
Command = new(null, PageContext);
|
Command = new(null, PageContext);
|
||||||
_itemTitle = "Error";
|
_itemTitle = "Error";
|
||||||
Subtitle = "Item failed to load";
|
Subtitle = "Item failed to load";
|
||||||
@@ -342,15 +346,15 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
|||||||
if (more is not null)
|
if (more is not null)
|
||||||
{
|
{
|
||||||
var newContextMenu = more
|
var newContextMenu = more
|
||||||
.Select(item =>
|
.Select<IContextItem, IContextItemViewModel>(item =>
|
||||||
{
|
{
|
||||||
if (item is ICommandContextItem contextItem)
|
if (item is ICommandContextItem contextItem)
|
||||||
{
|
{
|
||||||
return new CommandContextItemViewModel(contextItem, PageContext) as IContextItemViewModel;
|
return new CommandContextItemViewModel(contextItem, PageContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new SeparatorViewModel() as IContextItemViewModel;
|
return new SeparatorViewModel();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|||||||
@@ -23,6 +23,12 @@
|
|||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- For debugging purposes, uncomment this block to enable AOT builds -->
|
||||||
|
<!-- <PropertyGroup>
|
||||||
|
<EnableCmdPalAOT>true</EnableCmdPalAOT>
|
||||||
|
<CIBuild>true</CIBuild>
|
||||||
|
</PropertyGroup> -->
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(EnableCmdPalAOT)' == 'true'">
|
<PropertyGroup Condition="'$(EnableCmdPalAOT)' == 'true'">
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
|
|||||||
@@ -32,7 +32,16 @@ public partial class InstallPackageListItem : ListItem
|
|||||||
{
|
{
|
||||||
_package = package;
|
_package = package;
|
||||||
|
|
||||||
var version = _package.DefaultInstallVersion ?? _package.InstalledVersion;
|
PackageVersionInfo? version = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
version = _package.DefaultInstallVersion ?? _package.InstalledVersion;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogError("Could not get package version", e);
|
||||||
|
}
|
||||||
|
|
||||||
var versionTagText = "Unknown";
|
var versionTagText = "Unknown";
|
||||||
if (version is not null)
|
if (version is not null)
|
||||||
{
|
{
|
||||||
@@ -57,20 +66,27 @@ public partial class InstallPackageListItem : ListItem
|
|||||||
}
|
}
|
||||||
catch (COMException ex)
|
catch (COMException ex)
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"{ex.ErrorCode}");
|
Logger.LogWarning($"GetCatalogPackageMetadata error {ex.ErrorCode}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata is not null)
|
if (metadata is not null)
|
||||||
{
|
{
|
||||||
if (metadata.Tags.Where(t => t.Equals(WinGetExtensionPage.ExtensionsTag, StringComparison.OrdinalIgnoreCase)).Any())
|
for (var i = 0; i < metadata.Tags.Count; i++)
|
||||||
{
|
{
|
||||||
if (_installCommand is not null)
|
if (metadata.Tags[i].Equals(WinGetExtensionPage.ExtensionsTag, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_installCommand.SkipDependencies = true;
|
if (_installCommand is not null)
|
||||||
|
{
|
||||||
|
_installCommand.SkipDependencies = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var description = string.IsNullOrEmpty(metadata.Description) ? metadata.ShortDescription : metadata.Description;
|
var description = string.IsNullOrEmpty(metadata.Description) ?
|
||||||
|
metadata.ShortDescription :
|
||||||
|
metadata.Description;
|
||||||
var detailsBody = $"""
|
var detailsBody = $"""
|
||||||
|
|
||||||
{description}
|
{description}
|
||||||
@@ -116,9 +132,11 @@ public partial class InstallPackageListItem : ListItem
|
|||||||
// These can be l o n g
|
// These can be l o n g
|
||||||
{ Properties.Resources.winget_release_notes, (metadata.ReleaseNotes, string.Empty) },
|
{ Properties.Resources.winget_release_notes, (metadata.ReleaseNotes, string.Empty) },
|
||||||
};
|
};
|
||||||
var docs = metadata.Documentations.ToArray();
|
var docs = metadata.Documentations;
|
||||||
foreach (var item in docs)
|
var count = docs.Count;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
var item = docs[i];
|
||||||
simpleData.Add(item.DocumentLabel, (string.Empty, item.DocumentUrl));
|
simpleData.Add(item.DocumentLabel, (string.Empty, item.DocumentUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +159,7 @@ public partial class InstallPackageListItem : ListItem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.Tags.Any())
|
if (metadata.Tags.Count > 0)
|
||||||
{
|
{
|
||||||
DetailsElement pair = new()
|
DetailsElement pair = new()
|
||||||
{
|
{
|
||||||
@@ -169,6 +187,7 @@ public partial class InstallPackageListItem : ListItem
|
|||||||
{
|
{
|
||||||
// Handle other exceptions
|
// Handle other exceptions
|
||||||
ExtensionHost.LogMessage($"[WinGet] UpdatedInstalledStatus throw exception: {ex.Message}");
|
ExtensionHost.LogMessage($"[WinGet] UpdatedInstalledStatus throw exception: {ex.Message}");
|
||||||
|
Logger.LogError($"[WinGet] UpdatedInstalledStatus throw exception", ex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,10 +252,10 @@ public partial class InstallPackageListItem : ListItem
|
|||||||
Stopwatch s = new();
|
Stopwatch s = new();
|
||||||
Logger.LogDebug($"Starting RefreshPackageCatalogAsync");
|
Logger.LogDebug($"Starting RefreshPackageCatalogAsync");
|
||||||
s.Start();
|
s.Start();
|
||||||
var refs = WinGetStatics.AvailableCatalogs.ToArray();
|
var refs = WinGetStatics.AvailableCatalogs;
|
||||||
|
for (var i = 0; i < refs.Count; i++)
|
||||||
foreach (var catalog in refs)
|
|
||||||
{
|
{
|
||||||
|
var catalog = refs[i];
|
||||||
var operation = catalog.RefreshPackageCatalogAsync();
|
var operation = catalog.RefreshPackageCatalogAsync();
|
||||||
operation.Wait();
|
operation.Wait();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
private CancellationTokenSource? _cancellationTokenSource;
|
private CancellationTokenSource? _cancellationTokenSource;
|
||||||
private Task<IEnumerable<CatalogPackage>>? _currentSearchTask;
|
private Task<IEnumerable<CatalogPackage>>? _currentSearchTask;
|
||||||
|
|
||||||
private IEnumerable<CatalogPackage>? _results;
|
private List<CatalogPackage>? _results;
|
||||||
|
|
||||||
public static string ExtensionsTag => "windows-commandpalette-extension";
|
public static string ExtensionsTag => "windows-commandpalette-extension";
|
||||||
|
|
||||||
@@ -48,7 +48,6 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
|
|
||||||
public override IListItem[] GetItems()
|
public override IListItem[] GetItems()
|
||||||
{
|
{
|
||||||
IListItem[] items = [];
|
|
||||||
lock (_resultsLock)
|
lock (_resultsLock)
|
||||||
{
|
{
|
||||||
// emptySearchForTag ===
|
// emptySearchForTag ===
|
||||||
@@ -61,12 +60,28 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
{
|
{
|
||||||
IsLoading = true;
|
IsLoading = true;
|
||||||
DoUpdateSearchText(string.Empty);
|
DoUpdateSearchText(string.Empty);
|
||||||
return items;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_results is not null && _results.Any())
|
if (_results is not null && _results.Count != 0)
|
||||||
{
|
{
|
||||||
ListItem[] results = _results.Select(PackageToListItem).ToArray();
|
var count = _results.Count;
|
||||||
|
var results = new ListItem[count];
|
||||||
|
var next = 0;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var li = PackageToListItem(_results[i]);
|
||||||
|
results[next] = li;
|
||||||
|
next++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError("error converting result to listitem", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IsLoading = false;
|
IsLoading = false;
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
@@ -82,7 +97,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
|
|
||||||
IsLoading = false;
|
IsLoading = false;
|
||||||
|
|
||||||
return items;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ListItem PackageToListItem(CatalogPackage p) => new InstallPackageListItem(p);
|
private static ListItem PackageToListItem(CatalogPackage p) => new InstallPackageListItem(p);
|
||||||
@@ -108,7 +123,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
CancellationToken cancellationToken = _cancellationTokenSource.Token;
|
var cancellationToken = _cancellationTokenSource.Token;
|
||||||
|
|
||||||
IsLoading = true;
|
IsLoading = true;
|
||||||
|
|
||||||
@@ -139,7 +154,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IEnumerable<CatalogPackage> results = await searchTask;
|
var results = await searchTask;
|
||||||
|
|
||||||
// Ensure this is still the latest task
|
// Ensure this is still the latest task
|
||||||
if (_currentSearchTask == searchTask)
|
if (_currentSearchTask == searchTask)
|
||||||
@@ -156,7 +171,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Handle other exceptions
|
// Handle other exceptions
|
||||||
Logger.LogError(ex.Message);
|
Logger.LogError("Unexpected error while processing results", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,10 +180,10 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
Logger.LogDebug($"Completed search for '{query}'");
|
Logger.LogDebug($"Completed search for '{query}'");
|
||||||
lock (_resultsLock)
|
lock (_resultsLock)
|
||||||
{
|
{
|
||||||
this._results = results;
|
this._results = results.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseItemsChanged(this._results.Count());
|
RaiseItemsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<CatalogPackage>> DoSearchAsync(string query, CancellationToken ct)
|
private async Task<IEnumerable<CatalogPackage>> DoSearchAsync(string query, CancellationToken ct)
|
||||||
@@ -190,12 +205,12 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
HashSet<CatalogPackage> results = new(new PackageIdCompare());
|
HashSet<CatalogPackage> results = new(new PackageIdCompare());
|
||||||
|
|
||||||
// Default selector: this is the way to do a `winget search <query>`
|
// Default selector: this is the way to do a `winget search <query>`
|
||||||
PackageMatchFilter selector = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
|
var selector = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
|
||||||
selector.Field = Microsoft.Management.Deployment.PackageMatchField.CatalogDefault;
|
selector.Field = Microsoft.Management.Deployment.PackageMatchField.CatalogDefault;
|
||||||
selector.Value = query;
|
selector.Value = query;
|
||||||
selector.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
|
selector.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
|
||||||
|
|
||||||
FindPackagesOptions opts = WinGetStatics.WinGetFactory.CreateFindPackagesOptions();
|
var opts = WinGetStatics.WinGetFactory.CreateFindPackagesOptions();
|
||||||
opts.Selectors.Add(selector);
|
opts.Selectors.Add(selector);
|
||||||
|
|
||||||
// testing
|
// testing
|
||||||
@@ -204,7 +219,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
// Selectors is "OR", Filters is "AND"
|
// Selectors is "OR", Filters is "AND"
|
||||||
if (HasTag)
|
if (HasTag)
|
||||||
{
|
{
|
||||||
PackageMatchFilter tagFilter = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
|
var tagFilter = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
|
||||||
tagFilter.Field = Microsoft.Management.Deployment.PackageMatchField.Tag;
|
tagFilter.Field = Microsoft.Management.Deployment.PackageMatchField.Tag;
|
||||||
tagFilter.Value = _tag;
|
tagFilter.Value = _tag;
|
||||||
tagFilter.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
|
tagFilter.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
|
||||||
@@ -215,11 +230,11 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
// Clean up here, then...
|
// Clean up here, then...
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
Lazy<Task<PackageCatalog>> catalogTask = HasTag ? WinGetStatics.CompositeWingetCatalog : WinGetStatics.CompositeAllCatalog;
|
var catalogTask = HasTag ? WinGetStatics.CompositeWingetCatalog : WinGetStatics.CompositeAllCatalog;
|
||||||
|
|
||||||
// Both these catalogs should have been instantiated by the
|
// Both these catalogs should have been instantiated by the
|
||||||
// WinGetStatics static ctor when we were created.
|
// WinGetStatics static ctor when we were created.
|
||||||
PackageCatalog catalog = await catalogTask.Value;
|
var catalog = await catalogTask.Value;
|
||||||
|
|
||||||
if (catalog is null)
|
if (catalog is null)
|
||||||
{
|
{
|
||||||
@@ -235,8 +250,8 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
|
|
||||||
// BODGY, re: microsoft/winget-cli#5151
|
// BODGY, re: microsoft/winget-cli#5151
|
||||||
// FindPackagesAsync isn't actually async.
|
// FindPackagesAsync isn't actually async.
|
||||||
Task<FindPackagesResult> internalSearchTask = Task.Run(() => catalog.FindPackages(opts), ct);
|
var internalSearchTask = Task.Run(() => catalog.FindPackages(opts), ct);
|
||||||
FindPackagesResult searchResults = await internalSearchTask;
|
var searchResults = await internalSearchTask;
|
||||||
|
|
||||||
// TODO more error handling like this:
|
// TODO more error handling like this:
|
||||||
if (searchResults.Status != FindPackagesResultStatus.Ok)
|
if (searchResults.Status != FindPackagesResultStatus.Ok)
|
||||||
@@ -247,13 +262,17 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogDebug($" got results for ({query})", memberName: nameof(DoSearchAsync));
|
Logger.LogDebug($" got results for ({query})", memberName: nameof(DoSearchAsync));
|
||||||
foreach (Management.Deployment.MatchResult? match in searchResults.Matches.ToArray())
|
|
||||||
|
// FYI Using .ToArray or any other kind of enumerable loop
|
||||||
|
// on arrays returned by the winget API are NOT trim safe
|
||||||
|
var count = searchResults.Matches.Count;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
var match = searchResults.Matches[i];
|
||||||
|
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Print the packages
|
var package = match.CatalogPackage;
|
||||||
CatalogPackage package = match.CatalogPackage;
|
|
||||||
|
|
||||||
results.Add(package);
|
results.Add(package);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user