From f5a2560358f57e01948ecd3856abf64adc2cb1e8 Mon Sep 17 00:00:00 2001 From: theKBro Date: Wed, 21 Oct 2026 14:28:43 +0200 Subject: [PATCH 1/2] Fixes uninstall dispatch for EXE/Burn ARP entries whose registry subkey is GUID-shaped. --- .../Devolutions.Pinget.Core.Tests/CoreTests.cs | 10 ++++++++++ .../InstallerDispatch.cs | 13 +++++++++++-- rust/crates/pinget-core/src/lib.rs | 17 +++++++++++++++-- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/dotnet/src/Devolutions.Pinget.Core.Tests/CoreTests.cs b/dotnet/src/Devolutions.Pinget.Core.Tests/CoreTests.cs index 261c998..28ec4e6 100644 --- a/dotnet/src/Devolutions.Pinget.Core.Tests/CoreTests.cs +++ b/dotnet/src/Devolutions.Pinget.Core.Tests/CoreTests.cs @@ -2203,6 +2203,16 @@ public void RegistryEntryMatchesInstalledPackage_UsesLocalIdentityInsteadOfCorre Assert.False(InstallerDispatch.RegistryEntryMatchesInstalledPackage("ShareX.ShareX", "ShareX.ShareX", null, installed)); } + [Theory] + [InlineData(null, false)] + [InlineData(0, false)] + [InlineData(1, true)] + public void IsMsiArpEntry_RequiresWindowsInstallerDword(int? windowsInstaller, bool expected) + { + // A GUID-shaped Burn/EXE key must not be treated as an MSI ProductCode. + Assert.Equal(expected, InstallerDispatch.IsMsiArpEntry(windowsInstaller)); + } + [Fact] public void CorrelateInstalledPackage_TiebreaksOnUnnormalizedNamePrefix() { diff --git a/dotnet/src/Devolutions.Pinget.Core/InstallerDispatch.cs b/dotnet/src/Devolutions.Pinget.Core/InstallerDispatch.cs index a4b2a47..36de9b6 100644 --- a/dotnet/src/Devolutions.Pinget.Core/InstallerDispatch.cs +++ b/dotnet/src/Devolutions.Pinget.Core/InstallerDispatch.cs @@ -83,7 +83,8 @@ private static bool TryUninstallArp(ListMatch installed, UninstallRequest reques if (!RegistryEntryMatchesInstalledPackage(subkeyName, displayName, productCode, installed)) continue; - if (TryRunMsiUninstall(installed, subkeyName, productCode, request, out exitCode)) + if (IsMsiArpEntry(subkey.GetValue("WindowsInstaller")) && + TryRunMsiUninstall(installed, subkeyName, productCode, request, out exitCode)) return true; var quietUninstallCmd = subkey.GetValue("QuietUninstallString") as string; @@ -1013,7 +1014,15 @@ internal static string BuildMsixUninstallScript(string? packageFullName, IReadOn "$targets | Remove-AppxPackage"; } - private static bool TryRunMsiUninstall(ListMatch installed, string subkeyName, string? productCode, UninstallRequest request, out int exitCode) + internal static bool IsMsiArpEntry(object? windowsInstaller) + => windowsInstaller is int value && value == 1; + + private static bool TryRunMsiUninstall( + ListMatch installed, + string subkeyName, + string? productCode, + UninstallRequest request, + out int exitCode) { exitCode = 0; var uninstallCode = installed.ProductCodes diff --git a/rust/crates/pinget-core/src/lib.rs b/rust/crates/pinget-core/src/lib.rs index bd6ba2c..08faf61 100644 --- a/rust/crates/pinget-core/src/lib.rs +++ b/rust/crates/pinget-core/src/lib.rs @@ -9944,8 +9944,9 @@ fn try_uninstall_arp(installed: &ListMatch, request: &UninstallRequest) -> Resul continue; } - if let Some(exit_code) = - try_run_msi_uninstall(installed, &subkey_name, product_code.as_deref(), request)? + if is_msi_arp_entry(read_reg_dword(&subkey, "WindowsInstaller")) + && let Some(exit_code) = + try_run_msi_uninstall(installed, &subkey_name, product_code.as_deref(), request)? { return Ok(Some(exit_code)); } @@ -10195,6 +10196,10 @@ fn build_msix_uninstall_script(package_full_name: Option<&str>, package_family_n ) } +fn is_msi_arp_entry(windows_installer: Option) -> bool { + windows_installer == Some(1) +} + #[cfg(windows)] fn try_run_msi_uninstall( installed: &ListMatch, @@ -15272,6 +15277,14 @@ Installers: )); } + #[test] + fn msi_arp_dispatch_requires_windows_installer_dword() { + // A GUID-shaped Burn/EXE key must not be treated as an MSI ProductCode. + assert!(!is_msi_arp_entry(None)); + assert!(!is_msi_arp_entry(Some(0))); + assert!(is_msi_arp_entry(Some(1))); + } + #[test] #[cfg(windows)] fn build_uninstall_command_only_appends_silent_flag_when_needed() { From fed7d0eab0a4b9e8034ef5c1be7aa9a11e8643ae Mon Sep 17 00:00:00 2001 From: theKBro Date: Wed, 21 Oct 2026 14:38:32 +0200 Subject: [PATCH 2/2] Add conditional compilation for MSI ARP entry check on Windows --- rust/crates/pinget-core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/crates/pinget-core/src/lib.rs b/rust/crates/pinget-core/src/lib.rs index 08faf61..f9f56d0 100644 --- a/rust/crates/pinget-core/src/lib.rs +++ b/rust/crates/pinget-core/src/lib.rs @@ -10196,6 +10196,7 @@ fn build_msix_uninstall_script(package_full_name: Option<&str>, package_family_n ) } +#[cfg(any(windows, test))] fn is_msi_arp_entry(windows_installer: Option) -> bool { windows_installer == Some(1) }