@joebigelow / wix-1 / commits / 386a3578

Implement BundlePackage.

3693

Sean Hall committed Mar 30, 2022 at 17:08 UTC 386a3578413ba16b3c0615d47870ee44a0e461f6
40 files changed +1439 -98
src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+1
@@ -106,6 +106,7 @@ enum BOOTSTRAPPER_RELATION_TYPE
106 BOOTSTRAPPER_RELATION_DEPENDENT_ADDON,
107 BOOTSTRAPPER_RELATION_DEPENDENT_PATCH,
108 BOOTSTRAPPER_RELATION_UPDATE,
109 + BOOTSTRAPPER_RELATION_CHAIN_PACKAGE,
110 };
111
112 enum BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE
src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+5
@@ -1714,6 +1714,11 @@ namespace WixToolset.Mba.Core
1714 ///
1715 /// </summary>
1716 Update,
1717 +
1718 + /// <summary>
1719 + ///
1720 + /// </summary>
1721 + ChainPackage,
1722 }
1723
1724 /// <summary>
src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
+8
@@ -125,6 +125,8 @@ namespace WixToolset.Data
125 WixBundleCustomData,
126 WixBundleCustomDataAttribute,
127 WixBundleCustomDataCell,
128 + WixBundleBundlePackage,
129 + WixBundleBundlePackagePayload,
130 WixBundleExePackage,
131 WixBundleExePackagePayload,
132 WixBundleExtension,
@@ -553,6 +555,12 @@ namespace WixToolset.Data
555 case SymbolDefinitionType.WixBundle:
556 return SymbolDefinitions.WixBundle;
557
558 + case SymbolDefinitionType.WixBundleBundlePackage:
559 + return SymbolDefinitions.WixBundleBundlePackage;
560 +
561 + case SymbolDefinitionType.WixBundleBundlePackagePayload:
562 + return SymbolDefinitions.WixBundleBundlePackagePayload;
563 +
564 case SymbolDefinitionType.WixBundleContainer:
565 return SymbolDefinitions.WixBundleContainer;
566
src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs new
+36
@@ -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 +
3 +namespace 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 +
18 +namespace 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 +}
src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs new
+118
@@ -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 +
3 +namespace 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 +
23 +namespace 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 +}
src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs
+2 -1
@@ -66,6 +66,7 @@ namespace WixToolset.Data.Symbols
66 /// </summary>
67 public enum WixBundlePackageType
68 {
69 + Bundle,
70 Exe,
71 Msi,
72 Msp,
@@ -209,4 +210,4 @@ namespace WixToolset.Data.Symbols
210
211 public bool Permanent => (this.Attributes & WixBundlePackageAttributes.Permanent) == WixBundlePackageAttributes.Permanent;
212 }
212 -}
\ No newline at end of file
213 +}
src/burn/engine/apply.cpp
+102
@@ -216,6 +216,15 @@ static HRESULT DoRestoreRelatedBundleActions(
216 __in BURN_EXECUTE_CONTEXT* pContext,
217 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
218 );
219 +static 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 + );
228 static HRESULT ExecuteExePackage(
229 __in BURN_ENGINE_STATE* pEngineState,
230 __in BURN_EXECUTE_ACTION* pExecuteAction,
@@ -732,6 +741,9 @@ extern "C" HRESULT ApplyExecute(
741 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
742 wzId = pExecuteAction->relatedBundle.pRelatedBundle->package.sczId;
743 break;
744 + case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
745 + wzId = pExecuteAction->bundlePackage.pPackage->sczId;
746 + break;
747 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
748 wzId = pExecuteAction->exePackage.pPackage->sczId;
749 break;
@@ -2352,6 +2364,11 @@ static HRESULT DoExecuteAction(
2364 ExitOnFailure(hr, "Failed to execute related bundle.");
2365 break;
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 +
2372 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2373 hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
2374 ExitOnFailure(hr, "Failed to execute EXE package.");
@@ -2479,6 +2496,11 @@ static HRESULT DoRollbackActions(
2496 ExitOnFailure(hr, "Failed to execute related bundle.");
2497 break;
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 +
2504 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2505 hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2506 IgnoreRollbackError(hr, "Failed to rollback EXE package.");
@@ -2682,6 +2704,86 @@ LExit:
2704 return hr;
2705 }
2706
2707 +static 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 +
2772 +LExit:
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 +
2787 static HRESULT ExecuteExePackage(
2788 __in BURN_ENGINE_STATE* pEngineState,
2789 __in BURN_EXECUTE_ACTION* pExecuteAction,
src/burn/engine/bundlepackageengine.cpp
+261 -11
@@ -2,16 +2,80 @@
2
3 #include "precomp.h"
4
5 +static 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 + );
19 static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType(
20 __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType
21 );
22
23 // function definitions
24
25 +extern "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 +
67 +LExit:
68 + ReleaseStr(scz);
69 +
70 + return hr;
71 +}
72 +
73 extern "C" void BundlePackageEnginePackageUninitialize(
74 __in BURN_PACKAGE* pPackage
75 )
76 {
77 + ReleaseStr(pPackage->Bundle.sczBundleId);
78 + ReleaseStr(pPackage->Bundle.sczRegistrationKey);
79 ReleaseStr(pPackage->Bundle.sczInstallArguments);
80 ReleaseStr(pPackage->Bundle.sczRepairArguments);
81 ReleaseStr(pPackage->Bundle.sczUninstallArguments);
@@ -32,6 +96,44 @@ extern "C" void BundlePackageEnginePackageUninitialize(
96 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle));
97 }
98
99 +extern "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 +
137 //
138 // PlanCalculate - calculates the execute and rollback state for the requested package state.
139 //
@@ -151,6 +253,79 @@ LExit:
253 //
254 // PlanAdd - adds the calculated execute and rollback actions for the package.
255 //
256 +extern "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 +
322 +LExit:
323 + return hr;
324 +}
325 +
326 +//
327 +// PlanAdd - adds the calculated execute and rollback actions for the related bundle.
328 +//
329 extern "C" HRESULT BundlePackageEnginePlanAddRelatedBundle(
330 __in_opt DWORD *pdwInsertSequence,
331 __in BURN_RELATED_BUNDLE* pRelatedBundle,
@@ -164,7 +339,7 @@ extern "C" HRESULT BundlePackageEnginePlanAddRelatedBundle(
339 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
340
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.");
343
344 // add execute action
345 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
@@ -240,6 +415,26 @@ LExit:
415 return hr;
416 }
417
418 +extern "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 +
438 extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
439 __in BURN_EXECUTE_ACTION* pExecuteAction,
440 __in BURN_CACHE* pCache,
@@ -249,6 +444,57 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
444 __in LPVOID pvContext,
445 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
446 )
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 +
459 +extern "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 +
480 +LExit:
481 + return;
482 +}
483 +
484 +static 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 {
499 HRESULT hr = S_OK;
500 LPCWSTR wzArguments = NULL;
@@ -264,10 +510,6 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
510 PROCESS_INFORMATION pi = { };
511 DWORD dwExitCode = 0;
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;
513 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
514 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
515 LPCWSTR wzOperationCommandLine = NULL;
@@ -376,21 +618,29 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
618 }
619
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)
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);
629 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
630 }
631
632 // Add the list of ancestors, if any, to the burn command line.
386 - if (pExecuteAction->relatedBundle.sczAncestors)
633 + if (wzAncestors)
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);
636 ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");
637 }
638
392 - hr = CoreAppendEngineWorkingDirectoryToCommandLine(pExecuteAction->relatedBundle.sczEngineWorkingDirectory, &sczBaseCommand, NULL);
393 - ExitOnFailure(hr, "Failed to append the custom working directory to the bundlepackage command line.");
639 + if (wzEngineWorkingDirectory)
640 + {
641 + hr = CoreAppendEngineWorkingDirectoryToCommandLine(wzEngineWorkingDirectory, &sczBaseCommand, NULL);
642 + ExitOnFailure(hr, "Failed to append the custom working directory to the bundlepackage command line.");
643 + }
644
645 hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczBaseCommand, NULL);
646 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
src/burn/engine/bundlepackageengine.h
+26
@@ -9,12 +9,25 @@ extern "C" {
9
10 // function declarations
11
12 +HRESULT BundlePackageEngineParsePackageFromXml(
13 + __in IXMLDOMNode* pixnBundlePackage,
14 + __in BURN_PACKAGE* pPackage
15 + );
16 void BundlePackageEnginePackageUninitialize(
17 __in BURN_PACKAGE* pPackage
18 );
19 +HRESULT BundlePackageEngineDetectPackage(
20 + __in BURN_PACKAGE* pPackage
21 + );
22 HRESULT BundlePackageEnginePlanCalculatePackage(
23 __in BURN_PACKAGE* pPackage
24 );
25 +HRESULT BundlePackageEnginePlanAddPackage(
26 + __in BURN_PACKAGE* pPackage,
27 + __in BURN_PLAN* pPlan,
28 + __in BURN_LOGGING* pLog,
29 + __in BURN_VARIABLES* pVariables
30 + );
31 HRESULT BundlePackageEnginePlanAddRelatedBundle(
32 __in_opt DWORD *pdwInsertSequence,
33 __in BURN_RELATED_BUNDLE* pRelatedBundle,
@@ -22,6 +35,15 @@ HRESULT BundlePackageEnginePlanAddRelatedBundle(
35 __in BURN_LOGGING* pLog,
36 __in BURN_VARIABLES* pVariables
37 );
38 +HRESULT 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 + );
47 HRESULT BundlePackageEngineExecuteRelatedBundle(
48 __in BURN_EXECUTE_ACTION* pExecuteAction,
49 __in BURN_CACHE* pCache,
@@ -31,6 +53,10 @@ HRESULT BundlePackageEngineExecuteRelatedBundle(
53 __in LPVOID pvContext,
54 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
55 );
56 +void BundlePackageEngineUpdateInstallRegistrationState(
57 + __in BURN_EXECUTE_ACTION* pAction,
58 + __in HRESULT hrExecute
59 + );
60
61
62 #if defined(__cplusplus)
src/burn/engine/core.cpp
+14 -2
@@ -956,6 +956,9 @@ extern "C" LPCWSTR CoreRelationTypeToCommandLineString(
956 case BOOTSTRAPPER_RELATION_DEPENDENT_PATCH:
957 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH;
958 break;
959 + case BOOTSTRAPPER_RELATION_CHAIN_PACKAGE:
960 + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE;
961 + break;
962 case BOOTSTRAPPER_RELATION_NONE: __fallthrough;
963 default:
964 wzRelationTypeCommandLine = NULL;
@@ -1709,6 +1712,12 @@ extern "C" HRESULT CoreParseCommandLine(
1712
1713 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
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 + }
1721 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1))
1722 {
1723 pCommand->fPassthrough = TRUE;
@@ -2111,6 +2120,10 @@ static HRESULT DetectPackage(
2120 // Use the correct engine to detect the package.
2121 switch (pPackage->type)
2122 {
2123 + case BURN_PACKAGE_TYPE_BUNDLE:
2124 + hr = BundlePackageEngineDetectPackage(pPackage);
2125 + break;
2126 +
2127 case BURN_PACKAGE_TYPE_EXE:
2128 hr = ExeEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->variables);
2129 break;
@@ -2128,8 +2141,7 @@ static HRESULT DetectPackage(
2141 break;
2142
2143 default:
2131 - hr = E_NOTIMPL;
2132 - ExitOnRootFailure(hr, "Package type not supported by detect yet.");
2144 + ExitWithRootFailure(hr, E_NOTIMPL, "Package type not supported by detect yet.");
2145 }
2146
2147 LExit:
src/burn/engine/core.h
+1
@@ -27,6 +27,7 @@ const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_ADDON = L"burn.related.d
27 const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch";
28 const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH = L"burn.related.dependent.patch";
29 const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update";
30 +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE = L"burn.related.chain.package";
31 const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough";
32 const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate";
33 const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies";
src/burn/engine/elevation.cpp
+164 -12
@@ -20,6 +20,7 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE
20 BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP,
21 BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION,
22 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_RELATED_BUNDLE,
23 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_BUNDLE_PACKAGE,
24 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE,
25 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE,
26 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE,
@@ -250,6 +251,14 @@ static HRESULT OnExecuteRelatedBundle(
251 __in BYTE* pbData,
252 __in SIZE_T cbData
253 );
254 +static 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 + );
262 static HRESULT OnExecuteExePackage(
263 __in HANDLE hPipe,
264 __in BURN_CACHE* pCache,
@@ -910,6 +919,63 @@ LExit:
919 return hr;
920 }
921
922 +/*******************************************************************
923 + ElevationExecuteBundlePackage -
924 +
925 +*******************************************************************/
926 +extern "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 +
973 +LExit:
974 + ReleaseBuffer(pbData);
975 +
976 + return hr;
977 +}
978 +
979 /*******************************************************************
980 ElevationExecuteExePackage -
981
@@ -940,9 +1006,6 @@ extern "C" HRESULT ElevationExecuteExePackage(
1006 hr = BuffWriteNumber(&pbData, &cbData, fRollback);
1007 ExitOnFailure(hr, "Failed to write rollback.");
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 -
1009 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors);
1010 ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer.");
1011
@@ -2124,6 +2187,10 @@ static HRESULT ProcessElevatedChildMessage(
2187 hrResult = OnExecuteRelatedBundle(pContext->hPipe, pContext->pCache, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
2188 break;
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 +
2194 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE:
2195 hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pCache, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
2196 break;
@@ -2839,7 +2906,7 @@ LExit:
2906 return hr;
2907 }
2908
2842 -static HRESULT OnExecuteExePackage(
2909 +static HRESULT OnExecuteBundlePackage(
2910 __in HANDLE hPipe,
2911 __in BURN_CACHE* pCache,
2912 __in BURN_PACKAGES* pPackages,
@@ -2856,15 +2923,15 @@ static HRESULT OnExecuteExePackage(
2923 LPWSTR sczIgnoreDependencies = NULL;
2924 LPWSTR sczAncestors = NULL;
2925 LPWSTR sczEngineWorkingDirectory = NULL;
2859 - BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2926 + BOOTSTRAPPER_APPLY_RESTART bundleRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2927
2861 - executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
2928 + executeAction.type = BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE;
2929
2930 // Deserialize message data.
2931 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2932 ExitOnFailure(hr, "Failed to read EXE package id.");
2933
2867 - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action);
2934 + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.bundlePackage.action);
2935 ExitOnFailure(hr, "Failed to read action.");
2936
2937 hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
@@ -2882,21 +2949,107 @@ static HRESULT OnExecuteExePackage(
2949 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2950 ExitOnFailure(hr, "Failed to read variables.");
2951
2885 - hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage);
2952 + hr = PackageFindById(pPackages, sczPackage, &executeAction.bundlePackage.pPackage);
2953 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2954
2888 - if (BURN_PACKAGE_TYPE_EXE != executeAction.exePackage.pPackage->type)
2955 + if (BURN_PACKAGE_TYPE_BUNDLE != executeAction.bundlePackage.pPackage->type)
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);
2958 }
2959
2960 // Pass the list of dependencies to ignore, if any, to the related bundle.
2961 if (sczIgnoreDependencies && *sczIgnoreDependencies)
2962 {
2896 - hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0);
2963 + hr = StrAllocString(&executeAction.bundlePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0);
2964 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
2965 }
2966
2967 + // Pass the list of ancestors, if any, to the related bundle.
2968 + if (sczAncestors && *sczAncestors)
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 +
2984 +LExit:
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 +
3006 +static 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 {
@@ -2917,7 +3070,6 @@ static HRESULT OnExecuteExePackage(
3070 LExit:
3071 ReleaseStr(sczEngineWorkingDirectory);
3072 ReleaseStr(sczAncestors);
2920 - ReleaseStr(sczIgnoreDependencies);
3073 ReleaseStr(sczPackage);
3074 PlanUninitializeExecuteAction(&executeAction);
3075
src/burn/engine/elevation.h
+9
@@ -89,6 +89,15 @@ HRESULT ElevationExecuteRelatedBundle(
89 __in LPVOID pvContext,
90 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
91 );
92 +HRESULT 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 + );
101 HRESULT ElevationExecuteExePackage(
102 __in HANDLE hPipe,
103 __in BURN_EXECUTE_ACTION* pExecuteAction,
src/burn/engine/exeengine.cpp
+5 -19
@@ -91,7 +91,6 @@ extern "C" void ExeEnginePackageUninitialize(
91 ReleaseStr(pPackage->Exe.sczInstallArguments);
92 ReleaseStr(pPackage->Exe.sczRepairArguments);
93 ReleaseStr(pPackage->Exe.sczUninstallArguments);
94 - ReleaseStr(pPackage->Exe.sczIgnoreDependencies);
94 ReleaseMem(pPackage->Exe.rgExitCodes);
95
96 // free command-line arguments
@@ -291,12 +290,6 @@ extern "C" HRESULT ExeEnginePlanAddPackage(
290 pAction->exePackage.pPackage = pPackage;
291 pAction->exePackage.action = pPackage->rollback;
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 -
293 if (pPackage->Exe.wzAncestors)
294 {
295 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0);
@@ -325,12 +318,6 @@ extern "C" HRESULT ExeEnginePlanAddPackage(
318 pAction->exePackage.pPackage = pPackage;
319 pAction->exePackage.action = pPackage->execute;
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 -
321 if (pPackage->Exe.wzAncestors)
322 {
323 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0);
@@ -455,12 +442,11 @@ extern "C" HRESULT ExeEngineExecutePackage(
442 hr = StrAllocConcat(&sczBaseCommand, L" -norestart", 0);
443 ExitOnFailure(hr, "Failed to append norestart argument.");
444
458 - // Add the list of dependencies to ignore, if any, to the burn command line.
459 - if (pExecuteAction->exePackage.sczIgnoreDependencies)
460 - {
461 - hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
462 - ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
463 - }
445 + hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls", BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE);
446 + ExitOnFailure(hr, "Failed to append the relation type to the command line.");
447 +
448 + hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=ALL", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
449 + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
450
451 // Add the list of ancestors, if any, to the burn command line.
452 if (pExecuteAction->exePackage.sczAncestors)
src/burn/engine/logging.cpp
+2
@@ -781,6 +781,8 @@ extern "C" LPCSTR LoggingRelationTypeToString(
781 return "DependentPatch";
782 case BOOTSTRAPPER_RELATION_UPDATE:
783 return "Update";
784 + case BOOTSTRAPPER_RELATION_CHAIN_PACKAGE:
785 + return "ChainPackage";
786 default:
787 return "Invalid";
788 }
src/burn/engine/package.cpp
+11 -7
@@ -87,7 +87,7 @@ extern "C" HRESULT PackagesParseFromXml(
87 ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements.
88
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.");
92
93 // get package node count
@@ -199,7 +199,14 @@ extern "C" HRESULT PackagesParseFromXml(
199 }
200
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))
210 {
211 pPackage->type = BURN_PACKAGE_TYPE_EXE;
212
@@ -231,7 +238,7 @@ extern "C" HRESULT PackagesParseFromXml(
238 }
239 else
240 {
234 - // ignore other package types for now
241 + ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid package type: %ls", bstrNodeName);
242 }
243
244 if (!pPackage->fPermanent)
@@ -371,7 +378,7 @@ extern "C" void PackageUninitialize(
378 switch (pPackage->type)
379 {
380 case BURN_PACKAGE_TYPE_BUNDLE:
374 - BundlePackageEnginePackageUninitialize(pPackage);
381 + BundlePackageEnginePackageUninitialize(pPackage); // TODO: Modularization
382 break;
383 case BURN_PACKAGE_TYPE_EXE:
384 ExeEnginePackageUninitialize(pPackage); // TODO: Modularization
@@ -648,7 +655,6 @@ static HRESULT ParsePatchTargetCode(
655 IXMLDOMNodeList* pixnNodes = NULL;
656 IXMLDOMNode* pixnNode = NULL;
657 DWORD cNodes = 0;
651 - BSTR bstrNodeText = NULL;
658 BOOL fProduct;
659
660 hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes);
@@ -688,12 +694,10 @@ static HRESULT ParsePatchTargetCode(
694 pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE;
695
696 // prepare next iteration
691 - ReleaseNullBSTR(bstrNodeText);
697 ReleaseNullObject(pixnNode);
698 }
699
700 LExit:
696 - ReleaseBSTR(bstrNodeText);
701 ReleaseObject(pixnNode);
702 ReleaseObject(pixnNodes);
703
src/burn/engine/package.h
+3 -1
@@ -302,6 +302,8 @@ typedef struct _BURN_PACKAGE
302 {
303 struct
304 {
305 + LPWSTR sczBundleId;
306 + LPWSTR sczRegistrationKey;
307 LPWSTR sczInstallArguments;
308 LPWSTR sczRepairArguments;
309 LPWSTR sczUninstallArguments;
@@ -310,6 +312,7 @@ typedef struct _BURN_PACKAGE
312 LPCWSTR wzAncestors; // points directly into engine state.
313 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
314
315 + BOOL fWin64;
316 BOOL fSupportsBurnProtocol;
317
318 BURN_EXE_EXIT_CODE* rgExitCodes;
@@ -324,7 +327,6 @@ typedef struct _BURN_PACKAGE
327 LPWSTR sczInstallArguments;
328 LPWSTR sczRepairArguments;
329 LPWSTR sczUninstallArguments;
327 - LPWSTR sczIgnoreDependencies;
330 LPCWSTR wzAncestors; // points directly into engine state.
331 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
332
src/burn/engine/plan.cpp
+18 -6
@@ -292,7 +292,6 @@ extern "C" void PlanUninitializeExecuteAction(
292 break;
293
294 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
295 - ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies);
295 ReleaseStr(pExecuteAction->exePackage.sczAncestors);
296 ReleaseStr(pExecuteAction->exePackage.sczEngineWorkingDirectory);
297 break;
@@ -1181,6 +1180,10 @@ extern "C" HRESULT PlanExecutePackage(
1180 // Add execute actions.
1181 switch (pPackage->type)
1182 {
1183 + case BURN_PACKAGE_TYPE_BUNDLE:
1184 + hr = BundlePackageEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables);
1185 + break;
1186 +
1187 case BURN_PACKAGE_TYPE_EXE:
1188 hr = ExeEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables);
1189 break;
@@ -1506,16 +1509,16 @@ extern "C" HRESULT PlanRelatedBundlesComplete(
1509
1510 switch (pPlan->rgExecuteActions[i].type)
1511 {
1509 - case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
1510 - packageAction = pPlan->rgExecuteActions[i].relatedBundle.action;
1511 - pPackage = &pPlan->rgExecuteActions[i].relatedBundle.pRelatedBundle->package;
1512 + case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE:
1513 + packageAction = pPlan->rgExecuteActions[i].bundlePackage.action;
1514 + pPackage = pPlan->rgExecuteActions[i].bundlePackage.pPackage;
1515 fBundle = TRUE;
1516 break;
1517
1518 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
1519 packageAction = pPlan->rgExecuteActions[i].exePackage.action;
1520 pPackage = pPlan->rgExecuteActions[i].exePackage.pPackage;
1518 - fBundle = TRUE;
1521 + fBundle = pPackage->Exe.fBundle;
1522 break;
1523
1524 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
@@ -2751,6 +2754,10 @@ static HRESULT CalculateExecuteActions(
2754 // Calculate execute actions.
2755 switch (pPackage->type)
2756 {
2757 + case BURN_PACKAGE_TYPE_BUNDLE:
2758 + hr = BundlePackageEnginePlanCalculatePackage(pPackage);
2759 + break;
2760 +
2761 case BURN_PACKAGE_TYPE_EXE:
2762 hr = ExeEnginePlanCalculatePackage(pPackage);
2763 break;
@@ -2784,7 +2791,8 @@ static BOOL NeedsCache(
2791 )
2792 {
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).
2796 {
2797 return BOOTSTRAPPER_ACTION_STATE_NONE != action;
2798 }
@@ -2918,6 +2926,10 @@ static void ExecuteActionLog(
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);
2927 break;
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 +
2933 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2934 LogStringLine(PlanDumpLevel, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action));
2935 break;
src/burn/engine/plan.h
+8
@@ -43,6 +43,7 @@ enum BURN_EXECUTE_ACTION_TYPE
43 BURN_EXECUTE_ACTION_TYPE_WAIT_CACHE_PACKAGE,
44 BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE,
45 BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE,
46 + BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE,
47 BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE,
48 BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE,
49 BURN_EXECUTE_ACTION_TYPE_MSP_TARGET,
@@ -167,6 +168,13 @@ typedef struct _BURN_EXECUTE_ACTION
168 LPWSTR sczIgnoreDependencies;
169 LPWSTR sczAncestors;
170 LPWSTR sczEngineWorkingDirectory;
171 + } bundlePackage;
172 + struct
173 + {
174 + BURN_PACKAGE* pPackage;
175 + BOOTSTRAPPER_ACTION_STATE action;
176 + LPWSTR sczAncestors;
177 + LPWSTR sczEngineWorkingDirectory;
178 } exePackage;
179 struct
180 {
src/burn/engine/registration.cpp
+2 -3
@@ -7,7 +7,6 @@
7
8 const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
9 const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
10 -const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed";
10 const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon";
11 const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion";
12 const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize";
@@ -1219,7 +1218,7 @@ static HRESULT SetPaths(
1218 pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1219
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);
1222 ExitOnFailure(hr, "Failed to build uninstall registry key path.");
1223
1224 // build cache directory
@@ -1231,7 +1230,7 @@ static HRESULT SetPaths(
1230 ExitOnFailure(hr, "Failed to build cached executable path.");
1231
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);
1234 ExitOnFailure(hr, "Failed to build state file path.");
1235
1236 LExit:
src/burn/engine/registration.h
+2
@@ -22,6 +22,8 @@ const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_PROTOCOL_VERSION = L"EngineProto
22 const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
23 const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag";
24
25 +const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed";
26 +
27 enum BURN_RESUME_MODE
28 {
29 BURN_RESUME_MODE_NONE,
src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
+1
@@ -80,6 +80,7 @@
80 <ItemGroup>
81 <None Include="TestData\CacheTest\CacheSignatureTest.File" CopyToOutputDirectory="PreserveNewest" />
82 <None Include="TestData\PlanTest\BasicFunctionality_BundleA_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
83 + <None Include="TestData\PlanTest\BundlePackage_Multiple_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
84 <None Include="TestData\PlanTest\Failure_BundleD_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
85 <None Include="TestData\PlanTest\MsiTransaction_BundleAv1_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
86 <None Include="TestData\PlanTest\MsuPackageFixture_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
src/burn/test/BurnUnitTest/PlanTest.cpp
+135 -6
@@ -10,6 +10,7 @@ static HRESULT WINAPI PlanTestBAProc(
10 );
11
12 static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml";
13 +static LPCWSTR wzMultipleBundlePackageManifestFileName = L"BundlePackage_Multiple_manifest.xml";
14 static LPCWSTR wzSingleExeManifestFileName = L"Failure_BundleD_manifest.xml";
15 static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml";
16 static LPCWSTR wzSingleMsuManifestFileName = L"MsuPackageFixture_manifest.xml";
@@ -339,6 +340,121 @@ namespace Bootstrapper
340 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
341 }
342
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]
459 void OrphanCompatiblePackageTest()
460 {
@@ -716,7 +832,7 @@ namespace Bootstrapper
832 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
833 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"ExeA");
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);
836 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
837 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
838 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
@@ -727,7 +843,7 @@ namespace Bootstrapper
843 dwExecuteCheckpointId = 2;
844 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
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);
847 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
848 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
849 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
@@ -2125,7 +2241,7 @@ namespace Bootstrapper
2241 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
2242 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
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);
2245 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
2246 dwExecuteCheckpointId += 1; // cache checkpoints
2247 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
@@ -2678,20 +2794,33 @@ namespace Bootstrapper
2794 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
2795 }
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 +
2812 void ValidateExecuteExePackage(
2813 __in BURN_PLAN* pPlan,
2814 __in BOOL fRollback,
2815 __in DWORD dwIndex,
2816 __in LPCWSTR wzPackageId,
2686 - __in BOOTSTRAPPER_ACTION_STATE action,
2687 - __in LPCWSTR wzIgnoreDependencies
2817 + __in BOOTSTRAPPER_ACTION_STATE action
2818 )
2819 {
2820 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
2821 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type);
2822 NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId);
2823 Assert::Equal<DWORD>(action, pAction->exePackage.action);
2694 - NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies);
2824 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
2825 }
2826
src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml new
+1
@@ -0,0 +1 @@
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
src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj new
+19
@@ -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
src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs new
+11
@@ -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>
src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs new
+51
@@ -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 +
3 +namespace 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 +}
src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+7
@@ -178,6 +178,13 @@ namespace WixToolset.Core.Burn
178 {
179 switch (facade.PackageSymbol.Type)
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 +
188 case WixBundlePackageType.Exe:
189 {
190 var command = new ProcessExePackageCommand(facade, payloadSymbols);
src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
+2
@@ -65,6 +65,8 @@ namespace WixToolset.Core.Burn.Bind
65 case SymbolDefinitionType.WixBundle:
66 case SymbolDefinitionType.WixBundleContainer:
67 case SymbolDefinitionType.WixBundleCustomDataAttribute:
68 + case SymbolDefinitionType.WixBundleBundlePackage:
69 + case SymbolDefinitionType.WixBundleBundlePackagePayload:
70 case SymbolDefinitionType.WixBundleExePackage:
71 case SymbolDefinitionType.WixBundleExePackagePayload:
72 case SymbolDefinitionType.WixBundleExtension:
src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
+30 -2
@@ -37,6 +37,7 @@ namespace WixToolset.Core.Burn.Bundles
37
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;
40 + protected const uint IMAGE_NT_HEADER_OFFSET_MACHINE = 4;
41 protected const uint IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS = 6;
42 protected const uint IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER = 20;
43
@@ -57,6 +58,12 @@ namespace WixToolset.Core.Burn.Bundles
58 protected const uint IMAGE_NT_SIGNATURE = 0x00004550;
59 protected const ulong IMAGE_SECTION_WIXBURN_NAME = 0x6E7275627869772E; // ".wixburn", as a qword.
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 +
67 // The ".wixburn" section contains:
68 // 0- 3: magic number
69 // 4- 7: version
@@ -119,10 +126,13 @@ namespace WixToolset.Core.Burn.Bundles
126 this.AttachedContainers = new List<ContainerSlot>();
127 }
128
129 + public bool Invalid { get; protected set; }
130 + public ushort MachineType { get; private set; }
131 public uint Checksum { get; protected set; }
132 public uint SignatureOffset { get; protected set; }
133 public uint SignatureSize { get; protected set; }
134 public uint Version { get; protected set; }
135 + public Guid BundleId { get; protected set; }
136 public uint StubSize { get; protected set; }
137 public uint OriginalChecksum { get; protected set; }
138 public uint OriginalSignatureOffset { get; protected set; }
@@ -170,8 +180,12 @@ namespace WixToolset.Core.Burn.Bundles
180 /// Initialize the common information about a Burn engine.
181 /// </summary>
182 /// <param name="reader">Binary reader open against a Burn engine.</param>
173 - /// <returns>True if initialized.</returns>
174 - protected bool Initialize(BinaryReader reader)
183 + protected void Initialize(BinaryReader reader)
184 + {
185 + this.Invalid = !this.TryInitialize(reader);
186 + }
187 +
188 + private bool TryInitialize(BinaryReader reader)
189 {
190 if (!this.GetWixburnSectionInfo(reader))
191 {
@@ -202,6 +216,7 @@ namespace WixToolset.Core.Burn.Bundles
216 return false;
217 }
218
219 + this.BundleId = BurnCommon.ReadGuid(bytes, BURN_SECTION_OFFSET_BUNDLEGUID);
220 this.StubSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_STUBSIZE);
221 this.OriginalChecksum = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALCHECKSUM);
222 this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET);
@@ -321,6 +336,8 @@ namespace WixToolset.Core.Burn.Bundles
336 return false;
337 }
338
339 + this.MachineType = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_MACHINE);
340 +
341 var sizeOptionalHeader = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER);
342
343 this.sections = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS);
@@ -362,6 +379,17 @@ namespace WixToolset.Core.Burn.Bundles
379 return true;
380 }
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 +
393 /// <summary>
394 /// Reads a UInt16 value in little-endian format from an offset in an array of bytes.
395 /// </summary>
src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+7 -9
@@ -25,7 +25,6 @@ namespace WixToolset.Core.Burn.Bundles
25 {
26 private bool disposed;
27
28 - private bool invalidBundle;
28 private BinaryReader binaryReader;
29 private readonly List<DictionaryEntry> attachedContainerPayloadNames;
30
@@ -53,13 +52,12 @@ namespace WixToolset.Core.Burn.Bundles
52 /// <returns>Burn reader.</returns>
53 public static BurnReader Open(IMessaging messaging, string fileExe)
54 {
56 - var reader = new BurnReader(messaging, fileExe);
57 -
58 - reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
59 - if (!reader.Initialize(reader.binaryReader))
55 + var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
56 + var reader = new BurnReader(messaging, fileExe)
57 {
61 - reader.invalidBundle = true;
62 - }
58 + binaryReader = binaryReader,
59 + };
60 + reader.Initialize(reader.binaryReader);
61
62 return reader;
63 }
@@ -78,7 +76,7 @@ namespace WixToolset.Core.Burn.Bundles
76 return false;
77 }
78
81 - if (this.invalidBundle)
79 + if (this.Invalid)
80 {
81 return false;
82 }
@@ -156,7 +154,7 @@ namespace WixToolset.Core.Burn.Bundles
154 return false;
155 }
156
159 - if (this.invalidBundle)
157 + if (this.Invalid)
158 {
159 return false;
160 }
src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+7 -10
@@ -23,7 +23,6 @@ namespace WixToolset.Core.Burn.Bundles
23 internal class BurnWriter : BurnCommon
24 {
25 private bool disposed;
26 - private bool invalidBundle;
26 private BinaryWriter binaryWriter;
27
28 /// <summary>
@@ -48,13 +47,10 @@ namespace WixToolset.Core.Burn.Bundles
47
48 using (var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)))
49 {
51 - if (!writer.Initialize(binaryReader))
52 - {
53 - writer.invalidBundle = true;
54 - }
50 + writer.Initialize(binaryReader);
51 }
52
57 - if (!writer.invalidBundle)
53 + if (!writer.Invalid)
54 {
55 writer.binaryWriter = new BinaryWriter(File.Open(fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete));
56 }
@@ -70,7 +66,7 @@ namespace WixToolset.Core.Burn.Bundles
66 /// <returns></returns>
67 public bool InitializeBundleSectionData(long stubSize, string bundleId)
68 {
73 - if (this.invalidBundle)
69 + if (this.Invalid)
70 {
71 return false;
72 }
@@ -84,6 +80,7 @@ namespace WixToolset.Core.Burn.Bundles
80 this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin);
81 this.binaryWriter.Write(bundleGuid.ToByteArray());
82
83 + this.BundleId = bundleGuid;
84 this.StubSize = (uint)stubSize;
85
86 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_STUBSIZE, this.StubSize);
@@ -191,7 +188,7 @@ namespace WixToolset.Core.Burn.Bundles
188
189 public void RememberThenResetSignature()
190 {
194 - if (this.invalidBundle)
191 + if (this.Invalid)
192 {
193 return;
194 }
@@ -241,14 +238,14 @@ namespace WixToolset.Core.Burn.Bundles
238 /// <returns>true if the container data is successfully appended; false otherwise</returns>
239 private bool AppendContainer(Stream containerStream, uint containerSize, uint burnSectionOffsetSize, uint burnSectionCount)
240 {
244 - if (this.invalidBundle)
241 + if (this.Invalid)
242 {
243 return false;
244 }
245
246 if (burnSectionOffsetSize > (this.wixburnRawDataSize - sizeof(uint)))
247 {
251 - this.invalidBundle = true;
248 + this.Invalid = true;
249 this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(this.wixburnMaxContainers));
250 return false;
251 }
src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
+10 -1
@@ -381,7 +381,16 @@ namespace WixToolset.Core.Burn.Bundles
381 writer.WriteAttributeString("InstallCondition", package.PackageSymbol.InstallCondition);
382 }
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
394 {
395 writer.WriteAttributeString("DetectCondition", exePackage.DetectCondition);
396 writer.WriteAttributeString("InstallArguments", exePackage.InstallCommand);
src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
+26 -1
@@ -28,10 +28,12 @@ namespace WixToolset.Core.Burn.Bundles
28 public void Execute()
29 {
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);
32 var exePackages = this.Section.Symbols.OfType<WixBundleExePackageSymbol>().ToDictionary(t => t.Id.Id);
33 var msiPackages = this.Section.Symbols.OfType<WixBundleMsiPackageSymbol>().ToDictionary(t => t.Id.Id);
34 var mspPackages = this.Section.Symbols.OfType<WixBundleMspPackageSymbol>().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);
37 var exePackagePayloads = this.Section.Symbols.OfType<WixBundleExePackagePayloadSymbol>().ToDictionary(t => t.Id.Id);
38 var msiPackagePayloads = this.Section.Symbols.OfType<WixBundleMsiPackagePayloadSymbol>().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
51 if (wixGroup.ChildType == ComplexReferenceChildType.PackagePayload)
52 {
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))
67 {
68 if (package.Type == WixBundlePackageType.Exe)
69 {
@@ -129,6 +143,17 @@ namespace WixToolset.Core.Burn.Bundles
143
144 switch (package.Type)
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 +
157 case WixBundlePackageType.Exe:
158 if (exePackages.TryGetValue(id, out var exePackage))
159 {
src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs
+10 -1
@@ -55,7 +55,11 @@ namespace WixToolset.Core.Burn.Bundles
55
56 foreach (var packageFacade in this.PackageFacadesById.Values)
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)
63 {
64 this.ValidateExePackage(wixBundleExePackageSymbol, packageFacade.PackageSymbol);
65 }
@@ -90,6 +94,11 @@ namespace WixToolset.Core.Burn.Bundles
94 }
95 }
96
97 + private void ValidateBundlePackage(WixBundleBundlePackageSymbol symbol, WixBundlePackageSymbol packageSymbol)
98 + {
99 + this.ValidateChainPackage(packageSymbol, "BundlePackage");
100 + }
101 +
102 private void ValidateExePackage(WixBundleExePackageSymbol symbol, WixBundlePackageSymbol packageSymbol)
103 {
104 this.ValidateChainPackage(packageSymbol, "ExePackage");
src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs new
+152
@@ -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 +
3 +namespace 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 +}
src/wix/WixToolset.Core/Compiler_Bundle.cs
+47 -5
@@ -1517,6 +1517,9 @@ namespace WixToolset.Core
1517 WixBundlePackageType? packageType = null;
1518 switch (child.Name.LocalName)
1519 {
1520 + case "BundlePackagePayload":
1521 + packageType = WixBundlePackageType.Bundle;
1522 + break;
1523 case "ExePackagePayload":
1524 packageType = WixBundlePackageType.Exe;
1525 break;
@@ -1751,6 +1754,10 @@ namespace WixToolset.Core
1754 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1755 previousType = ComplexReferenceChildType.Package;
1756 break;
1757 + case "BundlePackage":
1758 + previousId = this.ParseBundlePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1759 + previousType = ComplexReferenceChildType.Package;
1760 + break;
1761 case "RollbackBoundary":
1762 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1763 previousType = ComplexReferenceChildType.Package;
@@ -1841,6 +1848,20 @@ namespace WixToolset.Core
1848 return this.ParseChainPackage(node, WixBundlePackageType.Exe, parentType, parentId, previousType, previousId);
1849 }
1850
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>
1866 /// Parse RollbackBoundary element
1867 /// </summary>
@@ -2096,15 +2117,15 @@ namespace WixToolset.Core
2117 break;
2118 case "InstallArguments":
2119 installArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2099 - allowed = (packageType == WixBundlePackageType.Exe);
2120 + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2121 break;
2122 case "RepairArguments":
2123 repairArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2103 - allowed = (packageType == WixBundlePackageType.Exe);
2124 + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2125 break;
2126 case "UninstallArguments":
2127 uninstallArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2107 - allowed = (packageType == WixBundlePackageType.Exe);
2128 + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2129 break;
2130 case "PerMachine":
2131 perMachine = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
@@ -2339,19 +2360,20 @@ namespace WixToolset.Core
2360 this.ParseProvidesElement(child, packageType, id.Id, out _);
2361 break;
2362 case "ExitCode":
2342 - allowed = (packageType == WixBundlePackageType.Exe);
2363 + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2364 if (allowed)
2365 {
2366 this.ParseExitCodeElement(child, id.Id);
2367 }
2368 break;
2369 case "CommandLine":
2349 - allowed = (packageType == WixBundlePackageType.Exe);
2370 + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2371 if (allowed)
2372 {
2373 this.ParseCommandLineElement(child, id.Id);
2374 }
2375 break;
2376 + case "BundlePackagePayload":
2377 case "ExePackagePayload":
2378 case "MsiPackagePayload":
2379 case "MspPackagePayload":
@@ -2422,6 +2444,18 @@ namespace WixToolset.Core
2444
2445 switch (packageType)
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 +
2459 case WixBundlePackageType.Exe:
2460 WixBundleExePackageAttributes exeAttributes = 0;
2461 exeAttributes |= (YesNoType.Yes == bundle) ? WixBundleExePackageAttributes.Bundle : 0;
@@ -2478,6 +2512,10 @@ namespace WixToolset.Core
2512 {
2513 switch (packageType)
2514 {
2515 + case WixBundlePackageType.Bundle:
2516 + this.Core.AddSymbol(new WixBundleBundlePackagePayloadSymbol(sourceLineNumbers, payloadId));
2517 + break;
2518 +
2519 case WixBundlePackageType.Exe:
2520 this.Core.AddSymbol(new WixBundleExePackagePayloadSymbol(sourceLineNumbers, payloadId));
2521 break;
@@ -2741,6 +2779,10 @@ namespace WixToolset.Core
2779 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2780 previousType = ComplexReferenceChildType.Package;
2781 break;
2782 + case "BundlePackage":
2783 + previousId = this.ParseBundlePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2784 + previousType = ComplexReferenceChildType.Package;
2785 + break;
2786 case "RollbackBoundary":
2787 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2788 previousType = ComplexReferenceChildType.Package;
src/wix/WixToolset.Core/Compiler_Dependency.cs
+1 -1
@@ -129,7 +129,7 @@ namespace WixToolset.Core
129 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, "ProductCode");
130 key = "!(bind.property.ProductCode)";
131 }
132 - else if (WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msu == packageType)
132 + else if (WixBundlePackageType.Bundle == packageType || WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msu == packageType)
133 {
134 // Must specify the provider key when authored for a package.
135 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs new
+114
@@ -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 +
3 +namespace 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 +}
src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs new
+10
@@ -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>