aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-03-30 17:08:40 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-04-01 22:06:11 -0500
commit386a3578413ba16b3c0615d47870ee44a0e461f6 (patch)
tree1dfcea9e5080f1f15cc880aba1541a962426c58b /src
parentd97c0d1685ef4c3840776327e76ce25d4dbdbeb1 (diff)
downloadwix-386a3578413ba16b3c0615d47870ee44a0e461f6.tar.gz
wix-386a3578413ba16b3c0615d47870ee44a0e461f6.tar.bz2
wix-386a3578413ba16b3c0615d47870ee44a0e461f6.zip
Implement BundlePackage.
3693
Diffstat (limited to 'src')
-rw-r--r--src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h1
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs5
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs8
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs36
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs118
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs3
-rw-r--r--src/burn/engine/apply.cpp102
-rw-r--r--src/burn/engine/bundlepackageengine.cpp272
-rw-r--r--src/burn/engine/bundlepackageengine.h26
-rw-r--r--src/burn/engine/core.cpp16
-rw-r--r--src/burn/engine/core.h1
-rw-r--r--src/burn/engine/elevation.cpp176
-rw-r--r--src/burn/engine/elevation.h9
-rw-r--r--src/burn/engine/exeengine.cpp24
-rw-r--r--src/burn/engine/logging.cpp2
-rw-r--r--src/burn/engine/package.cpp18
-rw-r--r--src/burn/engine/package.h4
-rw-r--r--src/burn/engine/plan.cpp24
-rw-r--r--src/burn/engine/plan.h8
-rw-r--r--src/burn/engine/registration.cpp5
-rw-r--r--src/burn/engine/registration.h2
-rw-r--r--src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj1
-rw-r--r--src/burn/test/BurnUnitTest/PlanTest.cpp141
-rw-r--r--src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml1
-rw-r--r--src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj19
-rw-r--r--src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs11
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs51
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs7
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs32
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs16
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs17
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs11
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs27
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs11
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs152
-rw-r--r--src/wix/WixToolset.Core/Compiler_Bundle.cs52
-rw-r--r--src/wix/WixToolset.Core/Compiler_Dependency.cs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs114
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs10
40 files changed, 1439 insertions, 98 deletions
diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
index df8cac76..943f5ead 100644
--- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
@@ -106,6 +106,7 @@ enum BOOTSTRAPPER_RELATION_TYPE
106 BOOTSTRAPPER_RELATION_DEPENDENT_ADDON, 106 BOOTSTRAPPER_RELATION_DEPENDENT_ADDON,
107 BOOTSTRAPPER_RELATION_DEPENDENT_PATCH, 107 BOOTSTRAPPER_RELATION_DEPENDENT_PATCH,
108 BOOTSTRAPPER_RELATION_UPDATE, 108 BOOTSTRAPPER_RELATION_UPDATE,
109 BOOTSTRAPPER_RELATION_CHAIN_PACKAGE,
109}; 110};
110 111
111enum BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE 112enum BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE
diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
index 1786eecd..7cf0957a 100644
--- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
@@ -1714,6 +1714,11 @@ namespace WixToolset.Mba.Core
1714 /// 1714 ///
1715 /// </summary> 1715 /// </summary>
1716 Update, 1716 Update,
1717
1718 /// <summary>
1719 ///
1720 /// </summary>
1721 ChainPackage,
1717 } 1722 }
1718 1723
1719 /// <summary> 1724 /// <summary>
diff --git a/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs b/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
index 21ca1b6d..d4a91343 100644
--- a/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
+++ b/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
@@ -125,6 +125,8 @@ namespace WixToolset.Data
125 WixBundleCustomData, 125 WixBundleCustomData,
126 WixBundleCustomDataAttribute, 126 WixBundleCustomDataAttribute,
127 WixBundleCustomDataCell, 127 WixBundleCustomDataCell,
128 WixBundleBundlePackage,
129 WixBundleBundlePackagePayload,
128 WixBundleExePackage, 130 WixBundleExePackage,
129 WixBundleExePackagePayload, 131 WixBundleExePackagePayload,
130 WixBundleExtension, 132 WixBundleExtension,
@@ -553,6 +555,12 @@ namespace WixToolset.Data
553 case SymbolDefinitionType.WixBundle: 555 case SymbolDefinitionType.WixBundle:
554 return SymbolDefinitions.WixBundle; 556 return SymbolDefinitions.WixBundle;
555 557
558 case SymbolDefinitionType.WixBundleBundlePackage:
559 return SymbolDefinitions.WixBundleBundlePackage;
560
561 case SymbolDefinitionType.WixBundleBundlePackagePayload:
562 return SymbolDefinitions.WixBundleBundlePackagePayload;
563
556 case SymbolDefinitionType.WixBundleContainer: 564 case SymbolDefinitionType.WixBundleContainer:
557 return SymbolDefinitions.WixBundleContainer; 565 return SymbolDefinitions.WixBundleContainer;
558 566
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs
new file mode 100644
index 00000000..a171682d
--- /dev/null
+++ b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs
@@ -0,0 +1,36 @@
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
3namespace WixToolset.Data
4{
5 using WixToolset.Data.Symbols;
6
7 public static partial class SymbolDefinitions
8 {
9 public static readonly IntermediateSymbolDefinition WixBundleBundlePackagePayload = new IntermediateSymbolDefinition(
10 SymbolDefinitionType.WixBundleBundlePackagePayload,
11 new IntermediateFieldDefinition[]
12 {
13 },
14 typeof(WixBundleBundlePackagePayloadSymbol));
15 }
16}
17
18namespace WixToolset.Data.Symbols
19{
20 public enum WixBundleBundlePackagePayloadSymbolFields
21 {
22 }
23
24 public class WixBundleBundlePackagePayloadSymbol : IntermediateSymbol
25 {
26 public WixBundleBundlePackagePayloadSymbol() : base(SymbolDefinitions.WixBundleBundlePackagePayload, null, null)
27 {
28 }
29
30 public WixBundleBundlePackagePayloadSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(SymbolDefinitions.WixBundleBundlePackagePayload, sourceLineNumber, id)
31 {
32 }
33
34 public IntermediateField this[WixBundleBundlePackagePayloadSymbolFields index] => this.Fields[(int)index];
35 }
36}
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs
new file mode 100644
index 00000000..36b9eb67
--- /dev/null
+++ b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs
@@ -0,0 +1,118 @@
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
3namespace WixToolset.Data
4{
5 using WixToolset.Data.Symbols;
6
7 public static partial class SymbolDefinitions
8 {
9 public static readonly IntermediateSymbolDefinition WixBundleBundlePackage = new IntermediateSymbolDefinition(
10 SymbolDefinitionType.WixBundleBundlePackage,
11 new[]
12 {
13 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.Attributes), IntermediateFieldType.Number),
14 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.BundleId), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.InstallCommand), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.RepairCommand), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.UninstallCommand), IntermediateFieldType.String),
18 },
19 typeof(WixBundleBundlePackageSymbol));
20 }
21}
22
23namespace WixToolset.Data.Symbols
24{
25 using System;
26
27 public enum WixBundleBundlePackageSymbolFields
28 {
29 Attributes,
30 BundleId,
31 InstallCommand,
32 RepairCommand,
33 UninstallCommand,
34 }
35
36 [Flags]
37 public enum WixBundleBundlePackageAttributes
38 {
39 None = 0,
40 SupportsBurnProtocol = 1,
41 Win64 = 2,
42 }
43
44 public class WixBundleBundlePackageSymbol : IntermediateSymbol
45 {
46 public WixBundleBundlePackageSymbol() : base(SymbolDefinitions.WixBundleBundlePackage, null, null)
47 {
48 }
49
50 public WixBundleBundlePackageSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(SymbolDefinitions.WixBundleBundlePackage, sourceLineNumber, id)
51 {
52 }
53
54 public IntermediateField this[WixBundleBundlePackageSymbolFields index] => this.Fields[(int)index];
55
56 public WixBundleBundlePackageAttributes Attributes
57 {
58 get => (WixBundleBundlePackageAttributes)(int)this.Fields[(int)WixBundleBundlePackageSymbolFields.Attributes];
59 set => this.Set((int)WixBundleBundlePackageSymbolFields.Attributes, (int)value);
60 }
61
62 public string BundleId
63 {
64 get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.BundleId];
65 set => this.Set((int)WixBundleBundlePackageSymbolFields.BundleId, value);
66 }
67
68 public string InstallCommand
69 {
70 get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.InstallCommand];
71 set => this.Set((int)WixBundleBundlePackageSymbolFields.InstallCommand, value);
72 }
73
74 public string RepairCommand
75 {
76 get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.RepairCommand];
77 set => this.Set((int)WixBundleBundlePackageSymbolFields.RepairCommand, value);
78 }
79
80 public string UninstallCommand
81 {
82 get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.UninstallCommand];
83 set => this.Set((int)WixBundleBundlePackageSymbolFields.UninstallCommand, value);
84 }
85
86 public bool SupportsBurnProtocol
87 {
88 get { return this.Attributes.HasFlag(WixBundleBundlePackageAttributes.SupportsBurnProtocol); }
89 set
90 {
91 if (value)
92 {
93 this.Attributes |= WixBundleBundlePackageAttributes.SupportsBurnProtocol;
94 }
95 else
96 {
97 this.Attributes &= ~WixBundleBundlePackageAttributes.SupportsBurnProtocol;
98 }
99 }
100 }
101
102 public bool Win64
103 {
104 get { return this.Attributes.HasFlag(WixBundleBundlePackageAttributes.Win64); }
105 set
106 {
107 if (value)
108 {
109 this.Attributes |= WixBundleBundlePackageAttributes.Win64;
110 }
111 else
112 {
113 this.Attributes &= ~WixBundleBundlePackageAttributes.Win64;
114 }
115 }
116 }
117 }
118}
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs
index 3d1a6ce8..e68a9d09 100644
--- a/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs
+++ b/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs
@@ -66,6 +66,7 @@ namespace WixToolset.Data.Symbols
66 /// </summary> 66 /// </summary>
67 public enum WixBundlePackageType 67 public enum WixBundlePackageType
68 { 68 {
69 Bundle,
69 Exe, 70 Exe,
70 Msi, 71 Msi,
71 Msp, 72 Msp,
@@ -209,4 +210,4 @@ namespace WixToolset.Data.Symbols
209 210
210 public bool Permanent => (this.Attributes & WixBundlePackageAttributes.Permanent) == WixBundlePackageAttributes.Permanent; 211 public bool Permanent => (this.Attributes & WixBundlePackageAttributes.Permanent) == WixBundlePackageAttributes.Permanent;
211 } 212 }
212} \ No newline at end of file 213}
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
index 6af5c040..9ee7b58c 100644
--- a/src/burn/engine/apply.cpp
+++ b/src/burn/engine/apply.cpp
@@ -216,6 +216,15 @@ static HRESULT DoRestoreRelatedBundleActions(
216 __in BURN_EXECUTE_CONTEXT* pContext, 216 __in BURN_EXECUTE_CONTEXT* pContext,
217 __out BOOTSTRAPPER_APPLY_RESTART* pRestart 217 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
218 ); 218 );
219static HRESULT ExecuteBundlePackage(
220 __in BURN_ENGINE_STATE* pEngineState,
221 __in BURN_EXECUTE_ACTION* pExecuteAction,
222 __in BURN_EXECUTE_CONTEXT* pContext,
223 __in BOOL fRollback,
224 __out BOOL* pfRetry,
225 __out BOOL* pfSuspend,
226 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
227 );
219static HRESULT ExecuteExePackage( 228static HRESULT ExecuteExePackage(
220 __in BURN_ENGINE_STATE* pEngineState, 229 __in BURN_ENGINE_STATE* pEngineState,
221 __in BURN_EXECUTE_ACTION* pExecuteAction, 230 __in BURN_EXECUTE_ACTION* pExecuteAction,
@@ -732,6 +741,9 @@ extern "C" HRESULT ApplyExecute(
732 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE: 741 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
733 wzId = pExecuteAction->relatedBundle.pRelatedBundle->package.sczId; 742 wzId = pExecuteAction->relatedBundle.pRelatedBundle->package.sczId;
734 break; 743 break;
744 case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
745 wzId = pExecuteAction->bundlePackage.pPackage->sczId;
746 break;
735 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: 747 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
736 wzId = pExecuteAction->exePackage.pPackage->sczId; 748 wzId = pExecuteAction->exePackage.pPackage->sczId;
737 break; 749 break;
@@ -2352,6 +2364,11 @@ static HRESULT DoExecuteAction(
2352 ExitOnFailure(hr, "Failed to execute related bundle."); 2364 ExitOnFailure(hr, "Failed to execute related bundle.");
2353 break; 2365 break;
2354 2366
2367 case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
2368 hr = ExecuteBundlePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
2369 ExitOnFailure(hr, "Failed to execute BUNDLE package.");
2370 break;
2371
2355 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: 2372 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2356 hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); 2373 hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
2357 ExitOnFailure(hr, "Failed to execute EXE package."); 2374 ExitOnFailure(hr, "Failed to execute EXE package.");
@@ -2479,6 +2496,11 @@ static HRESULT DoRollbackActions(
2479 ExitOnFailure(hr, "Failed to execute related bundle."); 2496 ExitOnFailure(hr, "Failed to execute related bundle.");
2480 break; 2497 break;
2481 2498
2499 case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
2500 hr = ExecuteBundlePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2501 IgnoreRollbackError(hr, "Failed to rollback BUNDLE package.");
2502 break;
2503
2482 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: 2504 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2483 hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); 2505 hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2484 IgnoreRollbackError(hr, "Failed to rollback EXE package."); 2506 IgnoreRollbackError(hr, "Failed to rollback EXE package.");
@@ -2682,6 +2704,86 @@ LExit:
2682 return hr; 2704 return hr;
2683} 2705}
2684 2706
2707static HRESULT ExecuteBundlePackage(
2708 __in BURN_ENGINE_STATE* pEngineState,
2709 __in BURN_EXECUTE_ACTION* pExecuteAction,
2710 __in BURN_EXECUTE_CONTEXT* pContext,
2711 __in BOOL fRollback,
2712 __out BOOL* pfRetry,
2713 __out BOOL* pfSuspend,
2714 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2715 )
2716{
2717 HRESULT hr = S_OK;
2718 HRESULT hrExecute = S_OK;
2719 GENERIC_EXECUTE_MESSAGE message = { };
2720 int nResult = 0;
2721 BOOL fBeginCalled = FALSE;
2722 BOOL fExecuted = FALSE;
2723 BURN_PACKAGE* pPackage = pExecuteAction->bundlePackage.pPackage;
2724
2725 Assert(pContext->fRollback == fRollback);
2726
2727 if (ShouldSkipPackage(pPackage, fRollback))
2728 {
2729 ExitFunction1(hr = S_OK);
2730 }
2731
2732 pContext->wzExecutingPackageId = pPackage->sczId;
2733 fBeginCalled = TRUE;
2734
2735 // Send package execute begin to BA.
2736 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->bundlePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE);
2737 ExitOnRootFailure(hr, "BA aborted execute BUNDLE package begin.");
2738
2739 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2740 message.dwUIHint = MB_OKCANCEL;
2741 message.progress.dwPercentage = fRollback ? 100 : 0;
2742 nResult = GenericExecuteMessageHandler(&message, pContext);
2743 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2744 ExitOnRootFailure(hr, "BA aborted BUNDLE progress.");
2745
2746 fExecuted = TRUE;
2747
2748 // Execute package.
2749 if (pPackage->fPerMachine)
2750 {
2751 hrExecute = ElevationExecuteBundlePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2752 ExitOnFailure(hrExecute, "Failed to configure per-machine BUNDLE package.");
2753 }
2754 else
2755 {
2756 hrExecute = BundlePackageEngineExecutePackage(pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2757 ExitOnFailure(hrExecute, "Failed to configure per-user BUNDLE package.");
2758 }
2759
2760 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2761 message.dwUIHint = MB_OKCANCEL;
2762 message.progress.dwPercentage = fRollback ? 0 : 100;
2763 nResult = GenericExecuteMessageHandler(&message, pContext);
2764 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2765 ExitOnRootFailure(hr, "BA aborted BUNDLE progress.");
2766
2767 pContext->cExecutedPackages += fRollback ? -1 : 1;
2768
2769 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
2770 ExitOnRootFailure(hr, "BA aborted BUNDLE package execute progress.");
2771
2772LExit:
2773 if (fExecuted)
2774 {
2775 BundlePackageEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute);
2776 }
2777
2778 if (fBeginCalled)
2779 {
2780 pPackage->fAbandonedProcess = pContext->fAbandonedProcess;
2781 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2782 }
2783
2784 return hr;
2785}
2786
2685static HRESULT ExecuteExePackage( 2787static HRESULT ExecuteExePackage(
2686 __in BURN_ENGINE_STATE* pEngineState, 2788 __in BURN_ENGINE_STATE* pEngineState,
2687 __in BURN_EXECUTE_ACTION* pExecuteAction, 2789 __in BURN_EXECUTE_ACTION* pExecuteAction,
diff --git a/src/burn/engine/bundlepackageengine.cpp b/src/burn/engine/bundlepackageengine.cpp
index 89488b91..f3badfc1 100644
--- a/src/burn/engine/bundlepackageengine.cpp
+++ b/src/burn/engine/bundlepackageengine.cpp
@@ -2,16 +2,80 @@
2 2
3#include "precomp.h" 3#include "precomp.h"
4 4
5static HRESULT ExecuteBundle(
6 __in BURN_CACHE* pCache,
7 __in BURN_VARIABLES* pVariables,
8 __in BOOL fRollback,
9 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
10 __in LPVOID pvContext,
11 __in BOOTSTRAPPER_ACTION_STATE action,
12 __in BOOTSTRAPPER_RELATION_TYPE relationType,
13 __in BURN_PACKAGE* pPackage,
14 __in_z_opt LPCWSTR wzIgnoreDependencies,
15 __in_z_opt LPCWSTR wzAncestors,
16 __in_z_opt LPCWSTR wzEngineWorkingDirectory,
17 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
18 );
5static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType( 19static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType(
6 __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType 20 __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType
7 ); 21 );
8 22
9// function definitions 23// function definitions
10 24
25extern "C" HRESULT BundlePackageEngineParsePackageFromXml(
26 __in IXMLDOMNode* pixnBundlePackage,
27 __in BURN_PACKAGE* pPackage
28 )
29{
30 HRESULT hr = S_OK;
31 BOOL fFoundXml = FALSE;
32 LPWSTR scz = NULL;
33
34 // @DetectCondition
35 hr = XmlGetAttributeEx(pixnBundlePackage, L"BundleId", &pPackage->Bundle.sczBundleId);
36 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @BundleId.");
37
38 // @InstallArguments
39 hr = XmlGetAttributeEx(pixnBundlePackage, L"InstallArguments", &pPackage->Bundle.sczInstallArguments);
40 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments.");
41
42 // @UninstallArguments
43 hr = XmlGetAttributeEx(pixnBundlePackage, L"UninstallArguments", &pPackage->Bundle.sczUninstallArguments);
44 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArguments.");
45
46 // @RepairArguments
47 hr = XmlGetAttributeEx(pixnBundlePackage, L"RepairArguments", &pPackage->Bundle.sczRepairArguments);
48 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairArguments.");
49
50 // @SupportsBurnProtocol
51 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"SupportsBurnProtocol", &pPackage->Bundle.fSupportsBurnProtocol);
52 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @SupportsBurnProtocol.");
53
54 // @Win64
55 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"Win64", &pPackage->Bundle.fWin64);
56 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Win64.");
57
58 hr = ExeEngineParseExitCodesFromXml(pixnBundlePackage, &pPackage->Bundle.rgExitCodes, &pPackage->Bundle.cExitCodes);
59 ExitOnFailure(hr, "Failed to parse exit codes.");
60
61 hr = ExeEngineParseCommandLineArgumentsFromXml(pixnBundlePackage, &pPackage->Bundle.rgCommandLineArguments, &pPackage->Bundle.cCommandLineArguments);
62 ExitOnFailure(hr, "Failed to parse command lines.");
63
64 hr = StrAllocFormatted(&pPackage->Bundle.sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pPackage->Bundle.sczBundleId);
65 ExitOnFailure(hr, "Failed to build uninstall registry key path.");
66
67LExit:
68 ReleaseStr(scz);
69
70 return hr;
71}
72
11extern "C" void BundlePackageEnginePackageUninitialize( 73extern "C" void BundlePackageEnginePackageUninitialize(
12 __in BURN_PACKAGE* pPackage 74 __in BURN_PACKAGE* pPackage
13 ) 75 )
14{ 76{
77 ReleaseStr(pPackage->Bundle.sczBundleId);
78 ReleaseStr(pPackage->Bundle.sczRegistrationKey);
15 ReleaseStr(pPackage->Bundle.sczInstallArguments); 79 ReleaseStr(pPackage->Bundle.sczInstallArguments);
16 ReleaseStr(pPackage->Bundle.sczRepairArguments); 80 ReleaseStr(pPackage->Bundle.sczRepairArguments);
17 ReleaseStr(pPackage->Bundle.sczUninstallArguments); 81 ReleaseStr(pPackage->Bundle.sczUninstallArguments);
@@ -32,6 +96,44 @@ extern "C" void BundlePackageEnginePackageUninitialize(
32 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle)); 96 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle));
33} 97}
34 98
99extern "C" HRESULT BundlePackageEngineDetectPackage(
100 __in BURN_PACKAGE* pPackage
101 )
102{
103 HRESULT hr = S_OK;
104 HKEY hkRegistration = NULL;
105 DWORD dwInstalled = 0;
106 BOOL fDetected = FALSE;
107 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
108 REG_KEY_BITNESS bitness = pPackage->Bundle.fWin64 ? REG_KEY_64BIT : REG_KEY_32BIT;
109
110 // TODO: detect all related bundles, so that the Obsolete state can be detected.
111 hr = RegOpenEx(hkRoot, pPackage->Bundle.sczRegistrationKey, KEY_QUERY_VALUE, bitness, &hkRegistration);
112 if (SUCCEEDED(hr))
113 {
114 hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled);
115 }
116
117 // Not finding the key or value is okay.
118 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
119 {
120 hr = S_OK;
121 }
122
123 fDetected = (1 == dwInstalled);
124
125 // update detect state
126 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
127
128 if (pPackage->fCanAffectRegistration)
129 {
130 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
131 }
132
133 ReleaseRegKey(hkRegistration);
134 return hr;
135}
136
35// 137//
36// PlanCalculate - calculates the execute and rollback state for the requested package state. 138// PlanCalculate - calculates the execute and rollback state for the requested package state.
37// 139//
@@ -151,6 +253,79 @@ LExit:
151// 253//
152// PlanAdd - adds the calculated execute and rollback actions for the package. 254// PlanAdd - adds the calculated execute and rollback actions for the package.
153// 255//
256extern "C" HRESULT BundlePackageEnginePlanAddPackage(
257 __in BURN_PACKAGE* pPackage,
258 __in BURN_PLAN* pPlan,
259 __in BURN_LOGGING* pLog,
260 __in BURN_VARIABLES* pVariables
261 )
262{
263 HRESULT hr = S_OK;
264 BURN_EXECUTE_ACTION* pAction = NULL;
265
266 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
267 ExitOnFailure(hr, "Failed to plan package dependency actions.");
268
269 // add rollback action
270 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
271 {
272 hr = PlanAppendRollbackAction(pPlan, &pAction);
273 ExitOnFailure(hr, "Failed to append rollback action.");
274
275 pAction->type = BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE;
276 pAction->bundlePackage.pPackage = pPackage;
277 pAction->bundlePackage.action = pPackage->rollback;
278
279 if (pPackage->Bundle.wzAncestors)
280 {
281 hr = StrAllocString(&pAction->bundlePackage.sczAncestors, pPackage->Bundle.wzAncestors, 0);
282 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
283 }
284
285 if (pPackage->Bundle.wzEngineWorkingDirectory)
286 {
287 hr = StrAllocString(&pAction->bundlePackage.sczEngineWorkingDirectory, pPackage->Bundle.wzEngineWorkingDirectory, 0);
288 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
289 }
290
291 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
292
293 hr = PlanExecuteCheckpoint(pPlan);
294 ExitOnFailure(hr, "Failed to append execute checkpoint.");
295 }
296
297 // add execute action
298 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
299 {
300 hr = PlanAppendExecuteAction(pPlan, &pAction);
301 ExitOnFailure(hr, "Failed to append execute action.");
302
303 pAction->type = BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE;
304 pAction->bundlePackage.pPackage = pPackage;
305 pAction->bundlePackage.action = pPackage->execute;
306
307 if (pPackage->Bundle.wzAncestors)
308 {
309 hr = StrAllocString(&pAction->bundlePackage.sczAncestors, pPackage->Bundle.wzAncestors, 0);
310 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
311 }
312
313 if (pPackage->Bundle.wzEngineWorkingDirectory)
314 {
315 hr = StrAllocString(&pAction->bundlePackage.sczEngineWorkingDirectory, pPackage->Bundle.wzEngineWorkingDirectory, 0);
316 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
317 }
318
319 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
320 }
321
322LExit:
323 return hr;
324}
325
326//
327// PlanAdd - adds the calculated execute and rollback actions for the related bundle.
328//
154extern "C" HRESULT BundlePackageEnginePlanAddRelatedBundle( 329extern "C" HRESULT BundlePackageEnginePlanAddRelatedBundle(
155 __in_opt DWORD *pdwInsertSequence, 330 __in_opt DWORD *pdwInsertSequence,
156 __in BURN_RELATED_BUNDLE* pRelatedBundle, 331 __in BURN_RELATED_BUNDLE* pRelatedBundle,
@@ -164,7 +339,7 @@ extern "C" HRESULT BundlePackageEnginePlanAddRelatedBundle(
164 BURN_PACKAGE* pPackage = &pRelatedBundle->package; 339 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
165 340
166 hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan); 341 hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan);
167 ExitOnFailure(hr, "Failed to plan package dependency actions."); 342 ExitOnFailure(hr, "Failed to plan related bundle dependency actions.");
168 343
169 // add execute action 344 // add execute action
170 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) 345 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
@@ -240,6 +415,26 @@ LExit:
240 return hr; 415 return hr;
241} 416}
242 417
418extern "C" HRESULT BundlePackageEngineExecutePackage(
419 __in BURN_EXECUTE_ACTION* pExecuteAction,
420 __in BURN_CACHE* pCache,
421 __in BURN_VARIABLES* pVariables,
422 __in BOOL fRollback,
423 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
424 __in LPVOID pvContext,
425 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
426 )
427{
428 BOOTSTRAPPER_ACTION_STATE action = pExecuteAction->bundlePackage.action;
429 LPCWSTR wzIgnoreDependencies = pExecuteAction->bundlePackage.sczIgnoreDependencies;
430 LPCWSTR wzAncestors = pExecuteAction->bundlePackage.sczAncestors;
431 LPCWSTR wzEngineWorkingDirectory = pExecuteAction->bundlePackage.sczEngineWorkingDirectory;
432 BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_CHAIN_PACKAGE;
433 BURN_PACKAGE* pPackage = pExecuteAction->bundlePackage.pPackage;
434
435 return ExecuteBundle(pCache, pVariables, fRollback, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart);
436}
437
243extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle( 438extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
244 __in BURN_EXECUTE_ACTION* pExecuteAction, 439 __in BURN_EXECUTE_ACTION* pExecuteAction,
245 __in BURN_CACHE* pCache, 440 __in BURN_CACHE* pCache,
@@ -250,6 +445,57 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
250 __out BOOTSTRAPPER_APPLY_RESTART* pRestart 445 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
251 ) 446 )
252{ 447{
448 BOOTSTRAPPER_ACTION_STATE action = pExecuteAction->relatedBundle.action;
449 LPCWSTR wzIgnoreDependencies = pExecuteAction->relatedBundle.sczIgnoreDependencies;
450 LPCWSTR wzAncestors = pExecuteAction->relatedBundle.sczAncestors;
451 LPCWSTR wzEngineWorkingDirectory = pExecuteAction->relatedBundle.sczEngineWorkingDirectory;
452 BURN_RELATED_BUNDLE* pRelatedBundle = pExecuteAction->relatedBundle.pRelatedBundle;
453 BOOTSTRAPPER_RELATION_TYPE relationType = ConvertRelationType(pRelatedBundle->planRelationType);
454 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
455
456 return ExecuteBundle(pCache, pVariables, fRollback, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart);
457}
458
459extern "C" void BundlePackageEngineUpdateInstallRegistrationState(
460 __in BURN_EXECUTE_ACTION* pAction,
461 __in HRESULT hrExecute
462 )
463{
464 BURN_PACKAGE* pPackage = pAction->bundlePackage.pPackage;
465
466 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
467 {
468 ExitFunction();
469 }
470
471 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->bundlePackage.action)
472 {
473 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
474 }
475 else
476 {
477 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
478 }
479
480LExit:
481 return;
482}
483
484static HRESULT ExecuteBundle(
485 __in BURN_CACHE* pCache,
486 __in BURN_VARIABLES* pVariables,
487 __in BOOL fRollback,
488 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
489 __in LPVOID pvContext,
490 __in BOOTSTRAPPER_ACTION_STATE action,
491 __in BOOTSTRAPPER_RELATION_TYPE relationType,
492 __in BURN_PACKAGE* pPackage,
493 __in_z_opt LPCWSTR wzIgnoreDependencies,
494 __in_z_opt LPCWSTR wzAncestors,
495 __in_z_opt LPCWSTR wzEngineWorkingDirectory,
496 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
497 )
498{
253 HRESULT hr = S_OK; 499 HRESULT hr = S_OK;
254 LPCWSTR wzArguments = NULL; 500 LPCWSTR wzArguments = NULL;
255 LPWSTR sczCachedDirectory = NULL; 501 LPWSTR sczCachedDirectory = NULL;
@@ -264,10 +510,6 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
264 PROCESS_INFORMATION pi = { }; 510 PROCESS_INFORMATION pi = { };
265 DWORD dwExitCode = 0; 511 DWORD dwExitCode = 0;
266 GENERIC_EXECUTE_MESSAGE message = { }; 512 GENERIC_EXECUTE_MESSAGE message = { };
267 BOOTSTRAPPER_ACTION_STATE action = pExecuteAction->relatedBundle.action;
268 BURN_RELATED_BUNDLE* pRelatedBundle = pExecuteAction->relatedBundle.pRelatedBundle;
269 BOOTSTRAPPER_RELATION_TYPE relationType = ConvertRelationType(pRelatedBundle->planRelationType);
270 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
271 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; 513 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
272 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); 514 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
273 LPCWSTR wzOperationCommandLine = NULL; 515 LPCWSTR wzOperationCommandLine = NULL;
@@ -376,21 +618,29 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
376 } 618 }
377 619
378 // Add the list of dependencies to ignore, if any, to the burn command line. 620 // Add the list of dependencies to ignore, if any, to the burn command line.
379 if (pExecuteAction->relatedBundle.sczIgnoreDependencies) 621 if (BOOTSTRAPPER_RELATION_CHAIN_PACKAGE == relationType)
380 { 622 {
381 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->relatedBundle.sczIgnoreDependencies); 623 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=ALL", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
624 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
625 }
626 else if (wzIgnoreDependencies)
627 {
628 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, wzIgnoreDependencies);
382 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); 629 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
383 } 630 }
384 631
385 // Add the list of ancestors, if any, to the burn command line. 632 // Add the list of ancestors, if any, to the burn command line.
386 if (pExecuteAction->relatedBundle.sczAncestors) 633 if (wzAncestors)
387 { 634 {
388 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->relatedBundle.sczAncestors); 635 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors);
389 ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); 636 ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");
390 } 637 }
391 638
392 hr = CoreAppendEngineWorkingDirectoryToCommandLine(pExecuteAction->relatedBundle.sczEngineWorkingDirectory, &sczBaseCommand, NULL); 639 if (wzEngineWorkingDirectory)
393 ExitOnFailure(hr, "Failed to append the custom working directory to the bundlepackage command line."); 640 {
641 hr = CoreAppendEngineWorkingDirectoryToCommandLine(wzEngineWorkingDirectory, &sczBaseCommand, NULL);
642 ExitOnFailure(hr, "Failed to append the custom working directory to the bundlepackage command line.");
643 }
394 644
395 hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczBaseCommand, NULL); 645 hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczBaseCommand, NULL);
396 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); 646 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
diff --git a/src/burn/engine/bundlepackageengine.h b/src/burn/engine/bundlepackageengine.h
index 0d59907d..9271ac6a 100644
--- a/src/burn/engine/bundlepackageengine.h
+++ b/src/burn/engine/bundlepackageengine.h
@@ -9,12 +9,25 @@ extern "C" {
9 9
10// function declarations 10// function declarations
11 11
12HRESULT BundlePackageEngineParsePackageFromXml(
13 __in IXMLDOMNode* pixnBundlePackage,
14 __in BURN_PACKAGE* pPackage
15 );
12void BundlePackageEnginePackageUninitialize( 16void BundlePackageEnginePackageUninitialize(
13 __in BURN_PACKAGE* pPackage 17 __in BURN_PACKAGE* pPackage
14 ); 18 );
19HRESULT BundlePackageEngineDetectPackage(
20 __in BURN_PACKAGE* pPackage
21 );
15HRESULT BundlePackageEnginePlanCalculatePackage( 22HRESULT BundlePackageEnginePlanCalculatePackage(
16 __in BURN_PACKAGE* pPackage 23 __in BURN_PACKAGE* pPackage
17 ); 24 );
25HRESULT BundlePackageEnginePlanAddPackage(
26 __in BURN_PACKAGE* pPackage,
27 __in BURN_PLAN* pPlan,
28 __in BURN_LOGGING* pLog,
29 __in BURN_VARIABLES* pVariables
30 );
18HRESULT BundlePackageEnginePlanAddRelatedBundle( 31HRESULT BundlePackageEnginePlanAddRelatedBundle(
19 __in_opt DWORD *pdwInsertSequence, 32 __in_opt DWORD *pdwInsertSequence,
20 __in BURN_RELATED_BUNDLE* pRelatedBundle, 33 __in BURN_RELATED_BUNDLE* pRelatedBundle,
@@ -22,6 +35,15 @@ HRESULT BundlePackageEnginePlanAddRelatedBundle(
22 __in BURN_LOGGING* pLog, 35 __in BURN_LOGGING* pLog,
23 __in BURN_VARIABLES* pVariables 36 __in BURN_VARIABLES* pVariables
24 ); 37 );
38HRESULT BundlePackageEngineExecutePackage(
39 __in BURN_EXECUTE_ACTION* pExecuteAction,
40 __in BURN_CACHE* pCache,
41 __in BURN_VARIABLES* pVariables,
42 __in BOOL fRollback,
43 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
44 __in LPVOID pvContext,
45 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
46 );
25HRESULT BundlePackageEngineExecuteRelatedBundle( 47HRESULT BundlePackageEngineExecuteRelatedBundle(
26 __in BURN_EXECUTE_ACTION* pExecuteAction, 48 __in BURN_EXECUTE_ACTION* pExecuteAction,
27 __in BURN_CACHE* pCache, 49 __in BURN_CACHE* pCache,
@@ -31,6 +53,10 @@ HRESULT BundlePackageEngineExecuteRelatedBundle(
31 __in LPVOID pvContext, 53 __in LPVOID pvContext,
32 __out BOOTSTRAPPER_APPLY_RESTART* pRestart 54 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
33 ); 55 );
56void BundlePackageEngineUpdateInstallRegistrationState(
57 __in BURN_EXECUTE_ACTION* pAction,
58 __in HRESULT hrExecute
59 );
34 60
35 61
36#if defined(__cplusplus) 62#if defined(__cplusplus)
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index 551843f2..3370ad05 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -956,6 +956,9 @@ extern "C" LPCWSTR CoreRelationTypeToCommandLineString(
956 case BOOTSTRAPPER_RELATION_DEPENDENT_PATCH: 956 case BOOTSTRAPPER_RELATION_DEPENDENT_PATCH:
957 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH; 957 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH;
958 break; 958 break;
959 case BOOTSTRAPPER_RELATION_CHAIN_PACKAGE:
960 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE;
961 break;
959 case BOOTSTRAPPER_RELATION_NONE: __fallthrough; 962 case BOOTSTRAPPER_RELATION_NONE: __fallthrough;
960 default: 963 default:
961 wzRelationTypeCommandLine = NULL; 964 wzRelationTypeCommandLine = NULL;
@@ -1709,6 +1712,12 @@ extern "C" HRESULT CoreParseCommandLine(
1709 1712
1710 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); 1713 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1711 } 1714 }
1715 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE, -1))
1716 {
1717 pCommand->relationType = BOOTSTRAPPER_RELATION_CHAIN_PACKAGE;
1718
1719 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1720 }
1712 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1)) 1721 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1))
1713 { 1722 {
1714 pCommand->fPassthrough = TRUE; 1723 pCommand->fPassthrough = TRUE;
@@ -2111,6 +2120,10 @@ static HRESULT DetectPackage(
2111 // Use the correct engine to detect the package. 2120 // Use the correct engine to detect the package.
2112 switch (pPackage->type) 2121 switch (pPackage->type)
2113 { 2122 {
2123 case BURN_PACKAGE_TYPE_BUNDLE:
2124 hr = BundlePackageEngineDetectPackage(pPackage);
2125 break;
2126
2114 case BURN_PACKAGE_TYPE_EXE: 2127 case BURN_PACKAGE_TYPE_EXE:
2115 hr = ExeEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->variables); 2128 hr = ExeEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->variables);
2116 break; 2129 break;
@@ -2128,8 +2141,7 @@ static HRESULT DetectPackage(
2128 break; 2141 break;
2129 2142
2130 default: 2143 default:
2131 hr = E_NOTIMPL; 2144 ExitWithRootFailure(hr, E_NOTIMPL, "Package type not supported by detect yet.");
2132 ExitOnRootFailure(hr, "Package type not supported by detect yet.");
2133 } 2145 }
2134 2146
2135LExit: 2147LExit:
diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h
index cee71aec..556124d6 100644
--- a/src/burn/engine/core.h
+++ b/src/burn/engine/core.h
@@ -27,6 +27,7 @@ const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_ADDON = L"burn.related.d
27const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch"; 27const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch";
28const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH = L"burn.related.dependent.patch"; 28const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH = L"burn.related.dependent.patch";
29const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update"; 29const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update";
30const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE = L"burn.related.chain.package";
30const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough"; 31const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough";
31const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate"; 32const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate";
32const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies"; 33const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies";
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp
index 8488b649..504ddaea 100644
--- a/src/burn/engine/elevation.cpp
+++ b/src/burn/engine/elevation.cpp
@@ -20,6 +20,7 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE
20 BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, 20 BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP,
21 BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, 21 BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION,
22 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_RELATED_BUNDLE, 22 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_RELATED_BUNDLE,
23 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_BUNDLE_PACKAGE,
23 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, 24 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE,
24 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, 25 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE,
25 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, 26 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE,
@@ -250,6 +251,14 @@ static HRESULT OnExecuteRelatedBundle(
250 __in BYTE* pbData, 251 __in BYTE* pbData,
251 __in SIZE_T cbData 252 __in SIZE_T cbData
252 ); 253 );
254static HRESULT OnExecuteBundlePackage(
255 __in HANDLE hPipe,
256 __in BURN_CACHE* pCache,
257 __in BURN_PACKAGES* pPackages,
258 __in BURN_VARIABLES* pVariables,
259 __in BYTE* pbData,
260 __in SIZE_T cbData
261 );
253static HRESULT OnExecuteExePackage( 262static HRESULT OnExecuteExePackage(
254 __in HANDLE hPipe, 263 __in HANDLE hPipe,
255 __in BURN_CACHE* pCache, 264 __in BURN_CACHE* pCache,
@@ -911,6 +920,63 @@ LExit:
911} 920}
912 921
913/******************************************************************* 922/*******************************************************************
923 ElevationExecuteBundlePackage -
924
925*******************************************************************/
926extern "C" HRESULT ElevationExecuteBundlePackage(
927 __in HANDLE hPipe,
928 __in BURN_EXECUTE_ACTION* pExecuteAction,
929 __in BURN_VARIABLES* pVariables,
930 __in BOOL fRollback,
931 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
932 __in LPVOID pvContext,
933 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
934 )
935{
936 HRESULT hr = S_OK;
937 BYTE* pbData = NULL;
938 SIZE_T cbData = 0;
939 BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { };
940 DWORD dwResult = 0;
941
942 // serialize message data
943 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->bundlePackage.pPackage->sczId);
944 ExitOnFailure(hr, "Failed to write package id to message buffer.");
945
946 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->bundlePackage.action);
947 ExitOnFailure(hr, "Failed to write action to message buffer.");
948
949 hr = BuffWriteNumber(&pbData, &cbData, fRollback);
950 ExitOnFailure(hr, "Failed to write rollback.");
951
952 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->bundlePackage.sczIgnoreDependencies);
953 ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer.");
954
955 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->bundlePackage.sczAncestors);
956 ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer.");
957
958 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->bundlePackage.sczEngineWorkingDirectory);
959 ExitOnFailure(hr, "Failed to write the custom working directory to the message buffer.");
960
961 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
962 ExitOnFailure(hr, "Failed to write variables.");
963
964 // send message
965 context.pfnGenericMessageHandler = pfnGenericMessageHandler;
966 context.pvContext = pvContext;
967
968 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_BUNDLE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult);
969 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_BUNDLE_PACKAGE message to per-machine process.");
970
971 hr = ProcessResult(dwResult, pRestart);
972
973LExit:
974 ReleaseBuffer(pbData);
975
976 return hr;
977}
978
979/*******************************************************************
914 ElevationExecuteExePackage - 980 ElevationExecuteExePackage -
915 981
916*******************************************************************/ 982*******************************************************************/
@@ -940,9 +1006,6 @@ extern "C" HRESULT ElevationExecuteExePackage(
940 hr = BuffWriteNumber(&pbData, &cbData, fRollback); 1006 hr = BuffWriteNumber(&pbData, &cbData, fRollback);
941 ExitOnFailure(hr, "Failed to write rollback."); 1007 ExitOnFailure(hr, "Failed to write rollback.");
942 1008
943 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczIgnoreDependencies);
944 ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer.");
945
946 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors); 1009 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors);
947 ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer."); 1010 ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer.");
948 1011
@@ -2124,6 +2187,10 @@ static HRESULT ProcessElevatedChildMessage(
2124 hrResult = OnExecuteRelatedBundle(pContext->hPipe, pContext->pCache, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); 2187 hrResult = OnExecuteRelatedBundle(pContext->hPipe, pContext->pCache, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
2125 break; 2188 break;
2126 2189
2190 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_BUNDLE_PACKAGE:
2191 hrResult = OnExecuteBundlePackage(pContext->hPipe, pContext->pCache, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
2192 break;
2193
2127 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE: 2194 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE:
2128 hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pCache, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); 2195 hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pCache, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
2129 break; 2196 break;
@@ -2839,7 +2906,7 @@ LExit:
2839 return hr; 2906 return hr;
2840} 2907}
2841 2908
2842static HRESULT OnExecuteExePackage( 2909static HRESULT OnExecuteBundlePackage(
2843 __in HANDLE hPipe, 2910 __in HANDLE hPipe,
2844 __in BURN_CACHE* pCache, 2911 __in BURN_CACHE* pCache,
2845 __in BURN_PACKAGES* pPackages, 2912 __in BURN_PACKAGES* pPackages,
@@ -2856,15 +2923,15 @@ static HRESULT OnExecuteExePackage(
2856 LPWSTR sczIgnoreDependencies = NULL; 2923 LPWSTR sczIgnoreDependencies = NULL;
2857 LPWSTR sczAncestors = NULL; 2924 LPWSTR sczAncestors = NULL;
2858 LPWSTR sczEngineWorkingDirectory = NULL; 2925 LPWSTR sczEngineWorkingDirectory = NULL;
2859 BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; 2926 BOOTSTRAPPER_APPLY_RESTART bundleRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2860 2927
2861 executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; 2928 executeAction.type = BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE;
2862 2929
2863 // Deserialize message data. 2930 // Deserialize message data.
2864 hr = BuffReadString(pbData, cbData, &iData, &sczPackage); 2931 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2865 ExitOnFailure(hr, "Failed to read EXE package id."); 2932 ExitOnFailure(hr, "Failed to read EXE package id.");
2866 2933
2867 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action); 2934 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.bundlePackage.action);
2868 ExitOnFailure(hr, "Failed to read action."); 2935 ExitOnFailure(hr, "Failed to read action.");
2869 2936
2870 hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); 2937 hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
@@ -2882,24 +2949,110 @@ static HRESULT OnExecuteExePackage(
2882 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); 2949 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2883 ExitOnFailure(hr, "Failed to read variables."); 2950 ExitOnFailure(hr, "Failed to read variables.");
2884 2951
2885 hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage); 2952 hr = PackageFindById(pPackages, sczPackage, &executeAction.bundlePackage.pPackage);
2886 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); 2953 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2887 2954
2888 if (BURN_PACKAGE_TYPE_EXE != executeAction.exePackage.pPackage->type) 2955 if (BURN_PACKAGE_TYPE_BUNDLE != executeAction.bundlePackage.pPackage->type)
2889 { 2956 {
2890 ExitWithRootFailure(hr, E_INVALIDARG, "Package is not an EXE package: %ls", sczPackage); 2957 ExitWithRootFailure(hr, E_INVALIDARG, "Package is not a BUNDLE package: %ls", sczPackage);
2891 } 2958 }
2892 2959
2893 // Pass the list of dependencies to ignore, if any, to the related bundle. 2960 // Pass the list of dependencies to ignore, if any, to the related bundle.
2894 if (sczIgnoreDependencies && *sczIgnoreDependencies) 2961 if (sczIgnoreDependencies && *sczIgnoreDependencies)
2895 { 2962 {
2896 hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0); 2963 hr = StrAllocString(&executeAction.bundlePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0);
2897 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); 2964 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
2898 } 2965 }
2899 2966
2900 // Pass the list of ancestors, if any, to the related bundle. 2967 // Pass the list of ancestors, if any, to the related bundle.
2901 if (sczAncestors && *sczAncestors) 2968 if (sczAncestors && *sczAncestors)
2902 { 2969 {
2970 hr = StrAllocString(&executeAction.bundlePackage.sczAncestors, sczAncestors, 0);
2971 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
2972 }
2973
2974 if (sczEngineWorkingDirectory && *sczEngineWorkingDirectory)
2975 {
2976 hr = StrAllocString(&executeAction.bundlePackage.sczEngineWorkingDirectory, sczEngineWorkingDirectory, 0);
2977 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
2978 }
2979
2980 // Execute BUNDLE package.
2981 hr = BundlePackageEngineExecutePackage(&executeAction, pCache, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &bundleRestart);
2982 ExitOnFailure(hr, "Failed to execute BUNDLE package.");
2983
2984LExit:
2985 ReleaseStr(sczEngineWorkingDirectory);
2986 ReleaseStr(sczAncestors);
2987 ReleaseStr(sczIgnoreDependencies);
2988 ReleaseStr(sczPackage);
2989 PlanUninitializeExecuteAction(&executeAction);
2990
2991 if (SUCCEEDED(hr))
2992 {
2993 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == bundleRestart)
2994 {
2995 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2996 }
2997 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == bundleRestart)
2998 {
2999 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
3000 }
3001 }
3002
3003 return hr;
3004}
3005
3006static HRESULT OnExecuteExePackage(
3007 __in HANDLE hPipe,
3008 __in BURN_CACHE* pCache,
3009 __in BURN_PACKAGES* pPackages,
3010 __in BURN_VARIABLES* pVariables,
3011 __in BYTE* pbData,
3012 __in SIZE_T cbData
3013 )
3014{
3015 HRESULT hr = S_OK;
3016 SIZE_T iData = 0;
3017 LPWSTR sczPackage = NULL;
3018 DWORD dwRollback = 0;
3019 BURN_EXECUTE_ACTION executeAction = { };
3020 LPWSTR sczAncestors = NULL;
3021 LPWSTR sczEngineWorkingDirectory = NULL;
3022 BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
3023
3024 executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
3025
3026 // Deserialize message data.
3027 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
3028 ExitOnFailure(hr, "Failed to read EXE package id.");
3029
3030 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action);
3031 ExitOnFailure(hr, "Failed to read action.");
3032
3033 hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
3034 ExitOnFailure(hr, "Failed to read rollback.");
3035
3036 hr = BuffReadString(pbData, cbData, &iData, &sczAncestors);
3037 ExitOnFailure(hr, "Failed to read the list of ancestors.");
3038
3039 hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingDirectory);
3040 ExitOnFailure(hr, "Failed to read the custom working directory.");
3041
3042 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
3043 ExitOnFailure(hr, "Failed to read variables.");
3044
3045 hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage);
3046 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
3047
3048 if (BURN_PACKAGE_TYPE_EXE != executeAction.exePackage.pPackage->type)
3049 {
3050 ExitWithRootFailure(hr, E_INVALIDARG, "Package is not an EXE package: %ls", sczPackage);
3051 }
3052
3053 // Pass the list of ancestors, if any, to the related bundle.
3054 if (sczAncestors && *sczAncestors)
3055 {
2903 hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0); 3056 hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0);
2904 ExitOnFailure(hr, "Failed to allocate the list of ancestors."); 3057 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
2905 } 3058 }
@@ -2917,7 +3070,6 @@ static HRESULT OnExecuteExePackage(
2917LExit: 3070LExit:
2918 ReleaseStr(sczEngineWorkingDirectory); 3071 ReleaseStr(sczEngineWorkingDirectory);
2919 ReleaseStr(sczAncestors); 3072 ReleaseStr(sczAncestors);
2920 ReleaseStr(sczIgnoreDependencies);
2921 ReleaseStr(sczPackage); 3073 ReleaseStr(sczPackage);
2922 PlanUninitializeExecuteAction(&executeAction); 3074 PlanUninitializeExecuteAction(&executeAction);
2923 3075
diff --git a/src/burn/engine/elevation.h b/src/burn/engine/elevation.h
index c2fa0627..3484057e 100644
--- a/src/burn/engine/elevation.h
+++ b/src/burn/engine/elevation.h
@@ -89,6 +89,15 @@ HRESULT ElevationExecuteRelatedBundle(
89 __in LPVOID pvContext, 89 __in LPVOID pvContext,
90 __out BOOTSTRAPPER_APPLY_RESTART* pRestart 90 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
91 ); 91 );
92HRESULT ElevationExecuteBundlePackage(
93 __in HANDLE hPipe,
94 __in BURN_EXECUTE_ACTION* pExecuteAction,
95 __in BURN_VARIABLES* pVariables,
96 __in BOOL fRollback,
97 __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
98 __in LPVOID pvContext,
99 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
100 );
92HRESULT ElevationExecuteExePackage( 101HRESULT ElevationExecuteExePackage(
93 __in HANDLE hPipe, 102 __in HANDLE hPipe,
94 __in BURN_EXECUTE_ACTION* pExecuteAction, 103 __in BURN_EXECUTE_ACTION* pExecuteAction,
diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp
index 0a2084e5..a287d171 100644
--- a/src/burn/engine/exeengine.cpp
+++ b/src/burn/engine/exeengine.cpp
@@ -91,7 +91,6 @@ extern "C" void ExeEnginePackageUninitialize(
91 ReleaseStr(pPackage->Exe.sczInstallArguments); 91 ReleaseStr(pPackage->Exe.sczInstallArguments);
92 ReleaseStr(pPackage->Exe.sczRepairArguments); 92 ReleaseStr(pPackage->Exe.sczRepairArguments);
93 ReleaseStr(pPackage->Exe.sczUninstallArguments); 93 ReleaseStr(pPackage->Exe.sczUninstallArguments);
94 ReleaseStr(pPackage->Exe.sczIgnoreDependencies);
95 ReleaseMem(pPackage->Exe.rgExitCodes); 94 ReleaseMem(pPackage->Exe.rgExitCodes);
96 95
97 // free command-line arguments 96 // free command-line arguments
@@ -291,12 +290,6 @@ extern "C" HRESULT ExeEnginePlanAddPackage(
291 pAction->exePackage.pPackage = pPackage; 290 pAction->exePackage.pPackage = pPackage;
292 pAction->exePackage.action = pPackage->rollback; 291 pAction->exePackage.action = pPackage->rollback;
293 292
294 if (pPackage->Exe.sczIgnoreDependencies)
295 {
296 hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
297 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
298 }
299
300 if (pPackage->Exe.wzAncestors) 293 if (pPackage->Exe.wzAncestors)
301 { 294 {
302 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0); 295 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0);
@@ -325,12 +318,6 @@ extern "C" HRESULT ExeEnginePlanAddPackage(
325 pAction->exePackage.pPackage = pPackage; 318 pAction->exePackage.pPackage = pPackage;
326 pAction->exePackage.action = pPackage->execute; 319 pAction->exePackage.action = pPackage->execute;
327 320
328 if (pPackage->Exe.sczIgnoreDependencies)
329 {
330 hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
331 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
332 }
333
334 if (pPackage->Exe.wzAncestors) 321 if (pPackage->Exe.wzAncestors)
335 { 322 {
336 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0); 323 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0);
@@ -455,12 +442,11 @@ extern "C" HRESULT ExeEngineExecutePackage(
455 hr = StrAllocConcat(&sczBaseCommand, L" -norestart", 0); 442 hr = StrAllocConcat(&sczBaseCommand, L" -norestart", 0);
456 ExitOnFailure(hr, "Failed to append norestart argument."); 443 ExitOnFailure(hr, "Failed to append norestart argument.");
457 444
458 // Add the list of dependencies to ignore, if any, to the burn command line. 445 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls", BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE);
459 if (pExecuteAction->exePackage.sczIgnoreDependencies) 446 ExitOnFailure(hr, "Failed to append the relation type to the command line.");
460 { 447
461 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); 448 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=ALL", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
462 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); 449 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
463 }
464 450
465 // Add the list of ancestors, if any, to the burn command line. 451 // Add the list of ancestors, if any, to the burn command line.
466 if (pExecuteAction->exePackage.sczAncestors) 452 if (pExecuteAction->exePackage.sczAncestors)
diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp
index de332f84..a766b896 100644
--- a/src/burn/engine/logging.cpp
+++ b/src/burn/engine/logging.cpp
@@ -781,6 +781,8 @@ extern "C" LPCSTR LoggingRelationTypeToString(
781 return "DependentPatch"; 781 return "DependentPatch";
782 case BOOTSTRAPPER_RELATION_UPDATE: 782 case BOOTSTRAPPER_RELATION_UPDATE:
783 return "Update"; 783 return "Update";
784 case BOOTSTRAPPER_RELATION_CHAIN_PACKAGE:
785 return "ChainPackage";
784 default: 786 default:
785 return "Invalid"; 787 return "Invalid";
786 } 788 }
diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp
index 8a80194e..89203ada 100644
--- a/src/burn/engine/package.cpp
+++ b/src/burn/engine/package.cpp
@@ -87,7 +87,7 @@ extern "C" HRESULT PackagesParseFromXml(
87 ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements. 87 ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements.
88 88
89 // select package nodes 89 // select package nodes
90 hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes); 90 hr = XmlSelectNodes(pixnBundle, L"Chain/BundlePackage|Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes);
91 ExitOnFailure(hr, "Failed to select package nodes."); 91 ExitOnFailure(hr, "Failed to select package nodes.");
92 92
93 // get package node count 93 // get package node count
@@ -199,7 +199,14 @@ extern "C" HRESULT PackagesParseFromXml(
199 } 199 }
200 200
201 // read type specific attributes 201 // read type specific attributes
202 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1)) 202 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"BundlePackage", -1))
203 {
204 pPackage->type = BURN_PACKAGE_TYPE_BUNDLE;
205
206 hr = BundlePackageEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
207 ExitOnFailure(hr, "Failed to parse BUNDLE package.");
208 }
209 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1))
203 { 210 {
204 pPackage->type = BURN_PACKAGE_TYPE_EXE; 211 pPackage->type = BURN_PACKAGE_TYPE_EXE;
205 212
@@ -231,7 +238,7 @@ extern "C" HRESULT PackagesParseFromXml(
231 } 238 }
232 else 239 else
233 { 240 {
234 // ignore other package types for now 241 ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid package type: %ls", bstrNodeName);
235 } 242 }
236 243
237 if (!pPackage->fPermanent) 244 if (!pPackage->fPermanent)
@@ -371,7 +378,7 @@ extern "C" void PackageUninitialize(
371 switch (pPackage->type) 378 switch (pPackage->type)
372 { 379 {
373 case BURN_PACKAGE_TYPE_BUNDLE: 380 case BURN_PACKAGE_TYPE_BUNDLE:
374 BundlePackageEnginePackageUninitialize(pPackage); 381 BundlePackageEnginePackageUninitialize(pPackage); // TODO: Modularization
375 break; 382 break;
376 case BURN_PACKAGE_TYPE_EXE: 383 case BURN_PACKAGE_TYPE_EXE:
377 ExeEnginePackageUninitialize(pPackage); // TODO: Modularization 384 ExeEnginePackageUninitialize(pPackage); // TODO: Modularization
@@ -648,7 +655,6 @@ static HRESULT ParsePatchTargetCode(
648 IXMLDOMNodeList* pixnNodes = NULL; 655 IXMLDOMNodeList* pixnNodes = NULL;
649 IXMLDOMNode* pixnNode = NULL; 656 IXMLDOMNode* pixnNode = NULL;
650 DWORD cNodes = 0; 657 DWORD cNodes = 0;
651 BSTR bstrNodeText = NULL;
652 BOOL fProduct; 658 BOOL fProduct;
653 659
654 hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes); 660 hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes);
@@ -688,12 +694,10 @@ static HRESULT ParsePatchTargetCode(
688 pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE; 694 pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE;
689 695
690 // prepare next iteration 696 // prepare next iteration
691 ReleaseNullBSTR(bstrNodeText);
692 ReleaseNullObject(pixnNode); 697 ReleaseNullObject(pixnNode);
693 } 698 }
694 699
695LExit: 700LExit:
696 ReleaseBSTR(bstrNodeText);
697 ReleaseObject(pixnNode); 701 ReleaseObject(pixnNode);
698 ReleaseObject(pixnNodes); 702 ReleaseObject(pixnNodes);
699 703
diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h
index deab47b7..c13c651b 100644
--- a/src/burn/engine/package.h
+++ b/src/burn/engine/package.h
@@ -302,6 +302,8 @@ typedef struct _BURN_PACKAGE
302 { 302 {
303 struct 303 struct
304 { 304 {
305 LPWSTR sczBundleId;
306 LPWSTR sczRegistrationKey;
305 LPWSTR sczInstallArguments; 307 LPWSTR sczInstallArguments;
306 LPWSTR sczRepairArguments; 308 LPWSTR sczRepairArguments;
307 LPWSTR sczUninstallArguments; 309 LPWSTR sczUninstallArguments;
@@ -310,6 +312,7 @@ typedef struct _BURN_PACKAGE
310 LPCWSTR wzAncestors; // points directly into engine state. 312 LPCWSTR wzAncestors; // points directly into engine state.
311 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state. 313 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
312 314
315 BOOL fWin64;
313 BOOL fSupportsBurnProtocol; 316 BOOL fSupportsBurnProtocol;
314 317
315 BURN_EXE_EXIT_CODE* rgExitCodes; 318 BURN_EXE_EXIT_CODE* rgExitCodes;
@@ -324,7 +327,6 @@ typedef struct _BURN_PACKAGE
324 LPWSTR sczInstallArguments; 327 LPWSTR sczInstallArguments;
325 LPWSTR sczRepairArguments; 328 LPWSTR sczRepairArguments;
326 LPWSTR sczUninstallArguments; 329 LPWSTR sczUninstallArguments;
327 LPWSTR sczIgnoreDependencies;
328 LPCWSTR wzAncestors; // points directly into engine state. 330 LPCWSTR wzAncestors; // points directly into engine state.
329 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state. 331 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
330 332
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp
index cb50b0c9..1d008dba 100644
--- a/src/burn/engine/plan.cpp
+++ b/src/burn/engine/plan.cpp
@@ -292,7 +292,6 @@ extern "C" void PlanUninitializeExecuteAction(
292 break; 292 break;
293 293
294 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: 294 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
295 ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies);
296 ReleaseStr(pExecuteAction->exePackage.sczAncestors); 295 ReleaseStr(pExecuteAction->exePackage.sczAncestors);
297 ReleaseStr(pExecuteAction->exePackage.sczEngineWorkingDirectory); 296 ReleaseStr(pExecuteAction->exePackage.sczEngineWorkingDirectory);
298 break; 297 break;
@@ -1181,6 +1180,10 @@ extern "C" HRESULT PlanExecutePackage(
1181 // Add execute actions. 1180 // Add execute actions.
1182 switch (pPackage->type) 1181 switch (pPackage->type)
1183 { 1182 {
1183 case BURN_PACKAGE_TYPE_BUNDLE:
1184 hr = BundlePackageEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables);
1185 break;
1186
1184 case BURN_PACKAGE_TYPE_EXE: 1187 case BURN_PACKAGE_TYPE_EXE:
1185 hr = ExeEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables); 1188 hr = ExeEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables);
1186 break; 1189 break;
@@ -1506,16 +1509,16 @@ extern "C" HRESULT PlanRelatedBundlesComplete(
1506 1509
1507 switch (pPlan->rgExecuteActions[i].type) 1510 switch (pPlan->rgExecuteActions[i].type)
1508 { 1511 {
1509 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE: 1512 case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
1510 packageAction = pPlan->rgExecuteActions[i].relatedBundle.action; 1513 packageAction = pPlan->rgExecuteActions[i].bundlePackage.action;
1511 pPackage = &pPlan->rgExecuteActions[i].relatedBundle.pRelatedBundle->package; 1514 pPackage = pPlan->rgExecuteActions[i].bundlePackage.pPackage;
1512 fBundle = TRUE; 1515 fBundle = TRUE;
1513 break; 1516 break;
1514 1517
1515 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: 1518 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
1516 packageAction = pPlan->rgExecuteActions[i].exePackage.action; 1519 packageAction = pPlan->rgExecuteActions[i].exePackage.action;
1517 pPackage = pPlan->rgExecuteActions[i].exePackage.pPackage; 1520 pPackage = pPlan->rgExecuteActions[i].exePackage.pPackage;
1518 fBundle = TRUE; 1521 fBundle = pPackage->Exe.fBundle;
1519 break; 1522 break;
1520 1523
1521 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: 1524 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
@@ -2751,6 +2754,10 @@ static HRESULT CalculateExecuteActions(
2751 // Calculate execute actions. 2754 // Calculate execute actions.
2752 switch (pPackage->type) 2755 switch (pPackage->type)
2753 { 2756 {
2757 case BURN_PACKAGE_TYPE_BUNDLE:
2758 hr = BundlePackageEnginePlanCalculatePackage(pPackage);
2759 break;
2760
2754 case BURN_PACKAGE_TYPE_EXE: 2761 case BURN_PACKAGE_TYPE_EXE:
2755 hr = ExeEnginePlanCalculatePackage(pPackage); 2762 hr = ExeEnginePlanCalculatePackage(pPackage);
2756 break; 2763 break;
@@ -2784,7 +2791,8 @@ static BOOL NeedsCache(
2784 ) 2791 )
2785{ 2792{
2786 BOOTSTRAPPER_ACTION_STATE action = fExecute ? pPackage->execute : pPackage->rollback; 2793 BOOTSTRAPPER_ACTION_STATE action = fExecute ? pPackage->execute : pPackage->rollback;
2787 if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall). 2794 // TODO: bundles could theoretically use package cache
2795 if (BURN_PACKAGE_TYPE_BUNDLE == pPackage->type || BURN_PACKAGE_TYPE_EXE == pPackage->type) // Bundle and Exe packages require the package for all operations (even uninstall).
2788 { 2796 {
2789 return BOOTSTRAPPER_ACTION_STATE_NONE != action; 2797 return BOOTSTRAPPER_ACTION_STATE_NONE != action;
2790 } 2798 }
@@ -2918,6 +2926,10 @@ static void ExecuteActionLog(
2918 LogStringLine(PlanDumpLevel, "%ls action[%u]: RELATED_BUNDLE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->relatedBundle.pRelatedBundle->package.sczId, LoggingActionStateToString(pAction->relatedBundle.action), pAction->relatedBundle.sczIgnoreDependencies); 2926 LogStringLine(PlanDumpLevel, "%ls action[%u]: RELATED_BUNDLE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->relatedBundle.pRelatedBundle->package.sczId, LoggingActionStateToString(pAction->relatedBundle.action), pAction->relatedBundle.sczIgnoreDependencies);
2919 break; 2927 break;
2920 2928
2929 case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
2930 LogStringLine(PlanDumpLevel, "%ls action[%u]: BUNDLE_PACKAGE package id: %ls, action: %hs", wzBase, iAction, pAction->bundlePackage.pPackage->sczId, LoggingActionStateToString(pAction->bundlePackage.action));
2931 break;
2932
2921 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: 2933 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2922 LogStringLine(PlanDumpLevel, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action)); 2934 LogStringLine(PlanDumpLevel, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action));
2923 break; 2935 break;
diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h
index 6e9a1ff5..5b6ee0fb 100644
--- a/src/burn/engine/plan.h
+++ b/src/burn/engine/plan.h
@@ -43,6 +43,7 @@ enum BURN_EXECUTE_ACTION_TYPE
43 BURN_EXECUTE_ACTION_TYPE_WAIT_CACHE_PACKAGE, 43 BURN_EXECUTE_ACTION_TYPE_WAIT_CACHE_PACKAGE,
44 BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, 44 BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE,
45 BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE, 45 BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE,
46 BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE,
46 BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, 47 BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE,
47 BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, 48 BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE,
48 BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, 49 BURN_EXECUTE_ACTION_TYPE_MSP_TARGET,
@@ -167,6 +168,13 @@ typedef struct _BURN_EXECUTE_ACTION
167 LPWSTR sczIgnoreDependencies; 168 LPWSTR sczIgnoreDependencies;
168 LPWSTR sczAncestors; 169 LPWSTR sczAncestors;
169 LPWSTR sczEngineWorkingDirectory; 170 LPWSTR sczEngineWorkingDirectory;
171 } bundlePackage;
172 struct
173 {
174 BURN_PACKAGE* pPackage;
175 BOOTSTRAPPER_ACTION_STATE action;
176 LPWSTR sczAncestors;
177 LPWSTR sczEngineWorkingDirectory;
170 } exePackage; 178 } exePackage;
171 struct 179 struct
172 { 180 {
diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp
index a65c30d3..78f8eeb1 100644
--- a/src/burn/engine/registration.cpp
+++ b/src/burn/engine/registration.cpp
@@ -7,7 +7,6 @@
7 7
8const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; 8const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
9const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; 9const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
10const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed";
11const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon"; 10const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon";
12const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion"; 11const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion";
13const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize"; 12const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize";
@@ -1219,7 +1218,7 @@ static HRESULT SetPaths(
1219 pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 1218 pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1220 1219
1221 // build uninstall registry key path 1220 // build uninstall registry key path
1222 hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId); 1221 hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId);
1223 ExitOnFailure(hr, "Failed to build uninstall registry key path."); 1222 ExitOnFailure(hr, "Failed to build uninstall registry key path.");
1224 1223
1225 // build cache directory 1224 // build cache directory
@@ -1231,7 +1230,7 @@ static HRESULT SetPaths(
1231 ExitOnFailure(hr, "Failed to build cached executable path."); 1230 ExitOnFailure(hr, "Failed to build cached executable path.");
1232 1231
1233 // build state file path 1232 // build state file path
1234 hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory); 1233 hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%ls\\state.rsm", sczCacheDirectory);
1235 ExitOnFailure(hr, "Failed to build state file path."); 1234 ExitOnFailure(hr, "Failed to build state file path.");
1236 1235
1237LExit: 1236LExit:
diff --git a/src/burn/engine/registration.h b/src/burn/engine/registration.h
index e4dd6f4a..58c883da 100644
--- a/src/burn/engine/registration.h
+++ b/src/burn/engine/registration.h
@@ -22,6 +22,8 @@ const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_PROTOCOL_VERSION = L"EngineProto
22const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; 22const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
23const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; 23const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag";
24 24
25const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed";
26
25enum BURN_RESUME_MODE 27enum BURN_RESUME_MODE
26{ 28{
27 BURN_RESUME_MODE_NONE, 29 BURN_RESUME_MODE_NONE,
diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
index 35415dc3..b0159840 100644
--- a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
+++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
@@ -80,6 +80,7 @@
80 <ItemGroup> 80 <ItemGroup>
81 <None Include="TestData\CacheTest\CacheSignatureTest.File" CopyToOutputDirectory="PreserveNewest" /> 81 <None Include="TestData\CacheTest\CacheSignatureTest.File" CopyToOutputDirectory="PreserveNewest" />
82 <None Include="TestData\PlanTest\BasicFunctionality_BundleA_manifest.xml" CopyToOutputDirectory="PreserveNewest" /> 82 <None Include="TestData\PlanTest\BasicFunctionality_BundleA_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
83 <None Include="TestData\PlanTest\BundlePackage_Multiple_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
83 <None Include="TestData\PlanTest\Failure_BundleD_manifest.xml" CopyToOutputDirectory="PreserveNewest" /> 84 <None Include="TestData\PlanTest\Failure_BundleD_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
84 <None Include="TestData\PlanTest\MsiTransaction_BundleAv1_manifest.xml" CopyToOutputDirectory="PreserveNewest" /> 85 <None Include="TestData\PlanTest\MsiTransaction_BundleAv1_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
85 <None Include="TestData\PlanTest\MsuPackageFixture_manifest.xml" CopyToOutputDirectory="PreserveNewest" /> 86 <None Include="TestData\PlanTest\MsuPackageFixture_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp
index 143ca7d7..536e5351 100644
--- a/src/burn/test/BurnUnitTest/PlanTest.cpp
+++ b/src/burn/test/BurnUnitTest/PlanTest.cpp
@@ -10,6 +10,7 @@ static HRESULT WINAPI PlanTestBAProc(
10 ); 10 );
11 11
12static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; 12static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml";
13static LPCWSTR wzMultipleBundlePackageManifestFileName = L"BundlePackage_Multiple_manifest.xml";
13static LPCWSTR wzSingleExeManifestFileName = L"Failure_BundleD_manifest.xml"; 14static LPCWSTR wzSingleExeManifestFileName = L"Failure_BundleD_manifest.xml";
14static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; 15static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml";
15static LPCWSTR wzSingleMsuManifestFileName = L"MsuPackageFixture_manifest.xml"; 16static LPCWSTR wzSingleMsuManifestFileName = L"MsuPackageFixture_manifest.xml";
@@ -340,6 +341,121 @@ namespace Bootstrapper
340 } 341 }
341 342
342 [Fact] 343 [Fact]
344 void MultipleBundlePackageInstallTest()
345 {
346 HRESULT hr = S_OK;
347 BURN_ENGINE_STATE engineState = { };
348 BURN_ENGINE_STATE* pEngineState = &engineState;
349 BURN_PLAN* pPlan = &engineState.plan;
350
351 InitializeEngineStateForCorePlan(wzMultipleBundlePackageManifestFileName, pEngineState);
352 DetectAttachedContainerAsAttached(pEngineState);
353 DetectPermanentPackagesAsPresentAndCached(pEngineState);
354
355 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL);
356 NativeAssert::Succeeded(hr, "CorePlan failed");
357
358 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action);
359 NativeAssert::StringEqual(L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", pPlan->wzBundleId);
360 NativeAssert::StringEqual(L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", pPlan->wzBundleProviderKey);
361 Assert::Equal<BOOL>(FALSE, pPlan->fEnabledForwardCompatibleBundle);
362 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
363 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
364 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
365 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
366 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
367
368 BOOL fRollback = FALSE;
369 DWORD dwIndex = 0;
370 ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, TRUE, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}");
371 Assert::Equal(dwIndex, pPlan->cRegistrationActions);
372
373 fRollback = TRUE;
374 dwIndex = 0;
375 ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, FALSE, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}");
376 Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions);
377
378 fRollback = FALSE;
379 dwIndex = 0;
380 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
381 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
382 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
383 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 6);
384 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB");
385 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
386 Assert::Equal(dwIndex, pPlan->cCacheActions);
387
388 fRollback = TRUE;
389 dwIndex = 0;
390 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
391
392 Assert::Equal(18575450ull, pPlan->qwEstimatedSize);
393 Assert::Equal(78462280ull, pPlan->qwCacheSizeTotal);
394
395 fRollback = FALSE;
396 dwIndex = 0;
397 DWORD dwExecuteCheckpointId = 2;
398 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
399 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
400 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
401 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
402 ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL);
403 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
404 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", registerActions1, 1);
405 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
406 dwExecuteCheckpointId += 1; // cache checkpoints
407 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
408 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageB");
409 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
410 ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL);
411 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
412 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", registerActions1, 1);
413 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
414 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
415 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
416 Assert::Equal(dwIndex, pPlan->cExecuteActions);
417
418 fRollback = TRUE;
419 dwIndex = 0;
420 dwExecuteCheckpointId = 2;
421 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
422 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
423 ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL);
424 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
425 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", unregisterActions1, 1);
426 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
427 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
428 dwExecuteCheckpointId += 1; // cache checkpoints
429 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
430 ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL);
431 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
432 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", unregisterActions1, 1);
433 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
434 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
435 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
436 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
437 Assert::Equal(dwIndex, pPlan->cRollbackActions);
438
439 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
440 Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal);
441
442 dwIndex = 0;
443 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
444
445 dwIndex = 0;
446 ValidateCleanAction(pPlan, dwIndex++, L"NetFx48Web");
447 Assert::Equal(dwIndex, pPlan->cCleanActions);
448
449 UINT uIndex = 0;
450 ValidatePlannedProvider(pPlan, uIndex++, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", NULL);
451 Assert::Equal(uIndex, pPlan->cPlannedProviders);
452
453 Assert::Equal(3ul, pEngineState->packages.cPackages);
454 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
455 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
456 }
457
458 [Fact]
343 void OrphanCompatiblePackageTest() 459 void OrphanCompatiblePackageTest()
344 { 460 {
345 HRESULT hr = S_OK; 461 HRESULT hr = S_OK;
@@ -716,7 +832,7 @@ namespace Bootstrapper
716 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 832 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
717 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"ExeA"); 833 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"ExeA");
718 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 834 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
719 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); 835 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_INSTALL);
720 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 836 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
721 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 837 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
722 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); 838 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
@@ -727,7 +843,7 @@ namespace Bootstrapper
727 dwExecuteCheckpointId = 2; 843 dwExecuteCheckpointId = 2;
728 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); 844 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
729 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 845 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
730 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); 846 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL);
731 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 847 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
732 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 848 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
733 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 849 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
@@ -2125,7 +2241,7 @@ namespace Bootstrapper
2125 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); 2241 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
2126 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 2242 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
2127 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web"); 2243 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web");
2128 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); 2244 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web", BOOTSTRAPPER_ACTION_STATE_INSTALL);
2129 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 2245 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
2130 dwExecuteCheckpointId += 1; // cache checkpoints 2246 dwExecuteCheckpointId += 1; // cache checkpoints
2131 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); 2247 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
@@ -2678,20 +2794,33 @@ namespace Bootstrapper
2678 Assert::Equal<BOOL>(FALSE, pAction->fDeleted); 2794 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
2679 } 2795 }
2680 2796
2797 void ValidateExecuteBundlePackage(
2798 __in BURN_PLAN* pPlan,
2799 __in BOOL fRollback,
2800 __in DWORD dwIndex,
2801 __in LPCWSTR wzPackageId,
2802 __in BOOTSTRAPPER_ACTION_STATE action
2803 )
2804 {
2805 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
2806 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE, pAction->type);
2807 NativeAssert::StringEqual(wzPackageId, pAction->bundlePackage.pPackage->sczId);
2808 Assert::Equal<DWORD>(action, pAction->bundlePackage.action);
2809 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
2810 }
2811
2681 void ValidateExecuteExePackage( 2812 void ValidateExecuteExePackage(
2682 __in BURN_PLAN* pPlan, 2813 __in BURN_PLAN* pPlan,
2683 __in BOOL fRollback, 2814 __in BOOL fRollback,
2684 __in DWORD dwIndex, 2815 __in DWORD dwIndex,
2685 __in LPCWSTR wzPackageId, 2816 __in LPCWSTR wzPackageId,
2686 __in BOOTSTRAPPER_ACTION_STATE action, 2817 __in BOOTSTRAPPER_ACTION_STATE action
2687 __in LPCWSTR wzIgnoreDependencies
2688 ) 2818 )
2689 { 2819 {
2690 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); 2820 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
2691 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type); 2821 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type);
2692 NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId); 2822 NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId);
2693 Assert::Equal<DWORD>(action, pAction->exePackage.action); 2823 Assert::Equal<DWORD>(action, pAction->exePackage.action);
2694 NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies);
2695 Assert::Equal<BOOL>(FALSE, pAction->fDeleted); 2824 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
2696 } 2825 }
2697 2826
diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml
new file mode 100644
index 00000000..6c60085f
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml
@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><BurnManifest xmlns="http://wixtoolset.org/schemas/v4/2008/Burn"><Log PathVariable="WixBundleLog" Prefix="~BundlePackageTests_MultipleBundlePackagesBundle" Extension=".log" /><RelatedBundle Id="{86D214FB-8D74-456C-99B3-6557ECA6159C}" Action="Upgrade" /><Variable Id="TestGroupName" Value="BundlePackageTests" Type="string" Hidden="no" Persisted="no" /><Variable Id="WixBundleInProgressName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSource" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSourceFolder" Hidden="no" Persisted="yes" /><Variable Id="WixBundleLastUsedSource" Hidden="no" Persisted="yes" /><RegistrySearch Id="NETFRAMEWORK45" Variable="NETFRAMEWORK45" Root="HKLM" Key="SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" Value="Release" Type="value" VariableType="string" /><UX><Payload Id="WixManagedBootstrapperApplicationHost" FilePath="mbahost.dll" SourcePath="u30" /><Payload Id="payO60IVK4ATGzPpMz3rwVbUWl6DyU" FilePath="WixToolset.Mba.Host.config" SourcePath="u0" /><Payload Id="payxj4zDAKL2NVlz4ohp0GvwFHepyI" FilePath="TestBA.dll" SourcePath="u1" /><Payload Id="pay1hOSAUC8_D633cD2TXpIXCL30OU" FilePath="mbanative.dll" SourcePath="u2" /><Payload Id="payujy6Izl_BlUNfHt2eI.ADfjYAv4" FilePath="WixToolset.Mba.Core.dll" SourcePath="u3" /><Payload Id="payR4EbR4OTDZpPEycWaSSM_gZRBWM" FilePath="mbapreq.thm" SourcePath="u4" /><Payload Id="paylVCy2Ecl8pHPdJTCQZryUG4T9us" FilePath="mbapreq.png" SourcePath="u5" /><Payload Id="payTaG4B_lob1aLcKFaOqSSG3MPMpU" FilePath="mbapreq.wxl" SourcePath="u6" /><Payload Id="payZwIGuiezVTitZOoZKxyh2DdRSGs" FilePath="1028\mbapreq.wxl" SourcePath="u7" /><Payload Id="pay.herBWX.LlOh8jLsx24aWdunV_0" FilePath="1029\mbapreq.wxl" SourcePath="u8" /><Payload Id="pay8DkMszYsoxxdgX14huLDMYXylQg" FilePath="1030\mbapreq.wxl" SourcePath="u9" /><Payload Id="payPaHpoTeOdkW.TK99IDwktNLhTAg" FilePath="1031\mbapreq.wxl" SourcePath="u10" /><Payload Id="pay45AtAzterLTMzZgdxxtuYvaiXwU" FilePath="1032\mbapreq.wxl" SourcePath="u11" /><Payload Id="payA2VEKIqhePyNIEmr14eyH3JoVLc" FilePath="1035\mbapreq.wxl" SourcePath="u12" /><Payload Id="payvre23ObscjzhcaFIifUAkXMdPa8" FilePath="1036\mbapreq.wxl" SourcePath="u13" /><Payload Id="paytxUV3vuBbG2c.a9c.d_sZX2x6wA" FilePath="1038\mbapreq.wxl" SourcePath="u14" /><Payload Id="payYvMWRK9xelo5.sQn7jRkJIaBp9A" FilePath="1040\mbapreq.wxl" SourcePath="u15" /><Payload Id="pay68KKSApyQimbA25t6kSbqhdeH10" FilePath="1041\mbapreq.wxl" SourcePath="u16" /><Payload Id="paypiqxaHpYZqx.9eDVjQrj1igLbRY" FilePath="1042\mbapreq.wxl" SourcePath="u17" /><Payload Id="payTO0YwZzxKpbqdrBVUcVRTu3BFe8" FilePath="1043\mbapreq.wxl" SourcePath="u18" /><Payload Id="payIXg2ldBJukRzhqWolJVOEbTmF34" FilePath="1044\mbapreq.wxl" SourcePath="u19" /><Payload Id="payOHIZbSkIvrpwKkkXI173tv3u3B4" FilePath="1045\mbapreq.wxl" SourcePath="u20" /><Payload Id="payQRQ_UZl_R2UtV0xDXB2yeH2bg3E" FilePath="1046\mbapreq.wxl" SourcePath="u21" /><Payload Id="payhrejLLBfc1i27iN._QPhQ4K337I" FilePath="1049\mbapreq.wxl" SourcePath="u22" /><Payload Id="payqEzaDNzxB68vGp29jgDcCos6dvg" FilePath="1051\mbapreq.wxl" SourcePath="u23" /><Payload Id="paydz8Vk8xSTyYohgGXTSIxWGXL5.Q" FilePath="1053\mbapreq.wxl" SourcePath="u24" /><Payload Id="pay0HRUZTlbC3taSOffJBsEj92Br8Y" FilePath="1055\mbapreq.wxl" SourcePath="u25" /><Payload Id="payIvUOkc_EMH7laMFehefNolV8hZo" FilePath="1060\mbapreq.wxl" SourcePath="u26" /><Payload Id="payLFhOb.rHuk4sW5CYAPMShG0NjGI" FilePath="2052\mbapreq.wxl" SourcePath="u27" /><Payload Id="payqIKCmERK7Nhxx_nNXvRxdKqKDbI" FilePath="2070\mbapreq.wxl" SourcePath="u28" /><Payload Id="payqeWUzIVaEqjuRXN0z8ECC3Y4tCc" FilePath="3082\mbapreq.wxl" SourcePath="u29" /><Payload Id="paylfeHEjJSSTnNzY9QMZM2Ye3Ipy4" FilePath="mbapreq.dll" SourcePath="u31" /><Payload Id="payDPxs6uy8nbky.R7zhir2RRAfc.c" FilePath="WixToolset.Mba.Host.dll" SourcePath="u32" /><Payload Id="uxTxMXPVMXwQrPTMIGa5WGt93w0Ns" FilePath="BootstrapperApplicationData.xml" SourcePath="u33" /><Payload Id="uxYRbgitOs0K878jn5L_z7LdJ21KI" FilePath="BundleExtensionData.xml" SourcePath="u34" /></UX><Container Id="WixAttachedContainer" FileSize="15696370" Hash="0F9966B421400E481D394DB4C4D7F0F92548E5BEB79B98880C926E817E8C1F381EC8A17053E2E66AE7132A3C9ECE441629E6A1FB3452C5C9282280C40252F141" FilePath="MultipleBundlePackagesBundle.exe" AttachedIndex="1" Attached="yes" Primary="yes" /><Payload Id="NetFx48Web" FilePath="redist\ndp48-web.exe" FileSize="1439328" CertificateRootPublicKeyIdentifier="F49F9B33E25E33CCA0BFB15A62B7C29FFAB3880B" CertificateRootThumbprint="ABDCA79AF9DD48A0EA702AD45260B3C03093FB4B" DownloadUrl="https://go.microsoft.com/fwlink/?LinkId=2085155" Packaging="external" SourcePath="redist\ndp48-web.exe" /><Payload Id="PackageA" FilePath="BundleA.exe" FileSize="5241635" Hash="20E1AFF76DE4693CB2876DC6BCCA0152DB16BE49AEDE2CD581C03FC39AB89DEA12BC25CB435F06E4D7D2B4443CE8A8935D5E92E2E49A4981B60A273980E4B29B" Packaging="embedded" SourcePath="a0" Container="WixAttachedContainer" /><Payload Id="PackageB" FilePath="BundleB_x64.exe" FileSize="10450821" Hash="43A58873D61D6E0FA83F6C5266F2F05FEA9BC85D11C195493B7FD9F0B4AA799C1EFCB78D76DCED32124D2EC62A4E7114B62CDE6F0B87E42A7E28CDBB0DA0FF8E" Packaging="embedded" SourcePath="a1" Container="WixAttachedContainer" /><RollbackBoundary Id="WixDefaultBoundary" Vital="yes" Transaction="no" /><Registration Id="{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}" ExecutableName="MultipleBundlePackagesBundle.exe" PerMachine="yes" Tag="" Version="1.0.0.0" ProviderKey="{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"><Arp Register="yes" DisplayName="~BundlePackageTests - MultipleBundlePackagesBundle" DisplayVersion="1.0.0.0" /></Registration><Chain><ExePackage Id="NetFx48Web" Cache="remove" CacheId="642721C60D52051C7F3434D8710FE3406A7CFE10B2B39E90EA847719ED1697D7C614F2DF44AD50412B1DF8C98DD78FDC57CA1D047D28C81AC158092E5FB18040" InstallSize="1439328" Size="1439328" PerMachine="yes" Permanent="yes" Vital="yes" RollbackBoundaryForward="WixDefaultBoundary" LogPathVariable="NetFx48WebLog" RollbackLogPathVariable="WixBundleRollbackLog_NetFx48Web" DetectCondition="NETFRAMEWORK45 &gt;= 528040" InstallArguments="/q /norestart /log &quot;[NetFx48WebLog].html&quot;" UninstallArguments="" Uninstallable="no" RepairArguments="" Repairable="no" Protocol="netfx4"><PayloadRef Id="NetFx48Web" /></ExePackage><BundlePackage Id="PackageA" Cache="keep" CacheId="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}v1.0.0.0" InstallSize="2169" Size="5241635" PerMachine="yes" Permanent="no" Vital="yes" LogPathVariable="WixBundleLog_PackageA" RollbackLogPathVariable="WixBundleRollbackLog_PackageA" BundleId="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}" InstallArguments="" UninstallArguments="" RepairArguments="" SupportsBurnProtocol="yes" Win64="no"><Provides Key="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}" Version="1.0.0.0" DisplayName="~BasicFunctionalityTests - BundleA" Imported="yes" /><PayloadRef Id="PackageA" /></BundlePackage><BundlePackage Id="PackageB" Cache="keep" CacheId="{7506235A-7C59-4750-82C7-EB460A87ED3A}v1.0.0.0" InstallSize="1441497" Size="10450821" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryBackward="WixDefaultBoundary" LogPathVariable="WixBundleLog_PackageB" RollbackLogPathVariable="WixBundleRollbackLog_PackageB" BundleId="{7506235A-7C59-4750-82C7-EB460A87ED3A}" InstallArguments="" UninstallArguments="" RepairArguments="" SupportsBurnProtocol="yes" Win64="yes"><Provides Key="{7506235A-7C59-4750-82C7-EB460A87ED3A}" Version="1.0.0.0" DisplayName="~BasicFunctionalityTests - BundleB_x64" Imported="yes" /><PayloadRef Id="PackageB" /></BundlePackage></Chain><CommandLine Variables="upperCase" /></BurnManifest> \ No newline at end of file
diff --git a/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj b/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj
new file mode 100644
index 00000000..a9639014
--- /dev/null
+++ b/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj
@@ -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>{86D214FB-8D74-456C-99B3-6557ECA6159C}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <ProjectReference Include="..\..\BasicFunctionalityTests\BundleA\BundleA.wixproj" />
12 <ProjectReference Include="..\..\BasicFunctionalityTests\BundleB_x64\BundleB_x64.wixproj" />
13 <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
14 </ItemGroup>
15 <ItemGroup>
16 <PackageReference Include="WixToolset.Bal.wixext" />
17 <PackageReference Include="WixToolset.NetFx.wixext" />
18 </ItemGroup>
19</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs b/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs
new file mode 100644
index 00000000..e0071874
--- /dev/null
+++ b/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs
@@ -0,0 +1,11 @@
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
4<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <Fragment>
6 <PackageGroup Id="BundlePackages">
7 <BundlePackage Id="PackageA" SourceFile="$(var.BundleA.TargetPath)" />
8 <BundlePackage Id="PackageB" SourceFile="$(var.BundleB_x64.TargetPath)" />
9 </PackageGroup>
10 </Fragment>
11</Wix>
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs
new file mode 100644
index 00000000..2e95aedb
--- /dev/null
+++ b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs
@@ -0,0 +1,51 @@
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
3namespace WixToolsetTest.BurnE2E
4{
5 using System.IO;
6 using Xunit;
7 using Xunit.Abstractions;
8
9 public class BundlePackageTests : BurnE2ETests
10 {
11 public BundlePackageTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
12
13 [Fact]
14 public void CanInstallAndUninstallBundlePackages()
15 {
16 var packageA = this.CreatePackageInstaller(@"..\BasicFunctionalityTests\PackageA");
17 var packageA_x64 = this.CreatePackageInstaller(@"..\BasicFunctionalityTests\PackageA_x64");
18 var bundleA = this.CreateBundleInstaller(@"..\BasicFunctionalityTests\BundleA");
19 var bundleB_x64 = this.CreateBundleInstaller(@"..\BasicFunctionalityTests\BundleB_x64");
20 var multipleBundlePackagesBundle = this.CreateBundleInstaller(@"MultipleBundlePackagesBundle");
21
22 var packageA32SourceCodeFilePath = packageA.GetInstalledFilePath("Package.wxs");
23 var packageA64SourceCodeFilePath = packageA_x64.GetInstalledFilePath("Package.wxs");
24
25 // Source file should *not* be installed
26 Assert.False(File.Exists(packageA32SourceCodeFilePath), $"PackageA payload should not be there on test start: {packageA32SourceCodeFilePath}");
27 Assert.False(File.Exists(packageA64SourceCodeFilePath), $"PackageA_x64 payload should not be there on test start: {packageA64SourceCodeFilePath}");
28
29 multipleBundlePackagesBundle.Install();
30 multipleBundlePackagesBundle.VerifyRegisteredAndInPackageCache();
31
32 bundleA.VerifyRegisteredAndInPackageCache();
33 bundleB_x64.VerifyRegisteredAndInPackageCache();
34
35 // Source file should be installed
36 Assert.True(File.Exists(packageA32SourceCodeFilePath), $"Should have found PackageA payload installed at: {packageA32SourceCodeFilePath}");
37 Assert.True(File.Exists(packageA64SourceCodeFilePath), $"Should have found PackageA_x64 payload installed at: {packageA64SourceCodeFilePath}");
38
39 multipleBundlePackagesBundle.Uninstall();
40 multipleBundlePackagesBundle.VerifyUnregisteredAndRemovedFromPackageCache();
41
42 bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
43 bundleB_x64.VerifyUnregisteredAndRemovedFromPackageCache();
44
45 // Source file should *not* be installed
46 Assert.False(File.Exists(packageA32SourceCodeFilePath), $"PackageA payload should have been removed by uninstall from: {packageA32SourceCodeFilePath}");
47 Assert.False(File.Exists(packageA64SourceCodeFilePath), $"PackageA_x64 payload should have been removed by uninstall from: {packageA64SourceCodeFilePath}");
48
49 }
50 }
51}
diff --git a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index a73992da..edcb0abf 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -178,6 +178,13 @@ namespace WixToolset.Core.Burn
178 { 178 {
179 switch (facade.PackageSymbol.Type) 179 switch (facade.PackageSymbol.Type)
180 { 180 {
181 case WixBundlePackageType.Bundle:
182 {
183 var command = new ProcessBundlePackageCommand(this.ServiceProvider, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder);
184 command.Execute();
185 }
186 break;
187
181 case WixBundlePackageType.Exe: 188 case WixBundlePackageType.Exe:
182 { 189 {
183 var command = new ProcessExePackageCommand(facade, payloadSymbols); 190 var command = new ProcessExePackageCommand(facade, payloadSymbols);
diff --git a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
index a76f84ec..6b37580e 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
@@ -65,6 +65,8 @@ namespace WixToolset.Core.Burn.Bind
65 case SymbolDefinitionType.WixBundle: 65 case SymbolDefinitionType.WixBundle:
66 case SymbolDefinitionType.WixBundleContainer: 66 case SymbolDefinitionType.WixBundleContainer:
67 case SymbolDefinitionType.WixBundleCustomDataAttribute: 67 case SymbolDefinitionType.WixBundleCustomDataAttribute:
68 case SymbolDefinitionType.WixBundleBundlePackage:
69 case SymbolDefinitionType.WixBundleBundlePackagePayload:
68 case SymbolDefinitionType.WixBundleExePackage: 70 case SymbolDefinitionType.WixBundleExePackage:
69 case SymbolDefinitionType.WixBundleExePackagePayload: 71 case SymbolDefinitionType.WixBundleExePackagePayload:
70 case SymbolDefinitionType.WixBundleExtension: 72 case SymbolDefinitionType.WixBundleExtension:
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
index 08183f32..7b34f4ae 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
@@ -37,6 +37,7 @@ namespace WixToolset.Core.Burn.Bundles
37 37
38 protected const uint IMAGE_NT_HEADER_SIZE = 24; // signature DWORD (4) + IMAGE_FILE_HEADER (20) 38 protected const uint IMAGE_NT_HEADER_SIZE = 24; // signature DWORD (4) + IMAGE_FILE_HEADER (20)
39 protected const uint IMAGE_NT_HEADER_OFFSET_SIGNATURE = 0; 39 protected const uint IMAGE_NT_HEADER_OFFSET_SIGNATURE = 0;
40 protected const uint IMAGE_NT_HEADER_OFFSET_MACHINE = 4;
40 protected const uint IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS = 6; 41 protected const uint IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS = 6;
41 protected const uint IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER = 20; 42 protected const uint IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER = 20;
42 43
@@ -57,6 +58,12 @@ namespace WixToolset.Core.Burn.Bundles
57 protected const uint IMAGE_NT_SIGNATURE = 0x00004550; 58 protected const uint IMAGE_NT_SIGNATURE = 0x00004550;
58 protected const ulong IMAGE_SECTION_WIXBURN_NAME = 0x6E7275627869772E; // ".wixburn", as a qword. 59 protected const ulong IMAGE_SECTION_WIXBURN_NAME = 0x6E7275627869772E; // ".wixburn", as a qword.
59 60
61 public const ushort IMAGE_FILE_MACHINE_AMD64 = 0x8664;
62 public const ushort IMAGE_FILE_MACHINE_ARM = 0x1C0;
63 public const ushort IMAGE_FILE_MACHINE_ARM64 = 0xAA64;
64 public const ushort IMAGE_FILE_MACHINE_ARMNT = 0x1C4;
65 public const ushort IMAGE_FILE_MACHINE_I386 = 0x14C;
66
60 // The ".wixburn" section contains: 67 // The ".wixburn" section contains:
61 // 0- 3: magic number 68 // 0- 3: magic number
62 // 4- 7: version 69 // 4- 7: version
@@ -119,10 +126,13 @@ namespace WixToolset.Core.Burn.Bundles
119 this.AttachedContainers = new List<ContainerSlot>(); 126 this.AttachedContainers = new List<ContainerSlot>();
120 } 127 }
121 128
129 public bool Invalid { get; protected set; }
130 public ushort MachineType { get; private set; }
122 public uint Checksum { get; protected set; } 131 public uint Checksum { get; protected set; }
123 public uint SignatureOffset { get; protected set; } 132 public uint SignatureOffset { get; protected set; }
124 public uint SignatureSize { get; protected set; } 133 public uint SignatureSize { get; protected set; }
125 public uint Version { get; protected set; } 134 public uint Version { get; protected set; }
135 public Guid BundleId { get; protected set; }
126 public uint StubSize { get; protected set; } 136 public uint StubSize { get; protected set; }
127 public uint OriginalChecksum { get; protected set; } 137 public uint OriginalChecksum { get; protected set; }
128 public uint OriginalSignatureOffset { get; protected set; } 138 public uint OriginalSignatureOffset { get; protected set; }
@@ -170,8 +180,12 @@ namespace WixToolset.Core.Burn.Bundles
170 /// Initialize the common information about a Burn engine. 180 /// Initialize the common information about a Burn engine.
171 /// </summary> 181 /// </summary>
172 /// <param name="reader">Binary reader open against a Burn engine.</param> 182 /// <param name="reader">Binary reader open against a Burn engine.</param>
173 /// <returns>True if initialized.</returns> 183 protected void Initialize(BinaryReader reader)
174 protected bool Initialize(BinaryReader reader) 184 {
185 this.Invalid = !this.TryInitialize(reader);
186 }
187
188 private bool TryInitialize(BinaryReader reader)
175 { 189 {
176 if (!this.GetWixburnSectionInfo(reader)) 190 if (!this.GetWixburnSectionInfo(reader))
177 { 191 {
@@ -202,6 +216,7 @@ namespace WixToolset.Core.Burn.Bundles
202 return false; 216 return false;
203 } 217 }
204 218
219 this.BundleId = BurnCommon.ReadGuid(bytes, BURN_SECTION_OFFSET_BUNDLEGUID);
205 this.StubSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_STUBSIZE); 220 this.StubSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_STUBSIZE);
206 this.OriginalChecksum = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALCHECKSUM); 221 this.OriginalChecksum = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALCHECKSUM);
207 this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET); 222 this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET);
@@ -321,6 +336,8 @@ namespace WixToolset.Core.Burn.Bundles
321 return false; 336 return false;
322 } 337 }
323 338
339 this.MachineType = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_MACHINE);
340
324 var sizeOptionalHeader = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER); 341 var sizeOptionalHeader = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER);
325 342
326 this.sections = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS); 343 this.sections = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS);
@@ -362,6 +379,17 @@ namespace WixToolset.Core.Burn.Bundles
362 return true; 379 return true;
363 } 380 }
364 381
382 internal static Guid ReadGuid(byte[] bytes, uint offset)
383 {
384 var guidBytes = new byte[16];
385 for (var i = 0; i < 16; ++i)
386 {
387 guidBytes[i] = bytes[offset + i];
388 }
389
390 return new Guid(guidBytes);
391 }
392
365 /// <summary> 393 /// <summary>
366 /// Reads a UInt16 value in little-endian format from an offset in an array of bytes. 394 /// Reads a UInt16 value in little-endian format from an offset in an array of bytes.
367 /// </summary> 395 /// </summary>
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
index b30ac48a..ac6a2b8f 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
@@ -25,7 +25,6 @@ namespace WixToolset.Core.Burn.Bundles
25 { 25 {
26 private bool disposed; 26 private bool disposed;
27 27
28 private bool invalidBundle;
29 private BinaryReader binaryReader; 28 private BinaryReader binaryReader;
30 private readonly List<DictionaryEntry> attachedContainerPayloadNames; 29 private readonly List<DictionaryEntry> attachedContainerPayloadNames;
31 30
@@ -53,13 +52,12 @@ namespace WixToolset.Core.Burn.Bundles
53 /// <returns>Burn reader.</returns> 52 /// <returns>Burn reader.</returns>
54 public static BurnReader Open(IMessaging messaging, string fileExe) 53 public static BurnReader Open(IMessaging messaging, string fileExe)
55 { 54 {
56 var reader = new BurnReader(messaging, fileExe); 55 var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
57 56 var reader = new BurnReader(messaging, fileExe)
58 reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
59 if (!reader.Initialize(reader.binaryReader))
60 { 57 {
61 reader.invalidBundle = true; 58 binaryReader = binaryReader,
62 } 59 };
60 reader.Initialize(reader.binaryReader);
63 61
64 return reader; 62 return reader;
65 } 63 }
@@ -78,7 +76,7 @@ namespace WixToolset.Core.Burn.Bundles
78 return false; 76 return false;
79 } 77 }
80 78
81 if (this.invalidBundle) 79 if (this.Invalid)
82 { 80 {
83 return false; 81 return false;
84 } 82 }
@@ -156,7 +154,7 @@ namespace WixToolset.Core.Burn.Bundles
156 return false; 154 return false;
157 } 155 }
158 156
159 if (this.invalidBundle) 157 if (this.Invalid)
160 { 158 {
161 return false; 159 return false;
162 } 160 }
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
index 5800f5c0..1f0a032e 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
@@ -23,7 +23,6 @@ namespace WixToolset.Core.Burn.Bundles
23 internal class BurnWriter : BurnCommon 23 internal class BurnWriter : BurnCommon
24 { 24 {
25 private bool disposed; 25 private bool disposed;
26 private bool invalidBundle;
27 private BinaryWriter binaryWriter; 26 private BinaryWriter binaryWriter;
28 27
29 /// <summary> 28 /// <summary>
@@ -48,13 +47,10 @@ namespace WixToolset.Core.Burn.Bundles
48 47
49 using (var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))) 48 using (var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)))
50 { 49 {
51 if (!writer.Initialize(binaryReader)) 50 writer.Initialize(binaryReader);
52 {
53 writer.invalidBundle = true;
54 }
55 } 51 }
56 52
57 if (!writer.invalidBundle) 53 if (!writer.Invalid)
58 { 54 {
59 writer.binaryWriter = new BinaryWriter(File.Open(fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete)); 55 writer.binaryWriter = new BinaryWriter(File.Open(fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete));
60 } 56 }
@@ -70,7 +66,7 @@ namespace WixToolset.Core.Burn.Bundles
70 /// <returns></returns> 66 /// <returns></returns>
71 public bool InitializeBundleSectionData(long stubSize, string bundleId) 67 public bool InitializeBundleSectionData(long stubSize, string bundleId)
72 { 68 {
73 if (this.invalidBundle) 69 if (this.Invalid)
74 { 70 {
75 return false; 71 return false;
76 } 72 }
@@ -84,6 +80,7 @@ namespace WixToolset.Core.Burn.Bundles
84 this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin); 80 this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin);
85 this.binaryWriter.Write(bundleGuid.ToByteArray()); 81 this.binaryWriter.Write(bundleGuid.ToByteArray());
86 82
83 this.BundleId = bundleGuid;
87 this.StubSize = (uint)stubSize; 84 this.StubSize = (uint)stubSize;
88 85
89 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_STUBSIZE, this.StubSize); 86 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_STUBSIZE, this.StubSize);
@@ -191,7 +188,7 @@ namespace WixToolset.Core.Burn.Bundles
191 188
192 public void RememberThenResetSignature() 189 public void RememberThenResetSignature()
193 { 190 {
194 if (this.invalidBundle) 191 if (this.Invalid)
195 { 192 {
196 return; 193 return;
197 } 194 }
@@ -241,14 +238,14 @@ namespace WixToolset.Core.Burn.Bundles
241 /// <returns>true if the container data is successfully appended; false otherwise</returns> 238 /// <returns>true if the container data is successfully appended; false otherwise</returns>
242 private bool AppendContainer(Stream containerStream, uint containerSize, uint burnSectionOffsetSize, uint burnSectionCount) 239 private bool AppendContainer(Stream containerStream, uint containerSize, uint burnSectionOffsetSize, uint burnSectionCount)
243 { 240 {
244 if (this.invalidBundle) 241 if (this.Invalid)
245 { 242 {
246 return false; 243 return false;
247 } 244 }
248 245
249 if (burnSectionOffsetSize > (this.wixburnRawDataSize - sizeof(uint))) 246 if (burnSectionOffsetSize > (this.wixburnRawDataSize - sizeof(uint)))
250 { 247 {
251 this.invalidBundle = true; 248 this.Invalid = true;
252 this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(this.wixburnMaxContainers)); 249 this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(this.wixburnMaxContainers));
253 return false; 250 return false;
254 } 251 }
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
index 6a684fe0..84923998 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
@@ -381,7 +381,16 @@ namespace WixToolset.Core.Burn.Bundles
381 writer.WriteAttributeString("InstallCondition", package.PackageSymbol.InstallCondition); 381 writer.WriteAttributeString("InstallCondition", package.PackageSymbol.InstallCondition);
382 } 382 }
383 383
384 if (package.SpecificPackageSymbol is WixBundleExePackageSymbol exePackage) // EXE 384 if (package.SpecificPackageSymbol is WixBundleBundlePackageSymbol bundlePackage) // BUNDLE
385 {
386 writer.WriteAttributeString("BundleId", bundlePackage.BundleId);
387 writer.WriteAttributeString("InstallArguments", bundlePackage.InstallCommand);
388 writer.WriteAttributeString("UninstallArguments", bundlePackage.UninstallCommand);
389 writer.WriteAttributeString("RepairArguments", bundlePackage.RepairCommand);
390 writer.WriteAttributeString("SupportsBurnProtocol", bundlePackage.SupportsBurnProtocol ? "yes" : "no");
391 writer.WriteAttributeString("Win64", bundlePackage.Win64 ? "yes" : "no");
392 }
393 else if (package.SpecificPackageSymbol is WixBundleExePackageSymbol exePackage) // EXE
385 { 394 {
386 writer.WriteAttributeString("DetectCondition", exePackage.DetectCondition); 395 writer.WriteAttributeString("DetectCondition", exePackage.DetectCondition);
387 writer.WriteAttributeString("InstallArguments", exePackage.InstallCommand); 396 writer.WriteAttributeString("InstallArguments", exePackage.InstallCommand);
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
index 2bf22a3d..19403631 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
@@ -28,10 +28,12 @@ namespace WixToolset.Core.Burn.Bundles
28 public void Execute() 28 public void Execute()
29 { 29 {
30 var wixGroupPackagesGroupedById = this.Section.Symbols.OfType<WixGroupSymbol>().Where(g => g.ParentType == ComplexReferenceParentType.Package).ToLookup(g => g.ParentId); 30 var wixGroupPackagesGroupedById = this.Section.Symbols.OfType<WixGroupSymbol>().Where(g => g.ParentType == ComplexReferenceParentType.Package).ToLookup(g => g.ParentId);
31 var bundlePackages = this.Section.Symbols.OfType<WixBundleBundlePackageSymbol>().ToDictionary(t => t.Id.Id);
31 var exePackages = this.Section.Symbols.OfType<WixBundleExePackageSymbol>().ToDictionary(t => t.Id.Id); 32 var exePackages = this.Section.Symbols.OfType<WixBundleExePackageSymbol>().ToDictionary(t => t.Id.Id);
32 var msiPackages = this.Section.Symbols.OfType<WixBundleMsiPackageSymbol>().ToDictionary(t => t.Id.Id); 33 var msiPackages = this.Section.Symbols.OfType<WixBundleMsiPackageSymbol>().ToDictionary(t => t.Id.Id);
33 var mspPackages = this.Section.Symbols.OfType<WixBundleMspPackageSymbol>().ToDictionary(t => t.Id.Id); 34 var mspPackages = this.Section.Symbols.OfType<WixBundleMspPackageSymbol>().ToDictionary(t => t.Id.Id);
34 var msuPackages = this.Section.Symbols.OfType<WixBundleMsuPackageSymbol>().ToDictionary(t => t.Id.Id); 35 var msuPackages = this.Section.Symbols.OfType<WixBundleMsuPackageSymbol>().ToDictionary(t => t.Id.Id);
36 var bundlePackagePayloads = this.Section.Symbols.OfType<WixBundleBundlePackagePayloadSymbol>().ToDictionary(t => t.Id.Id);
35 var exePackagePayloads = this.Section.Symbols.OfType<WixBundleExePackagePayloadSymbol>().ToDictionary(t => t.Id.Id); 37 var exePackagePayloads = this.Section.Symbols.OfType<WixBundleExePackagePayloadSymbol>().ToDictionary(t => t.Id.Id);
36 var msiPackagePayloads = this.Section.Symbols.OfType<WixBundleMsiPackagePayloadSymbol>().ToDictionary(t => t.Id.Id); 38 var msiPackagePayloads = this.Section.Symbols.OfType<WixBundleMsiPackagePayloadSymbol>().ToDictionary(t => t.Id.Id);
37 var mspPackagePayloads = this.Section.Symbols.OfType<WixBundleMspPackagePayloadSymbol>().ToDictionary(t => t.Id.Id); 39 var mspPackagePayloads = this.Section.Symbols.OfType<WixBundleMspPackagePayloadSymbol>().ToDictionary(t => t.Id.Id);
@@ -49,7 +51,19 @@ namespace WixToolset.Core.Burn.Bundles
49 if (wixGroup.ChildType == ComplexReferenceChildType.PackagePayload) 51 if (wixGroup.ChildType == ComplexReferenceChildType.PackagePayload)
50 { 52 {
51 IntermediateSymbol tempPackagePayload = null; 53 IntermediateSymbol tempPackagePayload = null;
52 if (exePackagePayloads.TryGetValue(wixGroup.ChildId, out var exePackagePayload)) 54 if (bundlePackagePayloads.TryGetValue(wixGroup.ChildId, out var bundlePackagePayload))
55 {
56 if (package.Type == WixBundlePackageType.Bundle)
57 {
58 tempPackagePayload = bundlePackagePayload;
59 }
60 else
61 {
62 this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported(bundlePackagePayload.SourceLineNumbers, "Bundle"));
63 this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported2(package.SourceLineNumbers));
64 }
65 }
66 else if (exePackagePayloads.TryGetValue(wixGroup.ChildId, out var exePackagePayload))
53 { 67 {
54 if (package.Type == WixBundlePackageType.Exe) 68 if (package.Type == WixBundlePackageType.Exe)
55 { 69 {
@@ -129,6 +143,17 @@ namespace WixToolset.Core.Burn.Bundles
129 143
130 switch (package.Type) 144 switch (package.Type)
131 { 145 {
146 case WixBundlePackageType.Bundle:
147 if (bundlePackages.TryGetValue(id, out var bundlePackage))
148 {
149 facades.Add(id, new PackageFacade(package, bundlePackage));
150 }
151 else
152 {
153 this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleBundlePackage", id));
154 }
155 break;
156
132 case WixBundlePackageType.Exe: 157 case WixBundlePackageType.Exe:
133 if (exePackages.TryGetValue(id, out var exePackage)) 158 if (exePackages.TryGetValue(id, out var exePackage))
134 { 159 {
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs
index 2db5841b..8a2f1c64 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs
@@ -55,7 +55,11 @@ namespace WixToolset.Core.Burn.Bundles
55 55
56 foreach (var packageFacade in this.PackageFacadesById.Values) 56 foreach (var packageFacade in this.PackageFacadesById.Values)
57 { 57 {
58 if (packageFacade.SpecificPackageSymbol is WixBundleExePackageSymbol wixBundleExePackageSymbol) 58 if (packageFacade.SpecificPackageSymbol is WixBundleBundlePackageSymbol wixBundleBundlePackageSymbol)
59 {
60 this.ValidateBundlePackage(wixBundleBundlePackageSymbol, packageFacade.PackageSymbol);
61 }
62 else if (packageFacade.SpecificPackageSymbol is WixBundleExePackageSymbol wixBundleExePackageSymbol)
59 { 63 {
60 this.ValidateExePackage(wixBundleExePackageSymbol, packageFacade.PackageSymbol); 64 this.ValidateExePackage(wixBundleExePackageSymbol, packageFacade.PackageSymbol);
61 } 65 }
@@ -90,6 +94,11 @@ namespace WixToolset.Core.Burn.Bundles
90 } 94 }
91 } 95 }
92 96
97 private void ValidateBundlePackage(WixBundleBundlePackageSymbol symbol, WixBundlePackageSymbol packageSymbol)
98 {
99 this.ValidateChainPackage(packageSymbol, "BundlePackage");
100 }
101
93 private void ValidateExePackage(WixBundleExePackageSymbol symbol, WixBundlePackageSymbol packageSymbol) 102 private void ValidateExePackage(WixBundleExePackageSymbol symbol, WixBundlePackageSymbol packageSymbol)
94 { 103 {
95 this.ValidateChainPackage(packageSymbol, "ExePackage"); 104 this.ValidateChainPackage(packageSymbol, "ExePackage");
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
new file mode 100644
index 00000000..fdb07a56
--- /dev/null
+++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
@@ -0,0 +1,152 @@
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
3namespace WixToolset.Core.Burn.Bundles
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using System.Xml;
10 using WixToolset.Data;
11 using WixToolset.Data.Symbols;
12 using WixToolset.Extensibility.Data;
13 using WixToolset.Extensibility.Services;
14
15 /// <summary>
16 /// Initializes package state from the Bundle contents.
17 /// </summary>
18 internal class ProcessBundlePackageCommand
19 {
20 public ProcessBundlePackageCommand(IServiceProvider serviceProvider, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadSymbol> packagePayloads, string intermediateFolder)
21 {
22 this.Messaging = serviceProvider.GetService<IMessaging>();
23 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
24 this.PackagePayloads = packagePayloads;
25 this.Section = section;
26 this.Facade = facade;
27 this.IntermediateFolder = intermediateFolder;
28 }
29
30 private IMessaging Messaging { get; }
31
32 private IBackendHelper BackendHelper { get; }
33
34 private Dictionary<string, WixBundlePayloadSymbol> PackagePayloads { get; }
35
36 private PackageFacade Facade { get; }
37
38 private IntermediateSection Section { get; }
39
40 private string IntermediateFolder { get; }
41
42 public List<ITrackedFile> TrackedFiles { get; } = new List<ITrackedFile>();
43
44 /// <summary>
45 /// Processes the Bundle packages to add properties and payloads from the Bundle packages.
46 /// </summary>
47 public void Execute()
48 {
49 var bundlePackage = (WixBundleBundlePackageSymbol)this.Facade.SpecificPackageSymbol;
50 var packagePayload = this.PackagePayloads[this.Facade.PackageSymbol.PayloadRef];
51 var sourcePath = packagePayload.SourceFile.Path;
52
53 using (var burnReader = BurnReader.Open(this.Messaging, sourcePath))
54 {
55 if (burnReader.Invalid)
56 {
57 return;
58 }
59
60 var baFolderPath = Path.Combine(this.IntermediateFolder, burnReader.BundleId.ToString());
61
62 if (!burnReader.ExtractUXContainer(baFolderPath, baFolderPath))
63 {
64 return;
65 }
66
67 foreach (var filePath in Directory.EnumerateFiles(baFolderPath, "*.*", SearchOption.AllDirectories))
68 {
69 this.TrackedFiles.Add(this.BackendHelper.TrackFile(filePath, TrackedFileType.Temporary, packagePayload.SourceLineNumbers));
70 }
71
72 switch (burnReader.MachineType)
73 {
74 case BurnCommon.IMAGE_FILE_MACHINE_ARM:
75 case BurnCommon.IMAGE_FILE_MACHINE_ARMNT:
76 case BurnCommon.IMAGE_FILE_MACHINE_I386:
77 break;
78 case BurnCommon.IMAGE_FILE_MACHINE_AMD64:
79 case BurnCommon.IMAGE_FILE_MACHINE_ARM64:
80 bundlePackage.Win64 = true;
81 break;
82 default:
83 Debug.Assert(false, "Unknown machine type");
84 break;
85 }
86
87 bundlePackage.BundleId = burnReader.BundleId.ToString("B").ToUpperInvariant();
88
89 // Assume that the .wixburn section version will change when the Burn protocol changes.
90 // This should be a safe assumption since we will need to add the protocol version to the section to support this harvesting.
91 bundlePackage.SupportsBurnProtocol = burnReader.Version == 2;
92
93 var document = new XmlDocument();
94 document.Load(Path.Combine(baFolderPath, "manifest.xml"));
95 var namespaceManager = new XmlNamespaceManager(document.NameTable);
96 namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace); // TODO: support v3 bundles
97 var registrationElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration", namespaceManager) as XmlElement;
98 var arpElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration/burn:Arp", namespaceManager) as XmlElement;
99
100 var perMachine = registrationElement.GetAttribute("PerMachine") == "yes";
101 this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
102
103 var version = registrationElement.GetAttribute("Version");
104 packagePayload.Version = version;
105
106 if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId))
107 {
108 this.Facade.PackageSymbol.CacheId = String.Format("{0}v{1}", bundlePackage.BundleId, version);
109 }
110
111 var providerKey = registrationElement.GetAttribute("ProviderKey");
112 var depId = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", bundlePackage.Id.Id, providerKey));
113 this.Section.AddSymbol(new WixDependencyProviderSymbol(packagePayload.SourceLineNumbers, depId)
114 {
115 ParentRef = bundlePackage.Id.Id,
116 ProviderKey = providerKey,
117 Version = version,
118 Attributes = WixDependencyProviderAttributes.ProvidesAttributesImported,
119 });
120
121 if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName))
122 {
123 this.Facade.PackageSymbol.DisplayName = arpElement.GetAttribute("DisplayName");
124 }
125
126 this.ProcessPackages(document, namespaceManager);
127
128 // TODO: Add payloads?
129 }
130 }
131
132 private void ProcessPackages(XmlDocument document, XmlNamespaceManager namespaceManager)
133 {
134 long packageInstallSize = 0;
135
136 foreach (XmlElement packageElement in document.SelectNodes("/burn:BurnManifest/burn:Chain/*", namespaceManager))
137 {
138 if (!packageElement.Name.EndsWith("Package"))
139 {
140 continue;
141 }
142
143 if (Int64.TryParse(packageElement.GetAttribute("InstallSize"), out var installSize))
144 {
145 packageInstallSize += installSize;
146 }
147 }
148
149 this.Facade.PackageSymbol.InstallSize = packageInstallSize;
150 }
151 }
152}
diff --git a/src/wix/WixToolset.Core/Compiler_Bundle.cs b/src/wix/WixToolset.Core/Compiler_Bundle.cs
index f671e90b..835fa2c2 100644
--- a/src/wix/WixToolset.Core/Compiler_Bundle.cs
+++ b/src/wix/WixToolset.Core/Compiler_Bundle.cs
@@ -1517,6 +1517,9 @@ namespace WixToolset.Core
1517 WixBundlePackageType? packageType = null; 1517 WixBundlePackageType? packageType = null;
1518 switch (child.Name.LocalName) 1518 switch (child.Name.LocalName)
1519 { 1519 {
1520 case "BundlePackagePayload":
1521 packageType = WixBundlePackageType.Bundle;
1522 break;
1520 case "ExePackagePayload": 1523 case "ExePackagePayload":
1521 packageType = WixBundlePackageType.Exe; 1524 packageType = WixBundlePackageType.Exe;
1522 break; 1525 break;
@@ -1751,6 +1754,10 @@ namespace WixToolset.Core
1751 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId); 1754 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1752 previousType = ComplexReferenceChildType.Package; 1755 previousType = ComplexReferenceChildType.Package;
1753 break; 1756 break;
1757 case "BundlePackage":
1758 previousId = this.ParseBundlePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1759 previousType = ComplexReferenceChildType.Package;
1760 break;
1754 case "RollbackBoundary": 1761 case "RollbackBoundary":
1755 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId); 1762 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1756 previousType = ComplexReferenceChildType.Package; 1763 previousType = ComplexReferenceChildType.Package;
@@ -1842,6 +1849,20 @@ namespace WixToolset.Core
1842 } 1849 }
1843 1850
1844 /// <summary> 1851 /// <summary>
1852 /// Parse BundlePackage element
1853 /// </summary>
1854 /// <param name="node">Element to parse</param>
1855 /// <param name="parentType">Type of parent group, if known.</param>
1856 /// <param name="parentId">Identifier of parent group, if known.</param>
1857 /// <param name="previousType">Type of previous item, if known.</param>
1858 /// <param name="previousId">Identifier of previous item, if known</param>
1859 /// <returns>Identifier for package element.</returns>
1860 private string ParseBundlePackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1861 {
1862 return this.ParseChainPackage(node, WixBundlePackageType.Bundle, parentType, parentId, previousType, previousId);
1863 }
1864
1865 /// <summary>
1845 /// Parse RollbackBoundary element 1866 /// Parse RollbackBoundary element
1846 /// </summary> 1867 /// </summary>
1847 /// <param name="node">Element to parse</param> 1868 /// <param name="node">Element to parse</param>
@@ -2096,15 +2117,15 @@ namespace WixToolset.Core
2096 break; 2117 break;
2097 case "InstallArguments": 2118 case "InstallArguments":
2098 installArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 2119 installArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2099 allowed = (packageType == WixBundlePackageType.Exe); 2120 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2100 break; 2121 break;
2101 case "RepairArguments": 2122 case "RepairArguments":
2102 repairArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); 2123 repairArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2103 allowed = (packageType == WixBundlePackageType.Exe); 2124 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2104 break; 2125 break;
2105 case "UninstallArguments": 2126 case "UninstallArguments":
2106 uninstallArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); 2127 uninstallArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2107 allowed = (packageType == WixBundlePackageType.Exe); 2128 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2108 break; 2129 break;
2109 case "PerMachine": 2130 case "PerMachine":
2110 perMachine = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); 2131 perMachine = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
@@ -2339,19 +2360,20 @@ namespace WixToolset.Core
2339 this.ParseProvidesElement(child, packageType, id.Id, out _); 2360 this.ParseProvidesElement(child, packageType, id.Id, out _);
2340 break; 2361 break;
2341 case "ExitCode": 2362 case "ExitCode":
2342 allowed = (packageType == WixBundlePackageType.Exe); 2363 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2343 if (allowed) 2364 if (allowed)
2344 { 2365 {
2345 this.ParseExitCodeElement(child, id.Id); 2366 this.ParseExitCodeElement(child, id.Id);
2346 } 2367 }
2347 break; 2368 break;
2348 case "CommandLine": 2369 case "CommandLine":
2349 allowed = (packageType == WixBundlePackageType.Exe); 2370 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2350 if (allowed) 2371 if (allowed)
2351 { 2372 {
2352 this.ParseCommandLineElement(child, id.Id); 2373 this.ParseCommandLineElement(child, id.Id);
2353 } 2374 }
2354 break; 2375 break;
2376 case "BundlePackagePayload":
2355 case "ExePackagePayload": 2377 case "ExePackagePayload":
2356 case "MsiPackagePayload": 2378 case "MsiPackagePayload":
2357 case "MspPackagePayload": 2379 case "MspPackagePayload":
@@ -2422,6 +2444,18 @@ namespace WixToolset.Core
2422 2444
2423 switch (packageType) 2445 switch (packageType)
2424 { 2446 {
2447 case WixBundlePackageType.Bundle:
2448 WixBundleBundlePackageAttributes bundleAttributes = 0;
2449
2450 this.Core.AddSymbol(new WixBundleBundlePackageSymbol(sourceLineNumbers, id)
2451 {
2452 Attributes = bundleAttributes,
2453 InstallCommand = installArguments,
2454 RepairCommand = repairArguments,
2455 UninstallCommand = uninstallArguments,
2456 });
2457 break;
2458
2425 case WixBundlePackageType.Exe: 2459 case WixBundlePackageType.Exe:
2426 WixBundleExePackageAttributes exeAttributes = 0; 2460 WixBundleExePackageAttributes exeAttributes = 0;
2427 exeAttributes |= (YesNoType.Yes == bundle) ? WixBundleExePackageAttributes.Bundle : 0; 2461 exeAttributes |= (YesNoType.Yes == bundle) ? WixBundleExePackageAttributes.Bundle : 0;
@@ -2478,6 +2512,10 @@ namespace WixToolset.Core
2478 { 2512 {
2479 switch (packageType) 2513 switch (packageType)
2480 { 2514 {
2515 case WixBundlePackageType.Bundle:
2516 this.Core.AddSymbol(new WixBundleBundlePackagePayloadSymbol(sourceLineNumbers, payloadId));
2517 break;
2518
2481 case WixBundlePackageType.Exe: 2519 case WixBundlePackageType.Exe:
2482 this.Core.AddSymbol(new WixBundleExePackagePayloadSymbol(sourceLineNumbers, payloadId)); 2520 this.Core.AddSymbol(new WixBundleExePackagePayloadSymbol(sourceLineNumbers, payloadId));
2483 break; 2521 break;
@@ -2741,6 +2779,10 @@ namespace WixToolset.Core
2741 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); 2779 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2742 previousType = ComplexReferenceChildType.Package; 2780 previousType = ComplexReferenceChildType.Package;
2743 break; 2781 break;
2782 case "BundlePackage":
2783 previousId = this.ParseBundlePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2784 previousType = ComplexReferenceChildType.Package;
2785 break;
2744 case "RollbackBoundary": 2786 case "RollbackBoundary":
2745 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); 2787 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2746 previousType = ComplexReferenceChildType.Package; 2788 previousType = ComplexReferenceChildType.Package;
diff --git a/src/wix/WixToolset.Core/Compiler_Dependency.cs b/src/wix/WixToolset.Core/Compiler_Dependency.cs
index 7c863883..664ac1be 100644
--- a/src/wix/WixToolset.Core/Compiler_Dependency.cs
+++ b/src/wix/WixToolset.Core/Compiler_Dependency.cs
@@ -129,7 +129,7 @@ namespace WixToolset.Core
129 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, "ProductCode"); 129 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, "ProductCode");
130 key = "!(bind.property.ProductCode)"; 130 key = "!(bind.property.ProductCode)";
131 } 131 }
132 else if (WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msu == packageType) 132 else if (WixBundlePackageType.Bundle == packageType || WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msu == packageType)
133 { 133 {
134 // Must specify the provider key when authored for a package. 134 // Must specify the provider key when authored for a package.
135 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); 135 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
new file mode 100644
index 00000000..268920a6
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
@@ -0,0 +1,114 @@
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
3namespace WixToolsetTest.CoreIntegration
4{
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Linq;
8 using System.Xml;
9 using WixBuildTools.TestSupport;
10 using WixToolset.Core.TestPackage;
11 using WixToolset.Data;
12 using WixToolset.Data.Symbols;
13 using Xunit;
14
15 public class BundlePackageFixture
16 {
17 [Fact]
18 public void CanBuildBundleWithBundlePackage()
19 {
20 var folder = TestData.Get(@"TestData");
21
22 using (var fs = new DisposableFileSystem())
23 {
24 var baseFolder = fs.GetFolder();
25 var chainIntermediateFolder = Path.Combine(baseFolder, "obj", "Chain");
26 var parentIntermediateFolder = Path.Combine(baseFolder, "obj", "Parent");
27 var binFolder = Path.Combine(baseFolder, "bin");
28 var chainBundlePath = Path.Combine(binFolder, "chain.exe");
29 var chainPdbPath = Path.Combine(binFolder, "chain.wixpdb");
30 var parentBundlePath = Path.Combine(binFolder, "parent.exe");
31 var parentPdbPath = Path.Combine(binFolder, "parent.wixpdb");
32 var baFolderPath = Path.Combine(baseFolder, "ba");
33 var extractFolderPath = Path.Combine(baseFolder, "extract");
34
35 var result = WixRunner.Execute(new[]
36 {
37 "build",
38 Path.Combine(folder, "Dependency", "CustomProviderKeyBundle.wxs"),
39 Path.Combine(folder, "BundleWithPackageGroupRef", "MinimalPackageGroup.wxs"),
40 "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
41 "-intermediateFolder", chainIntermediateFolder,
42 "-o", chainBundlePath,
43 });
44
45 result.AssertSuccess();
46
47 Assert.True(File.Exists(chainBundlePath));
48
49 string chainBundleId;
50 using (var wixOutput = WixOutput.Read(chainPdbPath))
51 {
52
53 var intermediate = Intermediate.Load(wixOutput);
54 var section = intermediate.Sections.Single();
55
56 var bundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single();
57 chainBundleId = bundleSymbol.BundleId;
58 }
59
60 result = WixRunner.Execute(new[]
61 {
62 "build",
63 Path.Combine(folder, "BundlePackage", "BundlePackage.wxs"),
64 "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
65 "-bindpath", binFolder,
66 "-intermediateFolder", parentIntermediateFolder,
67 "-o", parentBundlePath,
68 });
69
70 result.AssertSuccess();
71
72 Assert.True(File.Exists(parentBundlePath));
73
74 string parentBundleId;
75 using (var wixOutput = WixOutput.Read(parentPdbPath))
76 {
77
78 var intermediate = Intermediate.Load(wixOutput);
79 var section = intermediate.Sections.Single();
80
81 var bundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single();
82 parentBundleId = bundleSymbol.BundleId;
83 }
84
85 var extractResult = BundleExtractor.ExtractBAContainer(null, parentBundlePath, baFolderPath, extractFolderPath);
86 extractResult.AssertSuccess();
87
88 var ignoreAttributesByElementName = new Dictionary<string, List<string>>
89 {
90 { "BundlePackage", new List<string> { "Size" } },
91 };
92 var bundlePackages = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Chain/burn:BundlePackage")
93 .Cast<XmlElement>()
94 .Select(e => e.GetTestXml(ignoreAttributesByElementName))
95 .ToArray();
96 WixAssert.CompareLineByLine(new string[]
97 {
98 $"<BundlePackage Id='chain.exe' Cache='keep' CacheId='{chainBundleId}v1.0.0.0' InstallSize='34' Size='*' PerMachine='yes' Permanent='no' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' BundleId='{chainBundleId}' InstallArguments='' UninstallArguments='' RepairArguments='' SupportsBurnProtocol='yes' Win64='no'><Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' /><PayloadRef Id='chain.exe' /></BundlePackage>",
99 }, bundlePackages);
100
101 var registrations = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration")
102 .Cast<XmlElement>()
103 .Select(e => e.GetTestXml())
104 .ToArray();
105 WixAssert.CompareLineByLine(new string[]
106 {
107 $"<Registration Id='{parentBundleId}' ExecutableName='parent.exe' PerMachine='yes' Tag='' Version='1.0.1.0' ProviderKey='{parentBundleId}'>" +
108 "<Arp Register='yes' DisplayName='BundlePackageBundle' DisplayVersion='1.0.1.0' Publisher='Example Corporation' />" +
109 "</Registration>"
110 }, registrations);
111 }
112 }
113 }
114}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs
new file mode 100644
index 00000000..0602a48b
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs
@@ -0,0 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Bundle Name="BundlePackageBundle" Version="1.0.1.0" Manufacturer="Example Corporation" UpgradeCode="{4BE34BEE-CA23-488E-96A0-B15878E3654B}">
3 <BootstrapperApplication>
4 <BootstrapperApplicationDll SourceFile="fakeba.dll" />
5 </BootstrapperApplication>
6 <Chain>
7 <BundlePackage SourceFile="chain.exe" />
8 </Chain>
9 </Bundle>
10</Wix>