Skip to content
/ wix Public

Commit 49924eb

Browse files
committed
Allow patched package to be uninstalled.
Normally, a patched package is detected as superseded and therefore a normal uninstall takes no action. This change looks for applied patches and allows a normal uninstall to remove the package. Fixes wixtoolset/issues#6350
1 parent 8b9538a commit 49924eb

File tree

8 files changed

+164
-4
lines changed

8 files changed

+164
-4
lines changed

src/burn/engine/msiengine.cpp

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ static void RegisterSourceDirectory(
5050
__in BURN_PACKAGE* pPackage,
5151
__in_z LPCWSTR wzCacheDirectory
5252
);
53-
53+
static BOOL PackageHasAppliedPatch(
54+
__in BURN_PACKAGE* pPackage
55+
);
5456

5557
// function definitions
5658

@@ -904,7 +906,22 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage(
904906
else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) &&
905907
!pPackage->fPermanent) // removing a package that should be removed.
906908
{
907-
execute = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == pPackage->currentState ? BOOTSTRAPPER_ACTION_STATE_NONE : BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
909+
if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == pPackage->currentState)
910+
{
911+
// If the package is superseded, check to see if there's a patch installed.
912+
// A minor upgrade patch could be (usually is) the cause of the
913+
// supersedence. In that case, we should ignore the supersedence that would
914+
// normally prevent the uninstall. There is a gap in this logic: If a minor
915+
// upgrade package were installed without a bundle, then a small update patch
916+
// (which by definition doesn't change the version number) were installed,
917+
// this check would allow the uninstall. If the minor upgrade were installed
918+
// by a bundle, dependencies would keep the package installed.
919+
execute = PackageHasAppliedPatch(pPackage) ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
920+
}
921+
else
922+
{
923+
execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
924+
}
908925
}
909926
else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested)
910927
{
@@ -2265,6 +2282,7 @@ static HRESULT ConcatPatchProperty(
22652282
ReleaseStr(sczMspPath);
22662283
ReleaseStr(sczCachedDirectory);
22672284
ReleaseStr(sczPatches);
2285+
22682286
return hr;
22692287
}
22702288

@@ -2289,6 +2307,47 @@ static void RegisterSourceDirectory(
22892307

22902308
LExit:
22912309
ReleaseStr(sczMsiDirectory);
2310+
}
2311+
2312+
static BOOL PackageHasAppliedPatch(
2313+
__in BURN_PACKAGE* pPackage
2314+
)
2315+
{
2316+
HRESULT hr = S_OK;
2317+
BOOL fPatched = FALSE;
2318+
UINT er = ERROR_SUCCESS;
2319+
DWORD iPatch = 0;
2320+
WCHAR wzPatchCode[MAX_GUID_CHARS + 1] = {};
2321+
WCHAR wzTransforms[MAX_PATH] = {};
2322+
DWORD cchTransforms = countof(wzTransforms);
2323+
WCHAR wzPatchState[2] = {};
2324+
DWORD cchPatchState = countof(wzPatchState);
2325+
2326+
for (;;)
2327+
{
2328+
er = ::MsiEnumPatchesW(pPackage->Msi.sczProductCode, iPatch, wzPatchCode, wzTransforms, &cchTransforms);
2329+
2330+
if (ERROR_NO_MORE_ITEMS == er)
2331+
{
2332+
ExitFunction();
2333+
}
2334+
ExitOnWin32Error(er, hr, "Failed to enumerate patches for package %ls, product code %ls.", pPackage->sczId, pPackage->Msi.sczProductCode);
2335+
2336+
er = ::MsiGetPatchInfoExW(wzPatchCode, pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED
2337+
, INSTALLPROPERTY_PATCHSTATE, wzPatchState, &cchPatchState);
2338+
ExitOnWin32Error(er, hr, "Failed to get patch info for patch %ls.", wzPatchCode);
2339+
2340+
if ('1' == wzPatchState[0])
2341+
{
2342+
fPatched = TRUE;
2343+
2344+
ExitFunction();
2345+
}
2346+
2347+
++iPatch;
2348+
}
2349+
2350+
LExit:
2351+
return fPatched;
22922352

2293-
return;
22942353
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!-- 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. -->
2+
<Project Sdk="WixToolset.Sdk">
3+
<PropertyGroup>
4+
<OutputType>Bundle</OutputType>
5+
<UpgradeCode>{62C28DAF-A13E-4F55-ACA1-FB843630789C}</UpgradeCode>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
9+
</ItemGroup>
10+
<ItemGroup>
11+
<ProjectReference Include="..\PackageAv1\PackageAv1.wixproj" />
12+
<ProjectReference Include="..\PatchAMinor\PatchAMinor.wixproj" />
13+
<ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
14+
</ItemGroup>
15+
<ItemGroup>
16+
<PackageReference Include="WixToolset.BootstrapperApplications.wixext" />
17+
<PackageReference Include="WixToolset.NetFx.wixext" />
18+
</ItemGroup>
19+
</Project>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!-- 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. -->
2+
3+
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
4+
<Fragment>
5+
<PackageGroup Id="BundlePackages">
6+
<MsiPackage Id="PackageA" SourceFile="$(var.PackageAv1.TargetPath)" Compressed="no">
7+
<SlipstreamMsp Id="PatchAMinor" />
8+
</MsiPackage>
9+
<MspPackage Id="PatchAMinor" SourceFile="$(var.PatchAMinor.TargetPath)" Compressed="no" />
10+
</PackageGroup>
11+
</Fragment>
12+
</Wix>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!-- 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. -->
2+
<Project Sdk="WixToolset.Sdk">
3+
<PropertyGroup>
4+
<OutputType>Patch</OutputType>
5+
<TargetExt>.msp</TargetExt>
6+
<SuppressSpecificWarnings>1079</SuppressSpecificWarnings>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<ProjectReference Include="..\PackageAv1\PackageAv1.wixproj" />
10+
<ProjectReference Include="..\PackageAv1_0_1\PackageAv1_0_1.wixproj" />
11+
</ItemGroup>
12+
</Project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!-- 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. -->
2+
3+
<?ifndef TestVersion?>
4+
<?define TestVersion = 1.0.0.0?>
5+
<?endif?>
6+
7+
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
8+
<Patch AllowRemoval="yes" Classification="Update" Description="Patch A minor upgrade in test $(var.TestGroupName)" DisplayName="$(var.TestGroupName) - Patch A minor upgrade" Manufacturer="Example Corporation" MinorUpdateTargetRTM="yes">
9+
<Media Id="100" Cabinet="PatchA" EmbedCab="yes">
10+
<PatchBaseline
11+
Id="PatchA"
12+
BaselineFile="$(var.PackageAv1.TargetDir)$(var.PackageAv1.TargetName).wixpdb"
13+
UpdateFile="$(var.PackageAv1_0_1.TargetDir)$(var.PackageAv1_0_1.TargetName).wixpdb"
14+
/>
15+
</Media>
16+
17+
<PatchFamily Id="A" Version="$(var.TestVersion)" Supersede="yes">
18+
<ComponentRef Id="RegistryComponent" />
19+
<PropertyRef Id="TestVersion" />
20+
<PropertyRef Id="ProductVersion" />
21+
</PatchFamily>
22+
</Patch>
23+
</Wix>

src/test/burn/WixToolsetTest.BurnE2E/SlipstreamTests.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public SlipstreamTests(ITestOutputHelper testOutputHelper) : base(testOutputHelp
1818
private const string V101 = "1.0.1.0";
1919

2020
[RuntimeFact]
21-
public void CanInstallBundleWithSlipstreamedPatchThenRemoveIt()
21+
public void CanInstallBundleWithSlipstreamedSmallUpdatePatchThenRemoveIt()
2222
{
2323
var testRegistryValue = "PackageA";
2424

@@ -39,6 +39,28 @@ public void CanInstallBundleWithSlipstreamedPatchThenRemoveIt()
3939
packageAv1.VerifyTestRegistryRootDeleted();
4040
}
4141

42+
[RuntimeFact]
43+
public void CanInstallBundleWithSlipstreamedMinorUpgradePatchThenRemoveIt()
44+
{
45+
var testRegistryValue = "PackageA";
46+
47+
var packageAv1 = this.CreatePackageInstaller("PackageAv1");
48+
var bundleA = this.CreateBundleInstaller("BundleAv1_0_1");
49+
50+
var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs");
51+
Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}");
52+
53+
bundleA.Install();
54+
bundleA.VerifyRegisteredAndInPackageCache();
55+
Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled));
56+
packageAv1.VerifyTestRegistryValue(testRegistryValue, V101);
57+
58+
bundleA.Uninstall();
59+
bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
60+
Assert.False(File.Exists(packageAv1SourceCodeInstalled), String.Concat("PackageAv1 payload should have been removed by uninstall from: ", packageAv1SourceCodeInstalled));
61+
packageAv1.VerifyTestRegistryRootDeleted();
62+
}
63+
4264
/// <summary>
4365
/// BundleA installs PackageA with slipstreamed PatchA.
4466
/// BundleOnlyPatchA is installed which contains PatchA (which should be a no-op).

src/test/sandbox/TestSandbox.wsb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
<HostFolder>..\..\..\build</HostFolder>
55
<SandboxFolder>C:\build</SandboxFolder>
66
<ReadOnly>true</ReadOnly>
7+
</MappedFolder>
8+
<MappedFolder>
9+
<HostFolder>..\..\..\build\logs</HostFolder>
10+
<SandboxFolder>C:\logs</SandboxFolder>
11+
<ReadOnly>false</ReadOnly>
712
</MappedFolder>
813
<MappedFolder>
914
<HostFolder>.\</HostFolder>

src/test/sandbox/startup.bat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
@setlocal
22
@echo off
3+
4+
5+
::// Fix Sandbox glacial perf (24H2-???)
6+
:://
7+
REG ADD HKLM\SYSTEM\CurrentControlSet\Control\CI\Policy /v VerifiedAndReputablePolicyState /t REG_DWORD /d 0 /f
8+
CITOOL -r -j
9+
10+
311
SET DOTNET_VERSION=8.0
412
SET SANDBOX_FILES=C:\sandbox
513

0 commit comments

Comments
 (0)