Skip to content
Draft
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 @@ -21,22 +21,39 @@ internal sealed class NativeWinGetHelper : IWinGetManagerHelper
public PackageManager WinGetManager = null!;
public static PackageManager? ExternalWinGetManager;
private readonly WinGet Manager;
private readonly Func<WinGet, IWinGetManagerHelper> _bundledHelperFactory;

public string ActivationMode { get; private set; } = string.Empty;
public string ActivationSource { get; private set; } = string.Empty;
private bool _isBundledActivation;

internal static IReadOnlyList<string> 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<WinGet, IWinGetManagerHelper>? bundledHelperFactory,
bool skipInitialization
)
{
Manager = manager;
_bundledHelperFactory =
bundledHelperFactory ?? (static manager => new BundledWinGetHelper(manager));
if (CoreTools.IsAdministrator())
{
Logger.Info(
"Running elevated, WinGet class registration is likely to fail unless using lower trust class registration is allowed in settings"
);
}

if (TryInitializeBundledFactory())
if (skipInitialization)
{
return;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -273,7 +302,7 @@ public IReadOnlyList<Package> 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);
Expand Down Expand Up @@ -344,6 +373,12 @@ var nativePackage in TaskRecycler<IReadOnlyList<CatalogPackage>>.RunOrAttach(

public IReadOnlyList<Package> 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<Package> packages = [];
foreach (
Expand Down
35 changes: 35 additions & 0 deletions src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Package> InvokeGetInstalledPackages() => base.GetInstalledPackages_UnSafe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 += (_, _) =>
{
Expand All @@ -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"),
Expand Down
7 changes: 6 additions & 1 deletion src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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) =>
Expand Down
Loading