From 3ab87486511e7b4be3043abeea00a22ba45ea181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 17 Apr 2026 08:57:54 -0400 Subject: [PATCH] Fix WinGet activation fallback order Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ClientHelpers/NativeWinGetHelper.cs | 67 ++++++++++++++----- .../WinGetManagerTests.cs | 35 ++++++++++ .../ManagersPages/PackageManager.xaml.cs | 24 ++----- .../SoftwarePages/InstalledPackagesPage.cs | 7 +- 4 files changed, 97 insertions(+), 36 deletions(-) diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs index 232559b3a..9d8e31c58 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs @@ -21,14 +21,31 @@ internal sealed class NativeWinGetHelper : IWinGetManagerHelper public PackageManager WinGetManager = null!; public static PackageManager? ExternalWinGetManager; private readonly WinGet Manager; + private readonly Func _bundledHelperFactory; public string ActivationMode { get; private set; } = string.Empty; public string ActivationSource { get; private set; } = string.Empty; private bool _isBundledActivation; + internal static IReadOnlyList PreferredActivationModes => + [ + "packaged COM registration", + "lower-trust COM registration", + "bundled in-proc COM", + ]; + public NativeWinGetHelper(WinGet manager) + : this(manager, bundledHelperFactory: null, skipInitialization: false) { } + + internal NativeWinGetHelper( + WinGet manager, + Func? bundledHelperFactory, + bool skipInitialization + ) { Manager = manager; + _bundledHelperFactory = + bundledHelperFactory ?? (static manager => new BundledWinGetHelper(manager)); if (CoreTools.IsAdministrator()) { Logger.Info( @@ -36,7 +53,7 @@ public NativeWinGetHelper(WinGet manager) ); } - if (TryInitializeBundledFactory()) + if (skipInitialization) { return; } @@ -46,36 +63,47 @@ public NativeWinGetHelper(WinGet manager) return; } - InitializeLowerTrustFactory(); + if (TryInitializeLowerTrustFactory()) + { + return; + } + + InitializeBundledFactory(); } - private bool TryInitializeBundledFactory() + internal void UseBundledActivationForTesting() + { + _isBundledActivation = true; + ActivationMode = "bundled in-proc COM"; + ActivationSource = "test"; + } + + private bool TryInitializeLowerTrustFactory() { try { - var factory = new WindowsPackageManagerBundledFactory(); + var factory = new WindowsPackageManagerStandardFactory(allowLowerTrustRegistration: true); var winGetManager = factory.CreatePackageManager(); ApplyFactory( factory, winGetManager, - "bundled in-proc COM", - factory.LibraryPath, - "Connected to WinGet API using bundled in-proc activation." + "lower-trust COM registration", + "system COM registration (allow lower trust)", + "Connected to WinGet API using lower-trust COM activation." ); - _isBundledActivation = true; return true; } catch (WinGetComActivationException ex) { Logger.Warn( - $"Bundled WinGet in-proc activation failed ({ex.HResultHex}: {ex.Reason}), attempting packaged COM activation..." + $"Lower-trust WinGet COM activation failed ({ex.HResultHex}: {ex.Reason}), attempting bundled in-proc activation..." ); return false; } catch (Exception ex) { Logger.Warn( - $"Bundled WinGet in-proc activation failed ({ex.Message}), attempting packaged COM activation..." + $"Lower-trust WinGet COM activation failed ({ex.Message}), attempting bundled in-proc activation..." ); return false; } @@ -112,17 +140,18 @@ private bool TryInitializeStandardFactory() } } - private void InitializeLowerTrustFactory() + private void InitializeBundledFactory() { - var factory = new WindowsPackageManagerStandardFactory(allowLowerTrustRegistration: true); + var factory = new WindowsPackageManagerBundledFactory(); var winGetManager = factory.CreatePackageManager(); ApplyFactory( factory, winGetManager, - "lower-trust COM registration", - "system COM registration (allow lower trust)", - "Connected to WinGet API using lower-trust COM activation." + "bundled in-proc COM", + factory.LibraryPath, + "Connected to WinGet API using bundled in-proc activation." ); + _isBundledActivation = true; } private void ApplyFactory( @@ -273,7 +302,7 @@ public IReadOnlyList GetAvailableUpdates_UnSafe() if (_isBundledActivation) { Logger.Info("WinGet is using bundled in-proc COM — falling back to CLI for update detection"); - return new BundledWinGetHelper(Manager).GetAvailableUpdates_UnSafe(); + return _bundledHelperFactory(Manager).GetAvailableUpdates_UnSafe(); } var logger = Manager.TaskLogger.CreateNew(LoggableTaskType.ListUpdates); @@ -344,6 +373,12 @@ var nativePackage in TaskRecycler>.RunOrAttach( public IReadOnlyList GetInstalledPackages_UnSafe() { + if (_isBundledActivation) + { + Logger.Info("WinGet is using bundled in-proc COM — falling back to CLI for installed package detection"); + return _bundledHelperFactory(Manager).GetInstalledPackages_UnSafe(); + } + var logger = Manager.TaskLogger.CreateNew(LoggableTaskType.ListInstalledPackages); List packages = []; foreach ( diff --git a/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs b/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs index 6ed59ba04..e27f75052 100644 --- a/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs +++ b/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs @@ -130,6 +130,41 @@ public void GetInstalledPackagesUpdatesNoPackagesFlagForFailureAndRecovery() PackageAssert.Matches(Assert.Single(packages), "Contoso Tool", "Contoso.Tool", "1.2.3"); } + [Fact] + public void NativeWinGetHelperPrefersSystemComBeforeBundledActivation() + { + Assert.Equal( + ["packaged COM registration", "lower-trust COM registration", "bundled in-proc COM"], + NativeWinGetHelper.PreferredActivationModes + ); + } + + [Fact] + public void NativeWinGetHelperUsesBundledFallbackForInstalledPackagesWhenBundledActivationIsSelected() + { + var manager = new TestableWinGet(); + var expectedPackage = new PackageBuilder() + .WithManager(manager) + .WithName("Contoso Tool") + .WithId("Contoso.Tool") + .WithVersion("1.2.3") + .Build(); + var bundledFallbackHelper = new TestWinGetManagerHelper + { + GetInstalledPackagesHandler = () => [expectedPackage], + }; + var helper = new NativeWinGetHelper( + manager, + bundledHelperFactory: _ => bundledFallbackHelper, + skipInitialization: true + ); + helper.UseBundledActivationForTesting(); + + var packages = helper.GetInstalledPackages_UnSafe(); + + PackageAssert.Matches(Assert.Single(packages), "Contoso Tool", "Contoso.Tool", "1.2.3"); + } + private sealed class TestableWinGet : WinGet { public IReadOnlyList InvokeGetInstalledPackages() => base.GetInstalledPackages_UnSafe(); diff --git a/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml.cs b/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml.cs index e0c77ce12..4a8473c0d 100644 --- a/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml.cs +++ b/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml.cs @@ -193,17 +193,18 @@ protected override void OnNavigatedTo(NavigationEventArgs e) Text = $"{CoreTools.Translate("Use bundled WinGet instead of system WinGet")} ({CoreTools.Translate("This may help if WinGet packages are not shown")})", SettingName = Settings.K.ForceLegacyBundledWinGet, - CornerRadius = new CornerRadius(0, 0, 8, 8), - BorderThickness = new Thickness(1, 0, 1, 1), + CornerRadius = new CornerRadius(0), + BorderThickness = new Thickness(1, 0, 1, 0), }; WinGet_UseBundled.StateChanged += (_, _) => _ = ReloadPackageManager(); ExtraControls.Children.Add(WinGet_UseBundled); - /*CheckboxCard WinGet_EnableTroubleshooter = new() + CheckboxCard WinGet_EnableTroubleshooter = new() { Text = CoreTools.Translate("Enable the automatic WinGet troubleshooter"), SettingName = Settings.K.DisableWinGetMalfunctionDetector, - CornerRadius = new CornerRadius(0), + CornerRadius = new CornerRadius(0, 0, 8, 8), + BorderThickness = new Thickness(1, 0, 1, 1), }; WinGet_EnableTroubleshooter.StateChanged += (_, _) => { @@ -212,21 +213,6 @@ protected override void OnNavigatedTo(NavigationEventArgs e) }; ExtraControls.Children.Add(WinGet_EnableTroubleshooter); - CheckboxCard WinGet_EnableTroubleshooter_v2 = new() - { - Text = CoreTools.Translate("Enable an [experimental] improved WinGet troubleshooter"), - SettingName = Settings.K.DisableNewWinGetTroubleshooter, - // CornerRadius = new CornerRadius(0), - CornerRadius = new CornerRadius(0, 0, 8, 8), - BorderThickness = new Thickness(1, 0, 1, 0), - }; - WinGet_EnableTroubleshooter_v2.StateChanged += (_, _) => - { - MainApp.Instance.MainWindow.WinGetWarningBanner.IsOpen = false; - _ = InstalledPackagesLoader.Instance.ReloadPackages(); - }; - ExtraControls.Children.Add(WinGet_EnableTroubleshooter_v2);*/ - /*CheckboxCard WinGet_HideNonApplicableUpdates = new() { Text = CoreTools.Translate("Add updates that fail with a 'no applicable update found' to the ignored updates list"), diff --git a/src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs b/src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs index e47858ae3..f2cc2c49a 100644 --- a/src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs +++ b/src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs @@ -309,12 +309,12 @@ protected override void WhenPackagesLoaded(ReloadReason reason) } } + var infoBar = MainApp.Instance.MainWindow.WinGetWarningBanner; if ( WinGet.NO_PACKAGES_HAVE_BEEN_LOADED && !Settings.Get(Settings.K.DisableWinGetMalfunctionDetector) ) { - var infoBar = MainApp.Instance.MainWindow.WinGetWarningBanner; infoBar.IsOpen = true; infoBar.Title = CoreTools.Translate("WinGet malfunction detected"); infoBar.Message = CoreTools.Translate( @@ -324,6 +324,11 @@ protected override void WhenPackagesLoaded(ReloadReason reason) infoBar.ActionButton = button; button.Click += (_, _) => _ = DialogHelper.HandleBrokenWinGet(); } + else + { + infoBar.IsOpen = false; + infoBar.ActionButton = null; + } } protected override void WhenShowingContextMenu(IPackage package) =>