Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UniGetUI.Core.Logging;

Expand Down Expand Up @@ -31,6 +32,12 @@ public static void EnsureStamped()
if (!OperatingSystem.IsWindows())
return;

if (!RuntimeFeature.IsDynamicCodeSupported)
{
Logger.Info("Skipping Start Menu shortcut AUMID stamping because NativeAOT does not support legacy COM activation.");
return;
}

try
{
string? shortcutPath = ResolveStartMenuShortcut();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Avalonia.Controls;
Expand Down Expand Up @@ -63,6 +64,14 @@ private static bool HasHardwareGpu
Justification = "The DXGI COM interfaces are declared in full and referenced directly here, so their members are preserved by trimming.")]
private static bool DetectHardwareGpu()
{
if (!RuntimeFeature.IsDynamicCodeSupported)
{
Logger.Info(
"Skipping DXGI hardware GPU detection because NativeAOT does not support legacy COM activation."
);
return true;
}

try
{
Guid factoryIid = typeof(IDXGIFactory1).GUID;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ public static void AddPackage(IPackage package, CatalogPackage catalogPackage)
if (catalogPackage is null)
return null;

var availableVersions = catalogPackage.AvailableVersions?.ToArray() ?? [];
var availableVersions =
catalogPackage.AvailableVersions is { } versions
? NativeWinGetCollection.Copy(versions)
: [];
if (availableVersions.Length > 0)
{
metadata = catalogPackage
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace UniGetUI.PackageEngine.Managers.WingetManager;

internal static class NativeWinGetCollection
{
internal static T[] Copy<T>(IReadOnlyList<T> collection)
{
ArgumentNullException.ThrowIfNull(collection);

T[] copy = new T[collection.Count];
for (int index = 0; index < copy.Length; index++)
{
copy[index] = collection[index];
}

return copy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public IReadOnlyList<Package> FindPackages_UnSafe(string query)

// Spawn Tasks to find packages on catalogs
logger.Log("Spawning catalog fetching tasks...");
foreach (PackageCatalogReference CatalogReference in AvailableCatalogs.ToArray())
foreach (PackageCatalogReference CatalogReference in NativeWinGetCollection.Copy(AvailableCatalogs))
{
logger.Log($"Begin search on catalog {CatalogReference.Info.Name}");
// Connect to catalog
Expand Down Expand Up @@ -266,7 +266,9 @@ var filter_type in new[]
);

FindPackagesResult FoundPackages = CatalogTaskPair.Value.Result;
foreach (MatchResult matchResult in FoundPackages.Matches.ToArray())
foreach (
MatchResult matchResult in NativeWinGetCollection.Copy(FoundPackages.Matches)
)
{
CatalogPackage nativePackage = matchResult.CatalogPackage;
// Create the Package item and add it to the list
Expand Down Expand Up @@ -396,7 +398,10 @@ public IReadOnlyList<Package> GetInstalledPackages_UnSafe()
try
{
IManagerSource source;
var availableVersions = nativePackage.AvailableVersions?.ToArray() ?? [];
var availableVersions =
nativePackage.AvailableVersions is { } versions
? NativeWinGetCollection.Copy(versions)
: [];
if (availableVersions.Length > 0)
{
var installPackage = nativePackage.GetPackageVersionInfo(availableVersions[0]);
Expand Down Expand Up @@ -582,7 +587,7 @@ private IReadOnlyList<CatalogPackage> GetLocalWinGetPackages()
}

List<CatalogPackage> foundPackages = [];
foreach (var match in TaskResult.Matches.ToArray())
foreach (var match in NativeWinGetCollection.Copy(TaskResult.Matches))
{
foundPackages.Add(match.CatalogPackage);
}
Expand All @@ -601,7 +606,7 @@ INativeTaskLogger logger
)
{
return SelectReachableCatalogs(
WinGetManager.GetPackageCatalogs().ToArray(),
NativeWinGetCollection.Copy(WinGetManager.GetPackageCatalogs()),
static catalogRef => catalogRef.Info.Name,
catalogRef => TryConnectLocalCatalog(catalogRef, logger),
catalogName =>
Expand Down Expand Up @@ -688,7 +693,11 @@ public IReadOnlyList<IManagerSource> GetSources_UnSafe()
List<ManagerSource> sources = [];
INativeTaskLogger logger = Manager.TaskLogger.CreateNew(LoggableTaskType.ListSources);

foreach (PackageCatalogReference catalog in WinGetManager.GetPackageCatalogs().ToList())
foreach (
PackageCatalogReference catalog in NativeWinGetCollection.Copy(
WinGetManager.GetPackageCatalogs()
)
)
{
try
{
Expand Down Expand Up @@ -728,7 +737,12 @@ public IReadOnlyList<string> GetInstallableVersions_Unsafe(IPackage package)
if (nativePackage is null)
return [];

string[] versions = nativePackage.AvailableVersions.Select(x => x.Version).ToArray();
var nativeVersions = NativeWinGetCollection.Copy(nativePackage.AvailableVersions);
string[] versions = new string[nativeVersions.Length];
for (int index = 0; index < nativeVersions.Length; index++)
{
versions[index] = nativeVersions[index].Version;
}
foreach (string? version in versions)
{
logger.Log(version);
Expand Down Expand Up @@ -800,7 +814,7 @@ public void GetPackageDetails_UnSafe(IPackageDetails details)
details.ReleaseNotesUrl = new Uri(NativeDetails.ReleaseNotesUrl);

if (NativeDetails.Tags is not null)
details.Tags = NativeDetails.Tags.ToArray();
details.Tags = NativeWinGetCollection.Copy(NativeDetails.Tags);

bool metadataLoaded = _pingetPackageDetailsProvider.LoadPackageDetails(details, logger);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ internal static class WinGetIconsHelper
return null;

// Get the actual icon and return it
foreach (Icon? icon in NativeDetails.Icons.ToArray())
foreach (Icon? icon in NativeWinGetCollection.Copy(NativeDetails.Icons))
if (icon is not null && icon.Url is not null)
// Logger.Debug($"Found WinGet native icon for {package.Id} with URL={icon.Url}");
return new CacheableIcon(new Uri(icon.Url), icon.Sha256);
Expand Down
25 changes: 25 additions & 0 deletions src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ public void NativeWinGetHelperPrefersSystemComBeforeBundledActivation()
);
}

[Fact]
public void NativeWinGetCollectionCopiesWithoutEnumeratingWinRtCollections()
{
IReadOnlyList<string> nativeCollection = new EnumeratorThrowingReadOnlyList<string>(
["winget", "msstore"]
);

var collectionCopy = NativeWinGetCollection.Copy(nativeCollection);

Assert.Equal(["winget", "msstore"], collectionCopy);
}

[Fact]
public void GetBundledPingetExecutablePathPrefersRootExecutable()
{
Expand Down Expand Up @@ -1227,6 +1239,19 @@ public bool LoadPackageDetails(IPackageDetails details, INativeTaskLogger logger
}
}

private sealed class EnumeratorThrowingReadOnlyList<T>(IReadOnlyList<T> items) : IReadOnlyList<T>
{
public int Count => items.Count;

public T this[int index] => items[index];

IEnumerator<T> IEnumerable<T>.GetEnumerator() =>
throw new InvalidCastException("Enumeration is not available for this WinRT projection.");

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() =>
throw new InvalidCastException("Enumeration is not available for this WinRT projection.");
}

private sealed class TestNativeTaskLogger : INativeTaskLogger
{
public List<string> Lines { get; } = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Windows.Win32.System.Com;
Expand All @@ -18,40 +17,8 @@ public WindowsPackageManagerStandardFactory(
)
: base(clsidContext, allowLowerTrustRegistration) { }

[UnconditionalSuppressMessage(
"Trimming",
"IL2072",
Justification = "WinGet COM projected activation types come from the Windows Package Manager WinMD and registered COM server; this path does not depend on app-owned trimmed constructors.")]
protected override T CreateInstance<T>(Guid clsid, Guid iid)
{
if (!_allowLowerTrustRegistration)
{
Type? projectedType = Type.GetTypeFromCLSID(clsid);
if (projectedType is null)
{
throw new WinGetComActivationException(
clsid,
iid,
unchecked((int)0x80040154),
_allowLowerTrustRegistration
);
}

object? activatedInstance = Activator.CreateInstance(projectedType);
if (activatedInstance is null)
{
throw new WinGetComActivationException(
clsid,
iid,
unchecked((int)0x80004003),
_allowLowerTrustRegistration
);
}

IntPtr pointer = Marshal.GetIUnknownForObject(activatedInstance);
return MarshalGeneric<T>.FromAbi(pointer);
}

CLSCTX clsctx = CLSCTX.CLSCTX_LOCAL_SERVER;
if (_allowLowerTrustRegistration)
{
Expand All @@ -76,7 +43,17 @@ out IntPtr instance
);
}

return MarshalGeneric<T>.FromAbi(instance);
try
{
return MarshalGeneric<T>.FromAbi(instance);
}
finally
{
if (instance != IntPtr.Zero)
{
Marshal.Release(instance);
}
}
}

[DllImport(
Expand Down
Loading