Add option to hosts to always install prereqs.
Add PrereqPackage to BundlePackage Implements 4718
Sean Hall committed
May 13, 2022 at 15:39 UTC
d5985a1688bc878e42ffd3ce3939fa52303cab16
35 files changed
+1154
-42
src/ext/Bal/dnchost/dnchost.cpp
+19
@@ -76,6 +76,16 @@ extern "C" HRESULT WINAPI BootstrapperApplicationCreate(
76
vstate.fInitialized = TRUE;
77
}
78
79
+ if (vstate.prereqData.fAlwaysInstallPrereqs && !vstate.prereqData.fCompleted)
80
+ {
81
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application since it's configured to always run before loading the runtime.");
82
+
83
+ hr = CreatePrerequisiteBA(&vstate, pEngine, pArgs, pResults);
84
+ BalExitOnFailure(hr, "Failed to create the pre-requisite bootstrapper application.");
85
+
86
+ ExitFunction();
87
+ }
88
+
89
if (!vstate.fInitializedRuntime)
90
{
91
hr = LoadRuntime(&vstate);
@@ -214,6 +224,15 @@ static HRESULT LoadDncConfiguration(
224
hr = StrAllocConcat(&pState->sczBaFactoryRuntimeConfigPath, L".runtimeconfig.json", 0);
225
BalExitOnFailure(hr, "Failed to concat extension to runtime config path.");
226
227
+ hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixMbaPrereqOptions", &pixnHost);
228
+ BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find WixMbaPrereqOptions element in bootstrapper application config.");
229
+
230
+ if (fXmlFound)
231
+ {
232
+ hr = XmlGetAttributeNumber(pixnHost, L"AlwaysInstallPrereqs", reinterpret_cast<DWORD*>(&pState->prereqData.fAlwaysInstallPrereqs));
233
+ BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get AlwaysInstallPrereqs value.");
234
+ }
235
+
236
pState->type = DNCHOSTTYPE_FDD;
237
238
hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixDncOptions", &pixnHost);
src/ext/Bal/mbahost/mbahost.cpp
+49
-5
@@ -24,6 +24,10 @@ static HRESULT GetAppDomain(
24
static HRESULT LoadModulePaths(
25
__in MBASTATE* pState
26
);
27
+static HRESULT LoadMbaConfiguration(
28
+ __in MBASTATE* pState,
29
+ __in const BOOTSTRAPPER_CREATE_ARGS* pArgs
30
+ );
31
static HRESULT CheckSupportedFrameworks(
32
__in LPCWSTR wzConfigPath
33
);
@@ -96,12 +100,28 @@ extern "C" HRESULT WINAPI BootstrapperApplicationCreate(
100
101
if (!vstate.fInitialized)
102
{
103
+ hr = XmlInitialize();
104
+ BalExitOnFailure(hr, "Failed to initialize XML.");
105
+
106
hr = LoadModulePaths(&vstate);
107
BalExitOnFailure(hr, "Failed to load the module paths.");
108
109
+ hr = LoadMbaConfiguration(&vstate, pArgs);
110
+ BalExitOnFailure(hr, "Failed to get the mba configuration.");
111
+
112
vstate.fInitialized = TRUE;
113
}
114
115
+ if (vstate.prereqData.fAlwaysInstallPrereqs && !vstate.prereqData.fCompleted)
116
+ {
117
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application since it's configured to always run before loading the runtime.");
118
+
119
+ hr = CreatePrerequisiteBA(&vstate, pEngine, pArgs, pResults);
120
+ BalExitOnFailure(hr, "Failed to create the pre-requisite bootstrapper application.");
121
+
122
+ ExitFunction();
123
+ }
124
+
125
if (!vstate.fInitializedRuntime)
126
{
127
hr = LoadRuntime(&vstate);
@@ -264,6 +284,35 @@ LExit:
284
return hr;
285
}
286
287
+static HRESULT LoadMbaConfiguration(
288
+ __in MBASTATE* pState,
289
+ __in const BOOTSTRAPPER_CREATE_ARGS* pArgs
290
+ )
291
+{
292
+ HRESULT hr = S_OK;
293
+ IXMLDOMDocument* pixdManifest = NULL;
294
+ IXMLDOMNode* pixnHost = NULL;
295
+ BOOL fXmlFound = FALSE;
296
+
297
+ hr = XmlLoadDocumentFromFile(pArgs->pCommand->wzBootstrapperApplicationDataPath, &pixdManifest);
298
+ BalExitOnFailure(hr, "Failed to load BalManifest '%ls'", pArgs->pCommand->wzBootstrapperApplicationDataPath);
299
+
300
+ hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixMbaPrereqOptions", &pixnHost);
301
+ BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find WixMbaPrereqOptions element in bootstrapper application config.");
302
+
303
+ if (fXmlFound)
304
+ {
305
+ hr = XmlGetAttributeNumber(pixnHost, L"AlwaysInstallPrereqs", reinterpret_cast<DWORD*>(&pState->prereqData.fAlwaysInstallPrereqs));
306
+ BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get AlwaysInstallPrereqs value.");
307
+ }
308
+
309
+LExit:
310
+ ReleaseObject(pixnHost);
311
+ ReleaseObject(pixdManifest);
312
+
313
+ return hr;
314
+}
315
+
316
// Checks whether at least one of required supported frameworks is installed via the NETFX registry keys.
317
static HRESULT CheckSupportedFrameworks(
318
__in LPCWSTR wzConfigPath
@@ -280,9 +329,6 @@ static HRESULT CheckSupportedFrameworks(
329
DWORD dwFrameworkInstalled = 0;
330
BOOL fUpdatedManifest = FALSE;
331
283
- hr = XmlInitialize();
284
- ExitOnFailure(hr, "Failed to initialize XML.");
285
-
332
hr = XmlLoadDocumentFromFile(wzConfigPath, &pixdManifest);
333
ExitOnFailure(hr, "Failed to load bootstrapper config file from path: %ls", wzConfigPath);
334
@@ -342,8 +388,6 @@ LExit:
388
ReleaseObject(pNodeList);
389
ReleaseObject(pixdManifest);
390
345
- XmlUninitialize();
346
-
391
return hr;
392
}
393
src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs
+36
-2
@@ -107,7 +107,41 @@ namespace WixToolsetTest.Bal
107
}
108
109
[Fact]
110
- public void CantBuildUsingMBAWithNoPrereqs()
110
+ public void CanBuildUsingMBAWithAlwaysInstallPrereqs()
111
+ {
112
+ using (var fs = new DisposableFileSystem())
113
+ {
114
+ var baseFolder = fs.GetFolder();
115
+ var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
116
+ var bundleSourceFolder = TestData.Get(@"TestData\MBA");
117
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
118
+ var baFolderPath = Path.Combine(baseFolder, "ba");
119
+ var extractFolderPath = Path.Combine(baseFolder, "extract");
120
+
121
+ var compileResult = WixRunner.Execute(new[]
122
+ {
123
+ "build",
124
+ Path.Combine(bundleSourceFolder, "AlwaysInstallPrereqsBundle.wxs"),
125
+ "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
126
+ "-intermediateFolder", intermediateFolder,
127
+ "-o", bundleFile,
128
+ });
129
+
130
+ compileResult.AssertSuccess();
131
+
132
+ Assert.True(File.Exists(bundleFile));
133
+
134
+ var extractResult = BundleExtractor.ExtractBAContainer(null, bundleFile, baFolderPath, extractFolderPath);
135
+ extractResult.AssertSuccess();
136
+
137
+ var wixMbaPrereqOptionsElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:WixMbaPrereqOptions");
138
+ var wixMbaPrereqOptions = (XmlNode)Assert.Single(wixMbaPrereqOptionsElements);
139
+ Assert.Equal("<WixMbaPrereqOptions AlwaysInstallPrereqs='1' />", wixMbaPrereqOptions.GetTestXml());
140
+ }
141
+ }
142
+
143
+ [Fact]
144
+ public void CannotBuildUsingMBAWithNoPrereqs()
145
{
146
using (var fs = new DisposableFileSystem())
147
{
@@ -133,7 +167,7 @@ namespace WixToolsetTest.Bal
167
}
168
169
[Fact]
136
- public void CantBuildUsingOverridableWrongCase()
170
+ public void CannotBuildUsingOverridableWrongCase()
171
{
172
using (var fs = new DisposableFileSystem())
173
{
src/ext/Bal/test/WixToolsetTest.Bal/TestData/MBA/AlwaysInstallPrereqsBundle.wxs
new
+12
@@ -0,0 +1,12 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
3
+ xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
4
+ <Bundle Name="AlwaysInstallPrereqsBundle" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{36E9E102-E0E4-4A91-941D-6681A49216E6}">
5
+ <BootstrapperApplication>
6
+ <bal:WixManagedBootstrapperApplicationHost AlwaysInstallPrereqs="yes" />
7
+ </BootstrapperApplication>
8
+ <Chain>
9
+ <ExePackage bal:PrereqPackage="yes" Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
10
+ </Chain>
11
+ </Bundle>
12
+</Wix>
src/ext/Bal/wixext/BalBurnBackendExtension.cs
+1
@@ -24,6 +24,7 @@ namespace WixToolset.Bal
24
BalSymbolDefinitions.WixMbaPrereqInformation,
25
BalSymbolDefinitions.WixStdbaOptions,
26
BalSymbolDefinitions.WixStdbaOverridableVariable,
27
+ BalSymbolDefinitions.WixMbaPrereqOptions,
28
};
29
30
protected override IReadOnlyCollection<IntermediateSymbolDefinition> SymbolDefinitions => BurnSymbolDefinitions;
src/ext/Bal/wixext/BalCompiler.cs
+26
-1
@@ -117,6 +117,7 @@ namespace WixToolset.Bal
117
118
switch (parentElement.Name.LocalName)
119
{
120
+ case "BundlePackage":
121
case "ExePackage":
122
case "MsiPackage":
123
case "MspPackage":
@@ -216,7 +217,7 @@ namespace WixToolset.Bal
217
case "PrereqPackage":
218
if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute))
219
{
219
- if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out prereqInfo))
220
+ if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out _))
221
{
222
prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers)
223
{
@@ -703,6 +704,7 @@ namespace WixToolset.Bal
704
private void ParseWixManagedBootstrapperApplicationHostElement(Intermediate intermediate, IntermediateSection section, XElement node)
705
{
706
var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
707
+ bool alwaysInstallPrereqs = false;
708
string logoFile = null;
709
string themeFile = null;
710
string localizationFile = null;
@@ -714,6 +716,9 @@ namespace WixToolset.Bal
716
{
717
switch (attrib.Name.LocalName)
718
{
719
+ case "AlwaysInstallPrereqs":
720
+ alwaysInstallPrereqs = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes;
721
+ break;
722
case "LogoFile":
723
logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
724
break;
@@ -792,6 +797,14 @@ namespace WixToolset.Bal
797
}
798
799
this.CreateBARef(section, sourceLineNumbers, node, baId);
800
+
801
+ if (alwaysInstallPrereqs)
802
+ {
803
+ section.AddSymbol(new WixMbaPrereqOptionsSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixMbaPrereqOptions"))
804
+ {
805
+ AlwaysInstallPrereqs = 1,
806
+ });
807
+ }
808
}
809
}
810
@@ -802,6 +815,7 @@ namespace WixToolset.Bal
815
private void ParseWixDotNetCoreBootstrapperApplicationHostElement(Intermediate intermediate, IntermediateSection section, XElement node)
816
{
817
var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
818
+ bool alwaysInstallPrereqs = false;
819
string logoFile = null;
820
string themeFile = null;
821
string localizationFile = null;
@@ -814,6 +828,9 @@ namespace WixToolset.Bal
828
{
829
switch (attrib.Name.LocalName)
830
{
831
+ case "AlwaysInstallPrereqs":
832
+ alwaysInstallPrereqs = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes;
833
+ break;
834
case "LogoFile":
835
logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
836
break;
@@ -903,6 +920,14 @@ namespace WixToolset.Bal
920
}
921
922
this.CreateBARef(section, sourceLineNumbers, node, baId);
923
+
924
+ if (alwaysInstallPrereqs)
925
+ {
926
+ section.AddSymbol(new WixMbaPrereqOptionsSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixMbaPrereqOptions"))
927
+ {
928
+ AlwaysInstallPrereqs = 1,
929
+ });
930
+ }
931
}
932
}
933
src/ext/Bal/wixext/Symbols/BalSymbolDefinitions.cs
+5
@@ -16,6 +16,7 @@ namespace WixToolset.Bal
16
WixMbaPrereqInformation,
17
WixStdbaOptions,
18
WixStdbaOverridableVariable,
19
+ WixMbaPrereqOptions,
20
}
21
22
public static partial class BalSymbolDefinitions
@@ -60,6 +61,9 @@ namespace WixToolset.Bal
61
case BalSymbolDefinitionType.WixStdbaOverridableVariable:
62
return BalSymbolDefinitions.WixStdbaOverridableVariable;
63
64
+ case BalSymbolDefinitionType.WixMbaPrereqOptions:
65
+ return BalSymbolDefinitions.WixMbaPrereqOptions;
66
+
67
default:
68
throw new ArgumentOutOfRangeException(nameof(type));
69
}
@@ -75,6 +79,7 @@ namespace WixToolset.Bal
79
WixMbaPrereqInformation.AddTag(BurnConstants.BootstrapperApplicationDataSymbolDefinitionTag);
80
WixStdbaOptions.AddTag(BurnConstants.BootstrapperApplicationDataSymbolDefinitionTag);
81
WixStdbaOverridableVariable.AddTag(BurnConstants.BootstrapperApplicationDataSymbolDefinitionTag);
82
+ WixMbaPrereqOptions.AddTag(BurnConstants.BootstrapperApplicationDataSymbolDefinitionTag);
83
}
84
}
85
}
src/ext/Bal/wixext/Symbols/WixMbaPrereqOptionsSymbol.cs
new
+47
@@ -0,0 +1,47 @@
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.Bal
4
+{
5
+ using WixToolset.Data;
6
+ using WixToolset.Bal.Symbols;
7
+
8
+ public static partial class BalSymbolDefinitions
9
+ {
10
+ public static readonly IntermediateSymbolDefinition WixMbaPrereqOptions = new IntermediateSymbolDefinition(
11
+ BalSymbolDefinitionType.WixMbaPrereqOptions.ToString(),
12
+ new[]
13
+ {
14
+ new IntermediateFieldDefinition(nameof(WixMbaPrereqOptionsSymbolFields.AlwaysInstallPrereqs), IntermediateFieldType.Number),
15
+ },
16
+ typeof(WixMbaPrereqOptionsSymbol));
17
+ }
18
+}
19
+
20
+namespace WixToolset.Bal.Symbols
21
+{
22
+ using WixToolset.Data;
23
+
24
+ public enum WixMbaPrereqOptionsSymbolFields
25
+ {
26
+ AlwaysInstallPrereqs,
27
+ }
28
+
29
+ public class WixMbaPrereqOptionsSymbol : IntermediateSymbol
30
+ {
31
+ public WixMbaPrereqOptionsSymbol() : base(BalSymbolDefinitions.WixMbaPrereqOptions, null, null)
32
+ {
33
+ }
34
+
35
+ public WixMbaPrereqOptionsSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(BalSymbolDefinitions.WixMbaPrereqOptions, sourceLineNumber, id)
36
+ {
37
+ }
38
+
39
+ public IntermediateField this[WixMbaPrereqOptionsSymbolFields index] => this.Fields[(int)index];
40
+
41
+ public int AlwaysInstallPrereqs
42
+ {
43
+ get => this.Fields[(int)WixMbaPrereqOptionsSymbolFields.AlwaysInstallPrereqs].AsNumber();
44
+ set => this.Set((int)WixMbaPrereqOptionsSymbolFields.AlwaysInstallPrereqs, value);
45
+ }
46
+ }
47
+}
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+159
-20
@@ -30,6 +30,8 @@ enum WIXSTDBA_STATE
30
WIXSTDBA_STATE_HELP,
31
WIXSTDBA_STATE_DETECTING,
32
WIXSTDBA_STATE_DETECTED,
33
+ WIXSTDBA_STATE_PLANNING_PREREQS,
34
+ WIXSTDBA_STATE_PLANNED_PREREQS,
35
WIXSTDBA_STATE_PLANNING,
36
WIXSTDBA_STATE_PLANNED,
37
WIXSTDBA_STATE_APPLYING,
@@ -49,6 +51,7 @@ enum WM_WIXSTDBA
51
WM_WIXSTDBA_APPLY_PACKAGES,
52
WM_WIXSTDBA_CHANGE_STATE,
53
WM_WIXSTDBA_SHOW_FAILURE,
54
+ WM_WIXSTDBA_PLAN_PREREQS,
55
};
56
57
// This enum must be kept in the same order as the vrgwzPageNames array.
@@ -217,9 +220,11 @@ public: // IBootstrapperApplication
220
: "A restart is required by the prerequisites but the user delayed it. The bootstrapper application will be reloaded after the computer is restarted.");
221
}
222
}
220
- else if (m_fPrereqInstalled)
223
+ else if (m_fPrereqInstalled || m_fPrereqSkipped)
224
{
222
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "The prerequisites were successfully installed. The bootstrapper application will be reloaded.");
225
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, m_fPrereqInstalled
226
+ ? "The prerequisites were successfully installed. The bootstrapper application will be reloaded."
227
+ : "The prerequisites were already installed. The bootstrapper application will be reloaded.");
228
*pAction = BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER;
229
m_pPrereqData->fCompleted = TRUE;
230
}
@@ -275,13 +280,16 @@ public: // IBootstrapperApplication
280
hr = S_OK;
281
}
282
278
- // If the UI should be visible, display it now and hide the splash screen
279
- if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display)
283
+ if (!m_fPreplanPrereqs)
284
{
281
- ::ShowWindow(m_pTheme->hwndParent, SW_SHOW);
282
- }
285
+ // If the UI should be visible, display it now and hide the splash screen
286
+ if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display)
287
+ {
288
+ ::ShowWindow(m_pTheme->hwndParent, SW_SHOW);
289
+ }
290
284
- m_pEngine->CloseSplashScreen();
291
+ m_pEngine->CloseSplashScreen();
292
+ }
293
294
return __super::OnDetectBegin(fCached, registrationType, cPackages, pfCancel);
295
}
@@ -331,24 +339,37 @@ public: // IBootstrapperApplication
339
if (fEvaluateConditions)
340
{
341
hrStatus = EvaluateConditions();
334
-
335
- if (FAILED(hrStatus))
336
- {
337
- fSkipToPlan = FALSE;
338
- }
342
}
343
344
SetState(WIXSTDBA_STATE_DETECTED, hrStatus);
345
343
- if (fSkipToPlan)
346
+ if (SUCCEEDED(hrStatus))
347
{
345
- ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action);
348
+ if (m_fPreplanPrereqs)
349
+ {
350
+ ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PREREQS, 0, BOOTSTRAPPER_ACTION_INSTALL);
351
+ }
352
+ else if (fSkipToPlan)
353
+ {
354
+ ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action);
355
+ }
356
}
357
358
return hr;
359
}
360
361
362
+ virtual STDMETHODIMP OnPlanBegin(
363
+ __in DWORD cPackages,
364
+ __in BOOL* pfCancel
365
+ )
366
+ {
367
+ m_fPrereqPackagePlanned = FALSE;
368
+
369
+ return __super::OnPlanBegin(cPackages, pfCancel);
370
+ }
371
+
372
+
373
virtual STDMETHODIMP OnPlanRelatedBundleType(
374
__in_z LPCWSTR wzBundleId,
375
__in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE recommendedType,
@@ -484,24 +505,97 @@ public: // IBootstrapperApplication
505
}
506
507
508
+ virtual STDMETHODIMP OnPlannedPackage(
509
+ __in_z LPCWSTR wzPackageId,
510
+ __in BOOTSTRAPPER_ACTION_STATE execute,
511
+ __in BOOTSTRAPPER_ACTION_STATE rollback,
512
+ __in BOOL fPlannedCache,
513
+ __in BOOL fPlannedUncache
514
+ )
515
+ {
516
+ if (m_fPrereq && BOOTSTRAPPER_ACTION_STATE_NONE != execute)
517
+ {
518
+ m_fPrereqPackagePlanned = TRUE;
519
+ }
520
+
521
+ return __super::OnPlannedPackage(wzPackageId, execute, rollback, fPlannedCache, fPlannedUncache);
522
+ }
523
+
524
+
525
virtual STDMETHODIMP OnPlanComplete(
526
__in HRESULT hrStatus
527
)
528
{
529
HRESULT hr = S_OK;
530
+ BOOL fPlannedPrereqs = WIXSTDBA_STATE_PLANNING_PREREQS == m_state;
531
+ WIXSTDBA_STATE completedState = WIXSTDBA_STATE_PLANNED;
532
+ BOOL fApply = TRUE;
533
493
- SetState(WIXSTDBA_STATE_PLANNED, hrStatus);
534
+ if (fPlannedPrereqs)
535
+ {
536
+ if (SUCCEEDED(hrStatus) && !m_fPrereqPackagePlanned)
537
+ {
538
+ // Nothing to do, so close and let the parent BA take over.
539
+ m_fPrereqSkipped = TRUE;
540
+ SetState(WIXSTDBA_STATE_APPLIED, S_OK);
541
+ ExitFunction();
542
+ }
543
+ else if (BOOTSTRAPPER_ACTION_HELP == m_command.action)
544
+ {
545
+ // If prereq packages were planned then the managed BA probably can't be loaded, so show the help from this BA.
546
495
- if (SUCCEEDED(hrStatus))
547
+ // Need to force the state change since normally moving backwards is prevented.
548
+ ::PostMessageW(m_hWnd, WM_WIXSTDBA_CHANGE_STATE, 0, WIXSTDBA_STATE_HELP);
549
+
550
+ ::PostMessageW(m_hWnd, WM_WIXSTDBA_SHOW_HELP, 0, 0);
551
+
552
+ ExitFunction();
553
+ }
554
+
555
+ completedState = WIXSTDBA_STATE_PLANNED_PREREQS;
556
+ }
557
+
558
+ SetState(completedState, hrStatus);
559
+
560
+ if (FAILED(hrStatus))
561
+ {
562
+ ExitFunction();
563
+ }
564
+
565
+ if (fPlannedPrereqs)
566
+ {
567
+ // If the UI should be visible, display it now and hide the splash screen
568
+ if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display)
569
+ {
570
+ ::ShowWindow(m_pTheme->hwndParent, SW_SHOW);
571
+ }
572
+
573
+ m_pEngine->CloseSplashScreen();
574
+
575
+ fApply = BOOTSTRAPPER_DISPLAY_FULL > m_command.display ||
576
+ BOOTSTRAPPER_RESUME_TYPE_REBOOT == m_command.resumeType;
577
+ }
578
+
579
+ if (fApply)
580
{
581
::PostMessageW(m_hWnd, WM_WIXSTDBA_APPLY_PACKAGES, 0, 0);
582
}
583
584
+ LExit:
585
+ return hr;
586
+ }
587
+
588
+
589
+ virtual STDMETHODIMP OnApplyBegin(
590
+ __in DWORD dwPhaseCount,
591
+ __in BOOL* pfCancel
592
+ )
593
+ {
594
m_fStartedExecution = FALSE;
595
m_dwCalculatedCacheProgress = 0;
596
m_dwCalculatedExecuteProgress = 0;
597
504
- return hr;
598
+ return __super::OnApplyBegin(dwPhaseCount, pfCancel);
599
}
600
601
@@ -2149,6 +2243,7 @@ private:
2243
BOOL fRet = FALSE;
2244
MSG msg = { };
2245
DWORD dwQuit = 0;
2246
+ WM_WIXSTDBA firstAction = WM_WIXSTDBA_DETECT_PACKAGES;
2247
2248
// Initialize COM and theme.
2249
hr = ::CoInitialize(NULL);
@@ -2169,15 +2264,21 @@ private:
2264
if (FAILED(pThis->m_hrFinal))
2265
{
2266
pThis->SetState(WIXSTDBA_STATE_FAILED, hr);
2172
- ::PostMessageW(pThis->m_hWnd, WM_WIXSTDBA_SHOW_FAILURE, 0, 0);
2267
+ firstAction = WM_WIXSTDBA_SHOW_FAILURE;
2268
}
2269
else
2270
{
2271
// Okay, we're ready for packages now.
2272
pThis->SetState(WIXSTDBA_STATE_INITIALIZED, hr);
2178
- ::PostMessageW(pThis->m_hWnd, BOOTSTRAPPER_ACTION_HELP == pThis->m_command.action ? WM_WIXSTDBA_SHOW_HELP : WM_WIXSTDBA_DETECT_PACKAGES, 0, 0);
2273
+
2274
+ if (!pThis->m_fPreplanPrereqs && BOOTSTRAPPER_ACTION_HELP == pThis->m_command.action)
2275
+ {
2276
+ firstAction = WM_WIXSTDBA_SHOW_HELP;
2277
+ }
2278
}
2279
2280
+ ::PostMessageW(pThis->m_hWnd, firstAction, 0, 0);
2281
+
2282
// message pump
2283
while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
2284
{
@@ -2647,7 +2748,7 @@ private:
2748
2749
// Calculate the window style based on the theme style and command display value.
2750
dwWindowStyle = m_pTheme->dwStyle;
2650
- if (BOOTSTRAPPER_DISPLAY_NONE >= m_command.display)
2751
+ if (BOOTSTRAPPER_DISPLAY_NONE >= m_command.display || m_fPreplanPrereqs)
2752
{
2753
dwWindowStyle &= ~WS_VISIBLE;
2754
}
@@ -2850,6 +2951,10 @@ private:
2951
pBA->OnShowFailure();
2952
return 0;
2953
2954
+ case WM_WIXSTDBA_PLAN_PREREQS:
2955
+ pBA->OnPlanPrereqs(static_cast<BOOTSTRAPPER_ACTION>(lParam));
2956
+ return 0;
2957
+
2958
case WM_THMUTIL_CONTROL_WM_COMMAND:
2959
return pBA->OnThemeControlWmCommand(reinterpret_cast<THEME_CONTROLWMCOMMAND_ARGS*>(wParam), reinterpret_cast<THEME_CONTROLWMCOMMAND_RESULTS*>(lParam));
2960
@@ -3140,6 +3245,30 @@ private:
3245
}
3246
3247
3248
+ //
3249
+ // OnPlanPrereqs - preplan the packages to see if the preqba can be skipped.
3250
+ //
3251
+ void OnPlanPrereqs(
3252
+ __in BOOTSTRAPPER_ACTION action
3253
+ )
3254
+ {
3255
+ HRESULT hr = S_OK;
3256
+
3257
+ m_plannedAction = action;
3258
+
3259
+ SetState(WIXSTDBA_STATE_PLANNING_PREREQS, hr);
3260
+
3261
+ hr = m_pEngine->Plan(action);
3262
+ BalExitOnFailure(hr, "Failed to start planning prereq packages.");
3263
+
3264
+ LExit:
3265
+ if (FAILED(hr))
3266
+ {
3267
+ SetState(WIXSTDBA_STATE_PLANNING_PREREQS, hr);
3268
+ }
3269
+ }
3270
+
3271
+
3272
//
3273
// OnApply - apply the packages.
3274
//
@@ -3838,6 +3967,8 @@ LExit:
3967
break;
3968
3969
case WIXSTDBA_STATE_DETECTED: __fallthrough;
3970
+ case WIXSTDBA_STATE_PLANNING_PREREQS: __fallthrough;
3971
+ case WIXSTDBA_STATE_PLANNED_PREREQS: __fallthrough;
3972
case WIXSTDBA_STATE_PLANNING: __fallthrough;
3973
case WIXSTDBA_STATE_PLANNED: __fallthrough;
3974
case WIXSTDBA_STATE_APPLYING: __fallthrough;
@@ -3874,6 +4005,8 @@ LExit:
4005
break;
4006
4007
case WIXSTDBA_STATE_DETECTED:
4008
+ case WIXSTDBA_STATE_PLANNING_PREREQS: __fallthrough;
4009
+ case WIXSTDBA_STATE_PLANNED_PREREQS: __fallthrough;
4010
switch (m_command.action)
4011
{
4012
case BOOTSTRAPPER_ACTION_INSTALL:
@@ -4124,7 +4257,10 @@ public:
4257
4258
m_pPrereqData = pPrereqData;
4259
m_fPrereq = NULL != pPrereqData;
4260
+ m_fPreplanPrereqs = m_fPrereq && m_pPrereqData->fAlwaysInstallPrereqs;
4261
+ m_fPrereqPackagePlanned = FALSE;
4262
m_fPrereqInstalled = FALSE;
4263
+ m_fPrereqSkipped = FALSE;
4264
4265
pEngine->AddRef();
4266
m_pEngine = pEngine;
@@ -4405,7 +4541,10 @@ private:
4541
4542
PREQBA_DATA* m_pPrereqData;
4543
BOOL m_fPrereq;
4544
+ BOOL m_fPreplanPrereqs;
4545
+ BOOL m_fPrereqPackagePlanned;
4546
BOOL m_fPrereqInstalled;
4547
+ BOOL m_fPrereqSkipped;
4548
4549
ITaskbarList3* m_pTaskbarList;
4550
UINT m_uTaskbarButtonCreatedMessage;
src/ext/Bal/wixstdba/inc/preqba.h
+1
@@ -5,6 +5,7 @@
5
struct PREQBA_DATA
6
{
7
HRESULT hrHostInitialization;
8
+ BOOL fAlwaysInstallPrereqs;
9
BOOL fCompleted;
10
};
11
src/test/burn/TestBA/TestBA.cs
+7
@@ -146,6 +146,13 @@ namespace WixToolset.Test.BA
146
147
this.wait.WaitOne();
148
149
+ if (this.action == LaunchAction.Help)
150
+ {
151
+ this.Log("This is a BA for automated testing");
152
+ this.Engine.Quit(0);
153
+ return;
154
+ }
155
+
156
this.redetectRemaining = redetectCount;
157
for (int i = -1; i < redetectCount; i++)
158
{
src/test/burn/TestData/PrereqBaTests/BundleA/BundleA.wixproj
+4
-1
@@ -13,9 +13,12 @@
13
</ItemGroup>
14
<ItemGroup>
15
<ProjectReference Include="..\PackageA\PackageA.wixproj" />
16
- <ProjectReference Include="..\PackageF\PackageF.wixproj" />
16
+ <ProjectReference Include="..\PackageC\PackageC.wixproj" />
17
+ <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
18
+ <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
19
</ItemGroup>
20
<ItemGroup>
21
<PackageReference Include="WixToolset.Bal.wixext" />
22
+ <PackageReference Include="WixToolset.Util.wixext" />
23
</ItemGroup>
24
</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleA/BundleA.wxs
+10
-2
@@ -1,22 +1,30 @@
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" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
4
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
<Fragment>
6
<BootstrapperApplication Id="BrokenDnc">
7
<Payload SourceFile="!(bindpath.dncx86)\TestBA.deps.json" />
8
<Payload SourceFile="!(bindpath.dncx86)\TestBA.dll" bal:BAFactoryAssembly="yes" />
9
+ <Payload Name="good.runtimeconfig.json" SourceFile="!(bindpath.dncx86)\TestBA.runtimeconfig.json" />
10
<Payload Name="TestBA.runtimeconfig.json" SourceFile="bad.runtimeconfig.json" />
11
<Payload SourceFile="!(bindpath.dncx86)\mbanative.dll" />
12
<Payload SourceFile="!(bindpath.dncx86)\WixToolset.Mba.Core.dll" />
13
+ <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
14
<bal:WixDotNetCoreBootstrapperApplicationHost />
15
</BootstrapperApplication>
16
</Fragment>
17
18
<Fragment>
19
+ <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.runtimeconfig.json" Result="exists" />
20
+ <Variable Name="CAUSEINFINITELOOP" bal:Overridable="yes" />
21
+
22
<PackageGroup Id="BundlePackages">
23
+ <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
24
+ Permanent="yes" DetectCondition="NOT GoodConfigPresent OR CAUSEINFINITELOOP = 1"
25
+ InstallArguments=""[BARuntimeDirectory]TestBA.runtimeconfig.json" "[BARuntimeDirectory]good.runtimeconfig.json" "[BARuntimeDirectory]bad.runtimeconfig.json"" />
26
<MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" bal:PrereqPackage="yes" Permanent="yes" />
19
- <MsiPackage Id="PackageF" SourceFile="$(var.PackageF.TargetPath)" Cache="force" />
27
+ <MsiPackage Id="PackageC" SourceFile="$(var.PackageC.TargetPath)" Cache="force" />
28
</PackageGroup>
29
</Fragment>
30
</Wix>
src/test/burn/TestData/PrereqBaTests/BundleB/BundleB.wixproj
+4
-1
@@ -13,9 +13,12 @@
13
</ItemGroup>
14
<ItemGroup>
15
<ProjectReference Include="..\PackageB\PackageB.wixproj" />
16
- <ProjectReference Include="..\PackageF\PackageF.wixproj" />
16
+ <ProjectReference Include="..\PackageC\PackageC.wixproj" />
17
+ <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
18
+ <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
19
</ItemGroup>
20
<ItemGroup>
21
<PackageReference Include="WixToolset.Bal.wixext" />
22
+ <PackageReference Include="WixToolset.Util.wixext" />
23
</ItemGroup>
24
</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleB/BundleB.wxs
+10
-2
@@ -1,21 +1,29 @@
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" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
4
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
<Fragment>
6
<BootstrapperApplication Id="BrokenMba">
7
+ <Payload Name="good.config" SourceFile="!(bindpath.net2x86)\TestBA.BootstrapperCore.config" />
8
<Payload Name="WixToolset.Mba.Host.config" SourceFile="bad.config" />
9
<Payload SourceFile="!(bindpath.net2x86)\TestBA.dll" />
10
<Payload SourceFile="!(bindpath.net2x86)\mbanative.dll" />
11
<Payload SourceFile="!(bindpath.net2x86)\WixToolset.Mba.Core.dll" />
12
+ <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
13
<bal:WixManagedBootstrapperApplicationHost />
14
</BootstrapperApplication>
15
</Fragment>
16
17
<Fragment>
18
+ <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.config" Result="exists" />
19
+ <Variable Name="CAUSEINFINITELOOP" bal:Overridable="yes" />
20
+
21
<PackageGroup Id="BundlePackages">
22
+ <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
23
+ Permanent="yes" DetectCondition="NOT GoodConfigPresent OR CAUSEINFINITELOOP = 1"
24
+ InstallArguments=""[BARuntimeDirectory]WixToolset.Mba.Host.config" "[BARuntimeDirectory]good.config" "[BARuntimeDirectory]bad.config"" />
25
<MsiPackage Id="PackageB" SourceFile="$(var.PackageB.TargetPath)" bal:PrereqPackage="yes" Permanent="yes" />
18
- <MsiPackage Id="PackageF" SourceFile="$(var.PackageF.TargetPath)" />
26
+ <MsiPackage Id="PackageC" SourceFile="$(var.PackageC.TargetPath)" />
27
</PackageGroup>
28
</Fragment>
29
</Wix>
src/test/burn/TestData/PrereqBaTests/BundleC/BundleC.wixproj
new
+24
@@ -0,0 +1,24 @@
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
+ <BA>BrokenDncAlwaysPrereq</BA>
6
+ <UpgradeCode>{D2763AB7-979B-485C-AE52-DD03C23CCB93}</UpgradeCode>
7
+ </PropertyGroup>
8
+ <ItemGroup>
9
+ <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10
+ </ItemGroup>
11
+ <ItemGroup>
12
+ <BindInputPaths Include="$(BaseOutputPath)$(Configuration)\net6.0-windows\win-x86" BindName="dncx86" />
13
+ </ItemGroup>
14
+ <ItemGroup>
15
+ <ProjectReference Include="..\PackageA\PackageA.wixproj" />
16
+ <ProjectReference Include="..\PackageC\PackageC.wixproj" />
17
+ <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
18
+ <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
19
+ </ItemGroup>
20
+ <ItemGroup>
21
+ <PackageReference Include="WixToolset.Bal.wixext" />
22
+ <PackageReference Include="WixToolset.Util.wixext" />
23
+ </ItemGroup>
24
+</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleC/BundleC.wxs
new
+30
@@ -0,0 +1,30 @@
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" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
+ <Fragment>
6
+ <BootstrapperApplication Id="BrokenDncAlwaysPrereq">
7
+ <Payload SourceFile="!(bindpath.dncx86)\TestBA.deps.json" />
8
+ <Payload SourceFile="!(bindpath.dncx86)\TestBA.dll" bal:BAFactoryAssembly="yes" />
9
+ <Payload Name="good.runtimeconfig.json" SourceFile="!(bindpath.dncx86)\TestBA.runtimeconfig.json" />
10
+ <Payload Name="TestBA.runtimeconfig.json" SourceFile="bad.runtimeconfig.json" />
11
+ <Payload SourceFile="!(bindpath.dncx86)\mbanative.dll" />
12
+ <Payload SourceFile="!(bindpath.dncx86)\WixToolset.Mba.Core.dll" />
13
+ <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
14
+ <bal:WixDotNetCoreBootstrapperApplicationHost AlwaysInstallPrereqs="yes" />
15
+ </BootstrapperApplication>
16
+ </Fragment>
17
+
18
+ <Fragment>
19
+ <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.runtimeconfig.json" Result="exists" />
20
+ <Variable Name="CAUSEINFINITELOOP" bal:Overridable="yes" />
21
+
22
+ <PackageGroup Id="BundlePackages">
23
+ <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
24
+ Permanent="yes" DetectCondition="NOT GoodConfigPresent OR CAUSEINFINITELOOP = 1"
25
+ InstallArguments=""[BARuntimeDirectory]TestBA.runtimeconfig.json" "[BARuntimeDirectory]good.runtimeconfig.json" "[BARuntimeDirectory]bad.runtimeconfig.json"" />
26
+ <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" bal:PrereqPackage="yes" Permanent="yes" />
27
+ <MsiPackage Id="PackageC" SourceFile="$(var.PackageC.TargetPath)" Cache="force" />
28
+ </PackageGroup>
29
+ </Fragment>
30
+</Wix>
src/test/burn/TestData/PrereqBaTests/BundleC/bad.runtimeconfig.json
new
+10
@@ -0,0 +1,10 @@
1
+{
2
+ "runtimeOptions": {
3
+ "tfm": "net5.5",
4
+ "rollForward": "Disable",
5
+ "framework": {
6
+ "name": "Microsoft.WindowsDesktop.App",
7
+ "version": "5.5.0"
8
+ }
9
+ }
10
+}
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleD/BundleD.wixproj
new
+24
@@ -0,0 +1,24 @@
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
+ <BA>BrokenMbaAlwaysPrereq</BA>
6
+ <UpgradeCode>{415CA128-60E1-4D16-ACE8-A1D43E98B997}</UpgradeCode>
7
+ </PropertyGroup>
8
+ <ItemGroup>
9
+ <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10
+ </ItemGroup>
11
+ <ItemGroup>
12
+ <BindInputPaths Include="$(BaseOutputPath)$(Configuration)\net35\win-x86" BindName="net2x86" />
13
+ </ItemGroup>
14
+ <ItemGroup>
15
+ <ProjectReference Include="..\PackageB\PackageB.wixproj" />
16
+ <ProjectReference Include="..\PackageC\PackageC.wixproj" />
17
+ <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
18
+ <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
19
+ </ItemGroup>
20
+ <ItemGroup>
21
+ <PackageReference Include="WixToolset.Bal.wixext" />
22
+ <PackageReference Include="WixToolset.Util.wixext" />
23
+ </ItemGroup>
24
+</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleD/BundleD.wxs
new
+29
@@ -0,0 +1,29 @@
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" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
+ <Fragment>
6
+ <BootstrapperApplication Id="BrokenMbaAlwaysPrereq">
7
+ <Payload Name="good.config" SourceFile="!(bindpath.net2x86)\TestBA.BootstrapperCore.config" />
8
+ <Payload Name="WixToolset.Mba.Host.config" SourceFile="bad.config" />
9
+ <Payload SourceFile="!(bindpath.net2x86)\TestBA.dll" />
10
+ <Payload SourceFile="!(bindpath.net2x86)\mbanative.dll" />
11
+ <Payload SourceFile="!(bindpath.net2x86)\WixToolset.Mba.Core.dll" />
12
+ <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
13
+ <bal:WixManagedBootstrapperApplicationHost AlwaysInstallPrereqs="yes" />
14
+ </BootstrapperApplication>
15
+ </Fragment>
16
+
17
+ <Fragment>
18
+ <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.config" Result="exists" />
19
+ <Variable Name="CAUSEINFINITELOOP" bal:Overridable="yes" />
20
+
21
+ <PackageGroup Id="BundlePackages">
22
+ <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
23
+ Permanent="yes" DetectCondition="NOT GoodConfigPresent OR CAUSEINFINITELOOP = 1"
24
+ InstallArguments=""[BARuntimeDirectory]WixToolset.Mba.Host.config" "[BARuntimeDirectory]good.config" "[BARuntimeDirectory]bad.config"" />
25
+ <MsiPackage Id="PackageB" SourceFile="$(var.PackageB.TargetPath)" bal:PrereqPackage="yes" Permanent="yes" />
26
+ <MsiPackage Id="PackageC" SourceFile="$(var.PackageC.TargetPath)" />
27
+ </PackageGroup>
28
+ </Fragment>
29
+</Wix>
src/test/burn/TestData/PrereqBaTests/BundleD/bad.config
new
+17
@@ -0,0 +1,17 @@
1
+<?xml version="1.0" encoding="utf-8" ?>
2
+<!-- 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. -->
3
+
4
+
5
+<configuration>
6
+ <configSections>
7
+ <sectionGroup name="wix.bootstrapper" type="WixToolset.Mba.Host.BootstrapperSectionGroup, WixToolset.Mba.Host">
8
+ <section name="host" type="WixToolset.Mba.Host.HostSection, WixToolset.Mba.Host" />
9
+ </sectionGroup>
10
+ </configSections>
11
+ <startup>
12
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v5.8" />
13
+ </startup>
14
+ <wix.bootstrapper>
15
+ <host assemblyName="TestBA" />
16
+ </wix.bootstrapper>
17
+</configuration>
src/test/burn/TestData/PrereqBaTests/BundleE/BundleE.wixproj
new
+22
@@ -0,0 +1,22 @@
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
+ <BA>DncAlwaysPrereq</BA>
6
+ <UpgradeCode>{2F61ECD8-C28B-4FF9-9609-0E9633716CF9}</UpgradeCode>
7
+ </PropertyGroup>
8
+ <ItemGroup>
9
+ <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10
+ </ItemGroup>
11
+ <ItemGroup>
12
+ <BindInputPaths Include="$(BaseOutputPath)$(Configuration)\net6.0-windows\win-x86" BindName="dncx86" />
13
+ </ItemGroup>
14
+ <ItemGroup>
15
+ <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
16
+ <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
17
+ </ItemGroup>
18
+ <ItemGroup>
19
+ <PackageReference Include="WixToolset.Bal.wixext" />
20
+ <PackageReference Include="WixToolset.Util.wixext" />
21
+ </ItemGroup>
22
+</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleE/BundleE.wxs
new
+26
@@ -0,0 +1,26 @@
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" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
+ <Fragment>
6
+ <BootstrapperApplication Id="DncAlwaysPrereq">
7
+ <Payload SourceFile="!(bindpath.dncx86)\TestBA.deps.json" />
8
+ <Payload SourceFile="!(bindpath.dncx86)\TestBA.dll" bal:BAFactoryAssembly="yes" />
9
+ <Payload SourceFile="!(bindpath.dncx86)\TestBA.runtimeconfig.json" />
10
+ <Payload SourceFile="!(bindpath.dncx86)\mbanative.dll" />
11
+ <Payload SourceFile="!(bindpath.dncx86)\WixToolset.Mba.Core.dll" />
12
+ <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
13
+ <bal:WixDotNetCoreBootstrapperApplicationHost AlwaysInstallPrereqs="yes" />
14
+ </BootstrapperApplication>
15
+ </Fragment>
16
+
17
+ <Fragment>
18
+ <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.runtimeconfig.json" Result="exists" />
19
+
20
+ <PackageGroup Id="BundlePackages">
21
+ <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
22
+ Permanent="yes" DetectCondition="NOT GoodConfigPresent"
23
+ InstallArguments=""[BARuntimeDirectory]TestBA.runtimeconfig.json" "[BARuntimeDirectory]good.runtimeconfig.json" "[BARuntimeDirectory]bad.runtimeconfig.json"" />
24
+ </PackageGroup>
25
+ </Fragment>
26
+</Wix>
src/test/burn/TestData/PrereqBaTests/PackageC/PackageC.wixproj
renamed
+1
-4
@@ -4,9 +4,6 @@
4
<UpgradeCode>{7DEEE928-CD7F-49AD-8000-2ED6339D8A78}</UpgradeCode>
5
</PropertyGroup>
6
<ItemGroup>
7
- <Compile Include="..\..\Templates\PackageFail.wxs" Link="PackageFail.wxs" />
8
- </ItemGroup>
9
- <ItemGroup>
10
- <PackageReference Include="WixToolset.Util.wixext" />
7
+ <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
8
</ItemGroup>
9
</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/PrereqBaf/PrereqBaf.cpp
new
+79
@@ -0,0 +1,79 @@
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
+#include "precomp.h"
4
+#include "BalBaseBAFunctions.h"
5
+#include "BalBaseBAFunctionsProc.h"
6
+
7
+class CPrereqBaf : public CBalBaseBAFunctions
8
+{
9
+public: // IBAFunctions
10
+
11
+public: //IBootstrapperApplication
12
+
13
+ virtual STDMETHODIMP OnDetectBegin(
14
+ __in BOOL /*fCached*/,
15
+ __in BOOTSTRAPPER_REGISTRATION_TYPE /*registrationType*/,
16
+ __in DWORD /*cPackages*/,
17
+ __inout BOOL* /*pfCancel*/
18
+ )
19
+ {
20
+ HRESULT hr = S_OK;
21
+
22
+ hr = m_pEngine->SetVariableString(L"BARuntimeDirectory", m_command.wzBootstrapperWorkingFolder, FALSE);
23
+ ExitOnFailure(hr, "Failed to set BARuntimeDirectory");
24
+
25
+ LExit:
26
+ return hr;
27
+ }
28
+
29
+private:
30
+
31
+public:
32
+ //
33
+ // Constructor - initialize member variables.
34
+ //
35
+ CPrereqBaf(
36
+ __in HMODULE hModule,
37
+ __in IBootstrapperEngine* pEngine,
38
+ __in const BA_FUNCTIONS_CREATE_ARGS* pArgs
39
+ ) : CBalBaseBAFunctions(hModule, pEngine, pArgs)
40
+ {
41
+ }
42
+
43
+ //
44
+ // Destructor - release member variables.
45
+ //
46
+ ~CPrereqBaf()
47
+ {
48
+ }
49
+
50
+private:
51
+};
52
+
53
+
54
+HRESULT WINAPI CreateBAFunctions(
55
+ __in HMODULE hModule,
56
+ __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
57
+ __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
58
+ )
59
+{
60
+ HRESULT hr = S_OK;
61
+ CPrereqBaf* pBAFunctions = NULL;
62
+ IBootstrapperEngine* pEngine = NULL;
63
+
64
+ hr = BalInitializeFromCreateArgs(pArgs->pBootstrapperCreateArgs, &pEngine);
65
+ ExitOnFailure(hr, "Failed to initialize Bal.");
66
+
67
+ pBAFunctions = new CPrereqBaf(hModule, pEngine, pArgs);
68
+ ExitOnNull(pBAFunctions, hr, E_OUTOFMEMORY, "Failed to create new CPrereqBaf object.");
69
+
70
+ pResults->pfnBAFunctionsProc = BalBaseBAFunctionsProc;
71
+ pResults->pvBAFunctionsProcContext = pBAFunctions;
72
+ pBAFunctions = NULL;
73
+
74
+LExit:
75
+ ReleaseObject(pBAFunctions);
76
+ ReleaseObject(pEngine);
77
+
78
+ return hr;
79
+}
src/test/burn/TestData/PrereqBaTests/PrereqBaf/PrereqBaf.def
new
+6
@@ -0,0 +1,6 @@
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
+EXPORTS
5
+ BAFunctionsCreate
6
+ BAFunctionsDestroy
src/test/burn/TestData/PrereqBaTests/PrereqBaf/PrereqBaf.vcxproj
new
+66
@@ -0,0 +1,66 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+<Project DefaultTargets="Build" Toolsxmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5
+ <ItemGroup Label="ProjectConfigurations">
6
+ <ProjectConfiguration Include="Debug|ARM64">
7
+ <Configuration>Debug</Configuration>
8
+ <Platform>ARM64</Platform>
9
+ </ProjectConfiguration>
10
+ <ProjectConfiguration Include="Release|ARM64">
11
+ <Configuration>Release</Configuration>
12
+ <Platform>ARM64</Platform>
13
+ </ProjectConfiguration>
14
+ <ProjectConfiguration Include="Debug|Win32">
15
+ <Configuration>Debug</Configuration>
16
+ <Platform>Win32</Platform>
17
+ </ProjectConfiguration>
18
+ <ProjectConfiguration Include="Release|Win32">
19
+ <Configuration>Release</Configuration>
20
+ <Platform>Win32</Platform>
21
+ </ProjectConfiguration>
22
+ <ProjectConfiguration Include="Debug|x64">
23
+ <Configuration>Debug</Configuration>
24
+ <Platform>x64</Platform>
25
+ </ProjectConfiguration>
26
+ <ProjectConfiguration Include="Release|x64">
27
+ <Configuration>Release</Configuration>
28
+ <Platform>x64</Platform>
29
+ </ProjectConfiguration>
30
+ </ItemGroup>
31
+
32
+ <PropertyGroup Label="Globals">
33
+ <ProjectGuid>{C0A11DDB-6CCE-44EC-88FD-93910C2916E3}</ProjectGuid>
34
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
35
+ <CharacterSet>Unicode</CharacterSet>
36
+ <TargetName>PrereqBaf</TargetName>
37
+ <ProjectModuleDefinitionFile>PrereqBaf.def</ProjectModuleDefinitionFile>
38
+ <IsWixTestProject>true</IsWixTestProject>
39
+ </PropertyGroup>
40
+
41
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
42
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
43
+
44
+ <PropertyGroup>
45
+ <ProjectAdditionalLinkLibraries>comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib;wininet.lib</ProjectAdditionalLinkLibraries>
46
+ </PropertyGroup>
47
+
48
+ <ItemGroup>
49
+ <ClCompile Include="precomp.cpp">
50
+ <PrecompiledHeader>Create</PrecompiledHeader>
51
+ </ClCompile>
52
+ <ClCompile Include="PrereqBaf.cpp" />
53
+ </ItemGroup>
54
+ <ItemGroup>
55
+ <ClInclude Include="precomp.h" />
56
+ </ItemGroup>
57
+ <ItemGroup>
58
+ <None Include="PrereqBaf.def" />
59
+ </ItemGroup>
60
+
61
+ <ItemGroup>
62
+ <PackageReference Include="WixToolset.BalUtil" />
63
+ </ItemGroup>
64
+
65
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
66
+</Project>
src/test/burn/TestData/PrereqBaTests/PrereqBaf/precomp.cpp
new
+48
@@ -0,0 +1,48 @@
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
+#include "precomp.h"
4
+
5
+static HINSTANCE vhInstance = NULL;
6
+
7
+extern "C" BOOL WINAPI DllMain(
8
+ IN HINSTANCE hInstance,
9
+ IN DWORD dwReason,
10
+ IN LPVOID /* pvReserved */
11
+ )
12
+{
13
+ switch (dwReason)
14
+ {
15
+ case DLL_PROCESS_ATTACH:
16
+ ::DisableThreadLibraryCalls(hInstance);
17
+ vhInstance = hInstance;
18
+ break;
19
+
20
+ case DLL_PROCESS_DETACH:
21
+ vhInstance = NULL;
22
+ break;
23
+ }
24
+
25
+ return TRUE;
26
+}
27
+
28
+extern "C" HRESULT WINAPI BAFunctionsCreate(
29
+ __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
30
+ __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
31
+ )
32
+{
33
+ HRESULT hr = S_OK;
34
+
35
+ hr = CreateBAFunctions(vhInstance, pArgs, pResults);
36
+ BalExitOnFailure(hr, "Failed to create BAFunctions interface.");
37
+
38
+LExit:
39
+ return hr;
40
+}
41
+
42
+extern "C" void WINAPI BAFunctionsDestroy(
43
+ __in const BA_FUNCTIONS_DESTROY_ARGS* /*pArgs*/,
44
+ __inout BA_FUNCTIONS_DESTROY_RESULTS* /*pResults*/
45
+ )
46
+{
47
+ BalUninitialize();
48
+}
src/test/burn/TestData/PrereqBaTests/PrereqBaf/precomp.h
new
+31
@@ -0,0 +1,31 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+#include <windows.h>
6
+#include <msiquery.h>
7
+#include <objbase.h>
8
+#include <shlobj.h>
9
+#include <shlwapi.h>
10
+#include <stdlib.h>
11
+#include <strsafe.h>
12
+#include <CommCtrl.h>
13
+
14
+#include "dutil.h"
15
+#include "dictutil.h"
16
+#include "fileutil.h"
17
+#include "locutil.h"
18
+#include "pathutil.h"
19
+#include "strutil.h"
20
+
21
+#include "BalBaseBootstrapperApplication.h"
22
+#include "balutil.h"
23
+
24
+#include "BAFunctions.h"
25
+#include "IBAFunctions.h"
26
+
27
+HRESULT WINAPI CreateBAFunctions(
28
+ __in HMODULE hModule,
29
+ __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
30
+ __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
31
+ );
src/test/burn/TestData/PrereqBaTests/ReplaceConfig/ReplaceConfig.cpp
new
+33
@@ -0,0 +1,33 @@
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
+#include "precomp.h"
4
+
5
+int __cdecl wmain(
6
+ __in int argc,
7
+ __in LPWSTR argv[]
8
+ )
9
+{
10
+ HRESULT hr = S_OK;
11
+ DWORD dwExitCode = 0;
12
+ LPCWSTR wzDestinationFile = argc > 1 ? argv[1] : NULL;
13
+ LPCWSTR wzGoodFile = argc > 2 ? argv[2] : NULL;
14
+ LPCWSTR wzBadFile = argc > 3 ? argv[3] : NULL;
15
+
16
+ if (argc != 4)
17
+ {
18
+ ExitWithRootFailure(hr, E_INVALIDARG, "Invalid args");
19
+ }
20
+
21
+ if (!::MoveFileW(wzDestinationFile, wzBadFile))
22
+ {
23
+ ExitWithLastError(hr, "Failed to move bad file");
24
+ }
25
+
26
+ if (!::MoveFileW(wzGoodFile, wzDestinationFile))
27
+ {
28
+ ExitWithLastError(hr, "Failed to move good file");
29
+ }
30
+
31
+LExit:
32
+ return FAILED(hr) ? (int)hr : (int)dwExitCode;
33
+}
src/test/burn/TestData/PrereqBaTests/ReplaceConfig/ReplaceConfig.vcxproj
new
+63
@@ -0,0 +1,63 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+<Project DefaultTargets="Build" Toolsxmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5
+ <ItemGroup Label="ProjectConfigurations">
6
+ <ProjectConfiguration Include="Debug|ARM64">
7
+ <Configuration>Debug</Configuration>
8
+ <Platform>ARM64</Platform>
9
+ </ProjectConfiguration>
10
+ <ProjectConfiguration Include="Release|ARM64">
11
+ <Configuration>Release</Configuration>
12
+ <Platform>ARM64</Platform>
13
+ </ProjectConfiguration>
14
+ <ProjectConfiguration Include="Debug|Win32">
15
+ <Configuration>Debug</Configuration>
16
+ <Platform>Win32</Platform>
17
+ </ProjectConfiguration>
18
+ <ProjectConfiguration Include="Release|Win32">
19
+ <Configuration>Release</Configuration>
20
+ <Platform>Win32</Platform>
21
+ </ProjectConfiguration>
22
+ <ProjectConfiguration Include="Debug|x64">
23
+ <Configuration>Debug</Configuration>
24
+ <Platform>x64</Platform>
25
+ </ProjectConfiguration>
26
+ <ProjectConfiguration Include="Release|x64">
27
+ <Configuration>Release</Configuration>
28
+ <Platform>x64</Platform>
29
+ </ProjectConfiguration>
30
+ </ItemGroup>
31
+
32
+ <PropertyGroup Label="Globals">
33
+ <ProjectGuid>{3D10A07D-3321-4F8E-B884-951F8FB5D636}</ProjectGuid>
34
+ <ConfigurationType>Application</ConfigurationType>
35
+ <ProjectSubSystem>Console</ProjectSubSystem>
36
+ <CharacterSet>Unicode</CharacterSet>
37
+ <TargetName>ReplaceConfig</TargetName>
38
+ <IsWixTestProject>true</IsWixTestProject>
39
+ </PropertyGroup>
40
+
41
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
42
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
43
+
44
+ <PropertyGroup>
45
+ <ProjectAdditionalLinkLibraries>comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib;wininet.lib</ProjectAdditionalLinkLibraries>
46
+ </PropertyGroup>
47
+
48
+ <ItemGroup>
49
+ <ClCompile Include="precomp.cpp">
50
+ <PrecompiledHeader>Create</PrecompiledHeader>
51
+ </ClCompile>
52
+ <ClCompile Include="ReplaceConfig.cpp" />
53
+ </ItemGroup>
54
+ <ItemGroup>
55
+ <ClInclude Include="precomp.h" />
56
+ </ItemGroup>
57
+
58
+ <ItemGroup>
59
+ <PackageReference Include="WixToolset.DUtil" />
60
+ </ItemGroup>
61
+
62
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
63
+</Project>
src/test/burn/TestData/PrereqBaTests/ReplaceConfig/precomp.cpp
new
+3
@@ -0,0 +1,3 @@
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
+#include "precomp.h"
src/test/burn/TestData/PrereqBaTests/ReplaceConfig/precomp.h
new
+17
@@ -0,0 +1,17 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+#include <windows.h>
6
+#include <msiquery.h>
7
+#include <objbase.h>
8
+#include <shlobj.h>
9
+#include <shlwapi.h>
10
+#include <stdlib.h>
11
+#include <strsafe.h>
12
+#include <CommCtrl.h>
13
+
14
+#include "dutil.h"
15
+#include "fileutil.h"
16
+#include "pathutil.h"
17
+#include "strutil.h"
src/test/burn/WixTestTools/BundleInstaller.cs
+14
@@ -26,6 +26,20 @@ namespace WixTestTools
26
27
public string TestName { get; }
28
29
+ /// <summary>
30
+ /// Runs the bundle asking for help.
31
+ /// </summary>
32
+ /// <param name="expectedExitCode">Expected exit code, defaults to success.</param>
33
+ /// <param name="arguments">Optional arguments to pass to the tool.</param>
34
+ /// <returns>Path to the generated log file.</returns>
35
+ public string Help(int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments)
36
+ {
37
+ var newArgumentList = new List<string>();
38
+ newArgumentList.Add("-help");
39
+ newArgumentList.AddRange(arguments);
40
+ return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Custom, newArgumentList.ToArray());
41
+ }
42
+
43
/// <summary>
44
/// Installs the bundle with optional arguments.
45
/// </summary>
src/test/burn/WixToolsetTest.BurnE2E/PrereqBaTests.cs
+221
-4
@@ -14,6 +14,39 @@ namespace WixToolsetTest.BurnE2E
14
15
const int E_PREREQBA_INFINITE_LOOP = -2_114_714_646;
16
17
+ /// <summary>
18
+ /// This bundle purposely provides a .runtimeconfig.json file that requires a version of .NET Core that doesn't exist,
19
+ /// with an MSI package to represent the prerequisite package.
20
+ /// This verifies that:
21
+ /// The preqba doesn't infinitely try to install prereqs.
22
+ /// The engine automatically uninstalls the bundle since only permanent packages were installed.
23
+ /// </summary>
24
+ [RuntimeFact]
25
+ public void DncAlwaysPreqBaDetectsInfiniteLoop()
26
+ {
27
+ var packageA = this.CreatePackageInstaller("PackageA");
28
+ var packageC = this.CreatePackageInstaller("PackageC");
29
+
30
+ var bundleC = this.CreateBundleInstaller("BundleC");
31
+
32
+ var packageASourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs");
33
+
34
+ // Source file should *not* be installed
35
+ Assert.False(File.Exists(packageASourceCodeInstalled), $"Package A payload should not be there on test start: {packageASourceCodeInstalled}");
36
+ packageC.VerifyInstalled(false);
37
+
38
+ bundleC.Install(E_PREREQBA_INFINITE_LOOP, "CAUSEINFINITELOOP=1");
39
+
40
+ // Part of the test is Install actually completing.
41
+
42
+ // Source file should be installed
43
+ Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
44
+ packageC.VerifyInstalled(false);
45
+
46
+ // No non-permanent packages should have ended up installed or cached so it should have unregistered.
47
+ bundleC.VerifyUnregisteredAndRemovedFromPackageCache();
48
+ }
49
+
50
/// <summary>
51
/// This bundle purposely provides a .runtimeconfig.json file that requires a version of .NET Core that doesn't exist,
52
/// with an MSI package to represent the prerequisite package.
@@ -25,7 +58,7 @@ namespace WixToolsetTest.BurnE2E
58
public void DncPreqBaDetectsInfiniteLoop()
59
{
60
var packageA = this.CreatePackageInstaller("PackageA");
28
- this.CreatePackageInstaller("PackageF");
61
+ var packageC = this.CreatePackageInstaller("PackageC");
62
63
var bundleA = this.CreateBundleInstaller("BundleA");
64
@@ -33,18 +66,131 @@ namespace WixToolsetTest.BurnE2E
66
67
// Source file should *not* be installed
68
Assert.False(File.Exists(packageASourceCodeInstalled), $"Package A payload should not be there on test start: {packageASourceCodeInstalled}");
69
+ packageC.VerifyInstalled(false);
70
37
- bundleA.Install(E_PREREQBA_INFINITE_LOOP);
71
+ bundleA.Install(E_PREREQBA_INFINITE_LOOP, "CAUSEINFINITELOOP=1");
72
73
// Part of the test is Install actually completing.
74
75
// Source file should be installed
76
Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
77
+ packageC.VerifyInstalled(false);
78
79
// No non-permanent packages should have ended up installed or cached so it should have unregistered.
80
bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
81
}
82
83
+ /// <summary>
84
+ /// This bundle purposely provides a .runtimeconfig.json file that requires a version of .NET Core that doesn't exist,
85
+ /// with an EXE prereq package to swap it out with a good one.
86
+ /// This verifies that:
87
+ /// The preqba doesn't infinitely try to install prereqs.
88
+ /// The managed BA gets loaded after installing prereqs.
89
+ /// </summary>
90
+ [RuntimeFact]
91
+ public void DncAlwaysPreqBaLoadsManagedBaAfterInstallingPrereqs()
92
+ {
93
+ var packageA = this.CreatePackageInstaller("PackageA");
94
+ var packageC = this.CreatePackageInstaller("PackageC");
95
+
96
+ var bundleC = this.CreateBundleInstaller("BundleC");
97
+
98
+ var packageASourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs");
99
+
100
+ // Source file should *not* be installed
101
+ Assert.False(File.Exists(packageASourceCodeInstalled), $"Package A payload should not be there on test start: {packageASourceCodeInstalled}");
102
+ packageC.VerifyInstalled(false);
103
+
104
+ bundleC.Install();
105
+
106
+ // Source file should be installed
107
+ Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
108
+ packageC.VerifyInstalled(true);
109
+
110
+ bundleC.VerifyRegisteredAndInPackageCache();
111
+
112
+ bundleC.Uninstall();
113
+
114
+ bundleC.VerifyUnregisteredAndRemovedFromPackageCache();
115
+ }
116
+
117
+ /// <summary>
118
+ /// This bundle purposely provides a .runtimeconfig.json file that requires a version of .NET Core that doesn't exist,
119
+ /// with an EXE prereq package to swap it out with a good one.
120
+ /// This verifies that:
121
+ /// The preqba doesn't infinitely reload itself after failing to load the managed BA.
122
+ /// The managed BA gets loaded after installing prereqs.
123
+ /// </summary>
124
+ [RuntimeFact]
125
+ public void DncPreqBaLoadsManagedBaAfterInstallingPrereqs()
126
+ {
127
+ var packageA = this.CreatePackageInstaller("PackageA");
128
+ var packageC = this.CreatePackageInstaller("PackageC");
129
+
130
+ var bundleA = this.CreateBundleInstaller("BundleA");
131
+
132
+ var packageASourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs");
133
+
134
+ // Source file should *not* be installed
135
+ Assert.False(File.Exists(packageASourceCodeInstalled), $"Package A payload should not be there on test start: {packageASourceCodeInstalled}");
136
+ packageC.VerifyInstalled(false);
137
+
138
+ bundleA.Install();
139
+
140
+ // Source file should be installed
141
+ Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
142
+ packageC.VerifyInstalled(true);
143
+
144
+ bundleA.VerifyRegisteredAndInPackageCache();
145
+
146
+ bundleA.Uninstall();
147
+
148
+ bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
149
+ }
150
+
151
+ [RuntimeFact]
152
+ public void DncAlwaysPreqBaForwardsHelpToManagedBa()
153
+ {
154
+ var bundleE = this.CreateBundleInstaller("BundleE");
155
+
156
+ var bundleLog = bundleE.Help();
157
+
158
+ Assert.True(LogVerifier.MessageInLogFile(bundleLog, "This is a BA for automated testing"));
159
+ }
160
+
161
+ /// <summary>
162
+ /// This bundle purposely provides a WixToolset.Mba.Host.config file that requires a version of .NET Framework that doesn't exist,
163
+ /// with an MSI package to represent the prerequisite package.
164
+ /// This verifies that:
165
+ /// The preqba doesn't infinitely try to install prereqs.
166
+ /// The engine automatically uninstalls the bundle since only permanent packages were installed.
167
+ /// </summary>
168
+ [RuntimeFact]
169
+ public void MbaAlwaysPreqBaDetectsInfiniteLoop()
170
+ {
171
+ var packageB = this.CreatePackageInstaller("PackageB");
172
+ var packageC = this.CreatePackageInstaller("PackageC");
173
+
174
+ var bundleD = this.CreateBundleInstaller("BundleD");
175
+
176
+ var packageBSourceCodeInstalled = packageB.GetInstalledFilePath("Package.wxs");
177
+
178
+ // Source file should *not* be installed
179
+ Assert.False(File.Exists(packageBSourceCodeInstalled), $"Package B payload should not be there on test start: {packageBSourceCodeInstalled}");
180
+ packageC.VerifyInstalled(false);
181
+
182
+ bundleD.Install(E_PREREQBA_INFINITE_LOOP, "CAUSEINFINITELOOP=1");
183
+
184
+ // Part of the test is Install actually completing.
185
+
186
+ // Source file should be installed
187
+ Assert.True(File.Exists(packageBSourceCodeInstalled), String.Concat("Should have found Package B payload installed at: ", packageBSourceCodeInstalled));
188
+ packageC.VerifyInstalled(false);
189
+
190
+ // No non-permanent packages should have ended up installed or cached so it should have unregistered.
191
+ bundleD.VerifyUnregisteredAndRemovedFromPackageCache();
192
+ }
193
+
194
/// <summary>
195
/// This bundle purposely provides a WixToolset.Mba.Host.config file that requires a version of .NET Framework that doesn't exist,
196
/// with an MSI package to represent the prerequisite package.
@@ -56,7 +202,7 @@ namespace WixToolsetTest.BurnE2E
202
public void MbaPreqBaDetectsInfiniteLoop()
203
{
204
var packageB = this.CreatePackageInstaller("PackageB");
59
- this.CreatePackageInstaller("PackageF");
205
+ var packageC = this.CreatePackageInstaller("PackageC");
206
207
var bundleB = this.CreateBundleInstaller("BundleB");
208
@@ -64,13 +210,84 @@ namespace WixToolsetTest.BurnE2E
210
211
// Source file should *not* be installed
212
Assert.False(File.Exists(packageBSourceCodeInstalled), $"Package B payload should not be there on test start: {packageBSourceCodeInstalled}");
213
+ packageC.VerifyInstalled(false);
214
68
- bundleB.Install(E_PREREQBA_INFINITE_LOOP);
215
+ bundleB.Install(E_PREREQBA_INFINITE_LOOP, "CAUSEINFINITELOOP=1");
216
217
// Part of the test is Install actually completing.
218
219
// Source file should be installed
220
Assert.True(File.Exists(packageBSourceCodeInstalled), String.Concat("Should have found Package B payload installed at: ", packageBSourceCodeInstalled));
221
+ packageC.VerifyInstalled(false);
222
+
223
+ // No non-permanent packages should have ended up installed or cached so it should have unregistered.
224
+ bundleB.VerifyUnregisteredAndRemovedFromPackageCache();
225
+ }
226
+
227
+ /// <summary>
228
+ /// This bundle purposely provides a WixToolset.Mba.Host.config file that requires a version of .NET Framework that doesn't exist,
229
+ /// with an EXE prereq package to swap it out with a good one.
230
+ /// This verifies that:
231
+ /// The preqba doesn't infinitely try to install prereqs.
232
+ /// The managed BA gets loaded after installing prereqs.
233
+ /// </summary>
234
+ [RuntimeFact]
235
+ public void MbaAlwaysPreqBaLoadsManagedBaAfterInstallingPrereqs()
236
+ {
237
+ var packageB = this.CreatePackageInstaller("PackageB");
238
+ var packageC = this.CreatePackageInstaller("PackageC");
239
+
240
+ var bundleD = this.CreateBundleInstaller("BundleD");
241
+
242
+ var packageBSourceCodeInstalled = packageB.GetInstalledFilePath("Package.wxs");
243
+
244
+ // Source file should *not* be installed
245
+ Assert.False(File.Exists(packageBSourceCodeInstalled), $"Package B payload should not be there on test start: {packageBSourceCodeInstalled}");
246
+ packageC.VerifyInstalled(false);
247
+
248
+ bundleD.Install();
249
+
250
+ // Source file should be installed
251
+ Assert.True(File.Exists(packageBSourceCodeInstalled), String.Concat("Should have found Package B payload installed at: ", packageBSourceCodeInstalled));
252
+ packageC.VerifyInstalled(true);
253
+
254
+ bundleD.VerifyRegisteredAndInPackageCache();
255
+
256
+ bundleD.Uninstall();
257
+
258
+ bundleD.VerifyUnregisteredAndRemovedFromPackageCache();
259
+ }
260
+
261
+ /// <summary>
262
+ /// This bundle purposely provides a WixToolset.Mba.Host.config file that requires a version of .NET Framework that doesn't exist,
263
+ /// with an EXE prereq package to swap it out with a good one.
264
+ /// This verifies that:
265
+ /// The preqba doesn't infinitely reload itself after failing to load the managed BA.
266
+ /// The managed BA gets loaded after installing prereqs.
267
+ /// </summary>
268
+ [RuntimeFact]
269
+ public void MbaPreqBaLoadsManagedBaAfterInstallingPrereqs()
270
+ {
271
+ var packageB = this.CreatePackageInstaller("PackageB");
272
+ var packageC = this.CreatePackageInstaller("PackageC");
273
+
274
+ var bundleB = this.CreateBundleInstaller("BundleB");
275
+
276
+ var packageBSourceCodeInstalled = packageB.GetInstalledFilePath("Package.wxs");
277
+
278
+ // Source file should *not* be installed
279
+ Assert.False(File.Exists(packageBSourceCodeInstalled), $"Package B payload should not be there on test start: {packageBSourceCodeInstalled}");
280
+ packageC.VerifyInstalled(false);
281
+
282
+ bundleB.Install();
283
+
284
+ // Source file should be installed
285
+ Assert.True(File.Exists(packageBSourceCodeInstalled), String.Concat("Should have found Package B payload installed at: ", packageBSourceCodeInstalled));
286
+ packageC.VerifyInstalled(true);
287
+
288
+ bundleB.VerifyRegisteredAndInPackageCache();
289
+
290
+ bundleB.Uninstall();
291
292
// No non-permanent packages should have ended up installed or cached so it should have unregistered.
293
bundleB.VerifyUnregisteredAndRemovedFromPackageCache();