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 >= 528040" InstallArguments="/q /norestart /log "[NetFx48WebLog].html"" 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>