From 5d532574b351d30a0ef91a8344265b7ff889f82e Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Thu, 20 Mar 2025 21:14:12 -0400 Subject: Update registration disappears during upgrade. Update registration is stored in a shared registry key that Burn takes care to keep around across upgrades. The approach it used broke between WiX v3 and WiX v5. This change makes it work again by not rewriting update registration during upgrade uninstall. --- src/burn/engine/registration.cpp | 2 +- .../BundleAv1/Bundle.wxs | 58 ++++++++++++++++++++++ .../BundleAv1/BundleA.props | 7 +++ .../BundleAv1/BundleAv1.wixproj | 12 +++++ .../BundleAv1/BundleAv1.wxs | 10 ++++ .../BundleAv2/BundleAv2.wixproj | 18 +++++++ .../BundleAv2/BundleAv2.wxs | 10 ++++ .../PackageAv1/PackageA.props | 9 ++++ .../PackageAv1/PackageAv1.wixproj | 4 ++ .../PackageAv2/PackageAv2.wixproj | 7 +++ .../burn/WixTestTools/BundleUpdateRegistration.cs | 57 +++++++++++++++++++++ src/test/burn/WixTestTools/BundleVerifier.cs | 37 ++++++++++++++ .../OptionalUpdateRegistrationTests.cs | 40 +++++++++++++++ 13 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/Bundle.wxs create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleA.props create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleAv1.wixproj create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleAv1.wxs create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv2/BundleAv2.wixproj create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv2/BundleAv2.wxs create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv1/PackageA.props create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv1/PackageAv1.wixproj create mode 100644 src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv2/PackageAv2.wixproj create mode 100644 src/test/burn/WixTestTools/BundleUpdateRegistration.cs create mode 100644 src/test/burn/WixToolsetTest.BurnE2E/OptionalUpdateRegistrationTests.cs diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp index 85c006f7..7e00e305 100644 --- a/src/burn/engine/registration.cpp +++ b/src/burn/engine/registration.cpp @@ -809,7 +809,7 @@ extern "C" HRESULT RegistrationSessionBegin( } // Update registration. - if (pRegistration->update.fRegisterUpdate) + if (pRegistration->update.fRegisterUpdate && BOOTSTRAPPER_REGISTRATION_TYPE_FULL == registrationType) { hr = WriteUpdateRegistration(pRegistration, pVariables); ExitOnFailure(hr, "Failed to write update registration."); diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/Bundle.wxs b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/Bundle.wxs new file mode 100644 index 00000000..2e920297 --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/Bundle.wxs @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleA.props b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleA.props new file mode 100644 index 00000000..1887f94e --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleA.props @@ -0,0 +1,7 @@ + + + + Bundle + {9510DDE7-CD4F-45B3-9D57-3F7EA04DB33D} + + diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleAv1.wixproj b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleAv1.wixproj new file mode 100644 index 00000000..0dea1e40 --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleAv1.wixproj @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleAv1.wxs b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleAv1.wxs new file mode 100644 index 00000000..7bf16212 --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv1/BundleAv1.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv2/BundleAv2.wixproj b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv2/BundleAv2.wixproj new file mode 100644 index 00000000..271bf54b --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv2/BundleAv2.wixproj @@ -0,0 +1,18 @@ + + + + + 2.0.0.0 + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv2/BundleAv2.wxs b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv2/BundleAv2.wxs new file mode 100644 index 00000000..5cbee5a8 --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/BundleAv2/BundleAv2.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv1/PackageA.props b/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv1/PackageA.props new file mode 100644 index 00000000..25ae3f42 --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv1/PackageA.props @@ -0,0 +1,9 @@ + + + + {30746E93-9A4A-48EC-BAEA-3093A2530E73} + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv1/PackageAv1.wixproj b/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv1/PackageAv1.wixproj new file mode 100644 index 00000000..45d3b2c8 --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv1/PackageAv1.wixproj @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv2/PackageAv2.wixproj b/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv2/PackageAv2.wixproj new file mode 100644 index 00000000..d4455f2b --- /dev/null +++ b/src/test/burn/TestData/OptionalUpdateRegistrationTests/PackageAv2/PackageAv2.wixproj @@ -0,0 +1,7 @@ + + + + + 2.0.0.0 + + diff --git a/src/test/burn/WixTestTools/BundleUpdateRegistration.cs b/src/test/burn/WixTestTools/BundleUpdateRegistration.cs new file mode 100644 index 00000000..2ce68122 --- /dev/null +++ b/src/test/burn/WixTestTools/BundleUpdateRegistration.cs @@ -0,0 +1,57 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixTestTools +{ + using System; + using Microsoft.Win32; + + public class BundleUpdateRegistration + { + public const string BURN_UPDATE_REGISTRATION_REGISTRY_BUNDLE_PACKAGE_NAME = "PackageName"; + public const string BURN_UPDATE_REGISTRATION_REGISTRY_BUNDLE_PACKAGE_VERSION = "PackageVersion"; + public const string BURN_UPDATE_REGISTRATION_REGISTRY_BUNDLE_PUBLISHER = "Publisher"; + public const string BURN_UPDATE_REGISTRATION_REGISTRY_BUNDLE_PUBLISHING_GROUP = "PublishingGroup"; + + public string PackageName { get; set; } + + public string PackageVersion { get; set; } + + public string Publisher { get; set; } + + public string PublishingGroup { get; set; } + + public static bool TryGetPerMachineBundleUpdateRegistration(string manufacturer, string productFamily, string name, bool x64, out BundleUpdateRegistration registration) + { + return TryGetUpdateRegistration(manufacturer, productFamily, name, x64, perUser: false, out registration); + } + + public static bool TryGetPerUserBundleUpdateRegistration(string manufacturer, string productFamily, string name, out BundleUpdateRegistration registration) + { + return TryGetUpdateRegistration(manufacturer, productFamily, name, x64: true, perUser: true, out registration); + } + + private static bool TryGetUpdateRegistration(string manufacturer, string productFamily, string name, bool x64, bool perUser, out BundleUpdateRegistration registration) + { + var baseKey = perUser ? Registry.CurrentUser : Registry.LocalMachine; + var baseKeyPath = x64 ? @$"SOFTWARE\{manufacturer}\Updates\{productFamily}\{name}" + : @$"SOFTWARE\WOW6432Node\{manufacturer}\Updates\{productFamily}\{name}"; + using var idKey = baseKey.OpenSubKey(baseKeyPath); + + if (idKey == null) + { + registration = null; + return false; + } + + registration = new BundleUpdateRegistration() + { + PackageName = idKey.GetValue(BURN_UPDATE_REGISTRATION_REGISTRY_BUNDLE_PACKAGE_NAME) as string, + PackageVersion = idKey.GetValue(BURN_UPDATE_REGISTRATION_REGISTRY_BUNDLE_PACKAGE_VERSION) as string, + Publisher = idKey.GetValue(BURN_UPDATE_REGISTRATION_REGISTRY_BUNDLE_PUBLISHER) as string, + PublishingGroup = idKey.GetValue(BURN_UPDATE_REGISTRATION_REGISTRY_BUNDLE_PUBLISHING_GROUP) as string, + }; + + return true; + } + } +} diff --git a/src/test/burn/WixTestTools/BundleVerifier.cs b/src/test/burn/WixTestTools/BundleVerifier.cs index da61d96e..b6181047 100644 --- a/src/test/burn/WixTestTools/BundleVerifier.cs +++ b/src/test/burn/WixTestTools/BundleVerifier.cs @@ -6,6 +6,7 @@ namespace WixTestTools using System.IO; using System.Linq; using System.Text; + using System.Xml.Linq; using Microsoft.Win32; using WixInternal.TestSupport; using WixToolset.Data; @@ -23,6 +24,8 @@ namespace WixTestTools private WixBundleSymbol BundleSymbol { get; set; } + private WixUpdateRegistrationSymbol UpdateRegistrationSymbol { get; set; } + private WixBundleSymbol GetBundleSymbol() { if (this.BundleSymbol == null) @@ -36,6 +39,19 @@ namespace WixTestTools return this.BundleSymbol; } + private WixUpdateRegistrationSymbol GetUpdateRegistrationSymbol() + { + if (this.UpdateRegistrationSymbol == null) + { + using var wixOutput = WixOutput.Read(this.BundlePdb); + var intermediate = Intermediate.Load(wixOutput); + var section = intermediate.Sections.Single(); + this.UpdateRegistrationSymbol = section.Symbols.OfType().Single(); + } + + return this.UpdateRegistrationSymbol; + } + public string GetFullBurnPolicyRegistryPath() { var bundleSymbol = this.GetBundleSymbol(); @@ -118,6 +134,27 @@ namespace WixTestTools } } + public bool TryGetUpdateRegistration(out BundleUpdateRegistration registration) + { + var bundleSymbol = this.GetBundleSymbol(); + var x64 = bundleSymbol.Platform != Platform.X86; + + var updateRegistrationSymbol = this.GetUpdateRegistrationSymbol(); + var manufacturer = updateRegistrationSymbol.Manufacturer; + var productFamily = updateRegistrationSymbol.ProductFamily; + var name = updateRegistrationSymbol.Name; + + + if (bundleSymbol.PerMachine) + { + return BundleUpdateRegistration.TryGetPerMachineBundleUpdateRegistration(manufacturer, productFamily, name, x64, out registration); + } + else + { + return BundleUpdateRegistration.TryGetPerUserBundleUpdateRegistration(manufacturer, productFamily, name, out registration); + } + } + public BundleRegistration VerifyRegisteredAndInPackageCache(int? expectedSystemComponent = null) { Assert.True(this.TryGetRegistration(out var registration)); diff --git a/src/test/burn/WixToolsetTest.BurnE2E/OptionalUpdateRegistrationTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/OptionalUpdateRegistrationTests.cs new file mode 100644 index 00000000..a26a49e5 --- /dev/null +++ b/src/test/burn/WixToolsetTest.BurnE2E/OptionalUpdateRegistrationTests.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolsetTest.BurnE2E +{ + using WixTestTools; + using Xunit; + using Xunit.Abstractions; + + public class OptionalUpdateRegistrationTests : BurnE2ETests + { + public OptionalUpdateRegistrationTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } + + [RuntimeFact] + public void BundleUpdateRegistrationIsStickyAndAccurateAcrossUpgrades() + { + var packageAv1 = this.CreatePackageInstaller("PackageAv1"); + var packageAv2 = this.CreatePackageInstaller("PackageAv2"); + var bundleAv1 = this.CreateBundleInstaller("BundleAv1"); + var bundleAv2 = this.CreateBundleInstaller("BundleAv2"); + + bundleAv1.Install(); + bundleAv1.VerifyRegisteredAndInPackageCache(); + var gotV1Registration = bundleAv1.TryGetUpdateRegistration(out var v1Registration); + + bundleAv2.Install(); + bundleAv2.VerifyRegisteredAndInPackageCache(); + var gotV2Registration = bundleAv2.TryGetUpdateRegistration(out var v2Registration); + + bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache(); + + Assert.True(gotV1Registration, "Missing v1 update registration."); + Assert.True(gotV2Registration, "Missing v2 update registration."); + + Assert.Equal(v1Registration?.Publisher, v2Registration?.Publisher); + Assert.Equal(v1Registration?.PublishingGroup, v2Registration?.PublishingGroup); + Assert.Equal(v1Registration?.PackageName, v2Registration?.PackageName); + Assert.NotEqual(v1Registration?.PackageVersion, v2Registration?.PackageVersion); + } + } +} -- cgit v1.2.3-55-g6feb