aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-07-19 15:17:10 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-07-20 08:53:56 -0500
commit913b6238417dceeb8440315e4669990756d17655 (patch)
treea9e3552ea124d2025e30436afc8629f071c01ed4
parent93bb820eff547f8de304f05249f572da861256fb (diff)
downloadwix-913b6238417dceeb8440315e4669990756d17655.tar.gz
wix-913b6238417dceeb8440315e4669990756d17655.tar.bz2
wix-913b6238417dceeb8440315e4669990756d17655.zip
Add WixInternalUIBootstrapperApplication as a new built-in BA.
Implements 6835
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs5
-rw-r--r--src/api/burn/WixToolset.Mba.Core/PackageInfo.cs34
-rw-r--r--src/api/burn/balutil/balinfo.cpp27
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h14
-rw-r--r--src/api/burn/balutil/inc/balinfo.h10
-rw-r--r--src/api/wix/WixToolset.Data/Burn/BurnConstants.cs3
-rw-r--r--src/burn/engine/core.cpp6
-rw-r--r--src/burn/engine/splashscreen.cpp2
-rw-r--r--src/burn/engine/uithread.cpp2
-rw-r--r--src/ext/Bal/Bal.wixext.sln18
-rw-r--r--src/ext/Bal/bal.cmd8
-rw-r--r--src/ext/Bal/dnchost/dnchost.cpp8
-rw-r--r--src/ext/Bal/mbahost/mbahost.cpp8
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs6
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/InternalUIBAFixture.cs474
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/AllPrereqPackages.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitNonMsiPrimaryPackage.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitPrimaryPackage.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitPrimaryPackageEnableFeatureSelection.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/IuibaWarnings.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/MultipleDefaultPrimaryPackages.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/MultipleNonPermanentNonPrimaryPackages.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NoDefaultPrimaryPackage.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NonMsiPrimaryPackage.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NonPermanentPrereqPackage.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PermanentPrimaryPackage.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PrimaryPackageEnableFeatureSelection.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PrimaryPrereqPackage.wxs13
-rw-r--r--src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/SinglePrimaryPackage.wxs13
-rw-r--r--src/ext/Bal/wixext/BalBurnBackendExtension.cs294
-rw-r--r--src/ext/Bal/wixext/BalCompiler.cs269
-rw-r--r--src/ext/Bal/wixext/BalErrors.cs48
-rw-r--r--src/ext/Bal/wixext/BalWarnings.cs24
-rw-r--r--src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs17
-rw-r--r--src/ext/Bal/wixiuiba/WixInternalUIBootstrapperApplication.cpp918
-rw-r--r--src/ext/Bal/wixiuiba/WixInternalUIBootstrapperApplication.h18
-rw-r--r--src/ext/Bal/wixiuiba/precomp.cpp3
-rw-r--r--src/ext/Bal/wixiuiba/precomp.h30
-rw-r--r--src/ext/Bal/wixiuiba/wixiuiba.cpp192
-rw-r--r--src/ext/Bal/wixiuiba/wixiuiba.def6
-rw-r--r--src/ext/Bal/wixiuiba/wixiuiba.h13
-rw-r--r--src/ext/Bal/wixiuiba/wixiuiba.vcxproj72
-rw-r--r--src/ext/Bal/wixlib/BalExtension_platform.wxi14
-rw-r--r--src/ext/Bal/wixlib/bal.wixproj3
-rw-r--r--src/ext/Bal/wixlib/wixiuiba.wxs12
-rw-r--r--src/ext/Bal/wixstdba/Resources/iuipreq.thm67
-rw-r--r--src/ext/Bal/wixstdba/Resources/iuipreq.wxl34
-rw-r--r--src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp41
-rw-r--r--src/ext/Bal/wixstdba/inc/preqba.h4
-rw-r--r--src/test/burn/TestData/Templates/Bundle.wxs4
-rw-r--r--src/test/burn/TestData/TestData.proj2
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wixproj17
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wxs13
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/Bundle.wxs63
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/DtfSamples.sln (renamed from src/test/dtf/DtfE2ETests.sln)2
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.config (renamed from src/test/dtf/EmbeddedUI/EmbeddedUI.config)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.csproj (renamed from src/test/dtf/EmbeddedUI/EmbeddedUI.csproj)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/InstallProgressCounter.cs (renamed from src/test/dtf/EmbeddedUI/InstallProgressCounter.cs)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SampleEmbeddedUI.cs (renamed from src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml (renamed from src/test/dtf/EmbeddedUI/SetupWizard.xaml)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml.cs (renamed from src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs63
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wixproj15
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wxs11
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wixproj16
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wxs13
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/Bundle.wxs63
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wixproj14
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wxs10
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wixproj16
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wxs24
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wixproj14
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wxs9
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wixproj14
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wxs9
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wixproj14
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wxs9
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/ManagedCA/CustomAction.config (renamed from src/test/dtf/SampleCA/CustomAction.config)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/ManagedCA/ManagedCA.csproj (renamed from src/test/dtf/SampleCA/SampleCA.csproj)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/ManagedCA/SampleCA.cs (renamed from src/test/dtf/SampleCA/SampleCA.cs)0
-rw-r--r--src/test/burn/TestData/WixIuiBaTests/ManagedCA/testsub/testfile.txt (renamed from src/test/dtf/SampleCA/testsub/testfile.txt)0
-rw-r--r--src/test/burn/WixTestTools/PackageVerifier.cs18
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs144
-rw-r--r--src/test/dtf/Directory.Build.props11
-rw-r--r--src/test/dtf/Directory.Build.targets6
-rw-r--r--src/test/test.cmd2
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs5
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExtensionManifestCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs5
-rw-r--r--src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs5
92 files changed, 3352 insertions, 151 deletions
diff --git a/src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs b/src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs
index ee3be820..c6285f03 100644
--- a/src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs
@@ -68,6 +68,11 @@ namespace WixToolset.Mba.Core
68 string PrereqLicenseUrl { get; } 68 string PrereqLicenseUrl { get; }
69 69
70 /// <summary> 70 /// <summary>
71 /// See <see cref="PrimaryPackageType"/>
72 /// </summary>
73 PrimaryPackageType PrimaryPackageType { get; }
74
75 /// <summary>
71 /// 76 ///
72 /// </summary> 77 /// </summary>
73 string ProductCode { get; } 78 string ProductCode { get; }
diff --git a/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs b/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs
index e400cbe4..3fa1b49e 100644
--- a/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs
+++ b/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs
@@ -64,6 +64,37 @@ namespace WixToolset.Mba.Core
64 } 64 }
65 65
66 /// <summary> 66 /// <summary>
67 /// Metadata for BAs like WixInternalUIBootstrapperApplication that only support one main package.
68 /// </summary>
69 public enum PrimaryPackageType
70 {
71 /// <summary>
72 /// Not a primary package.
73 /// </summary>
74 None,
75
76 /// <summary>
77 /// The default package if no architecture specific package is available for the current architecture.
78 /// </summary>
79 Default,
80
81 /// <summary>
82 /// The package to use on x86 machines.
83 /// </summary>
84 X86,
85
86 /// <summary>
87 /// The package to use on x64 machines.
88 /// </summary>
89 X64,
90
91 /// <summary>
92 /// The package to use on ARM64 machines.
93 /// </summary>
94 ARM64,
95 }
96
97 /// <summary>
67 /// Default implementation of <see cref="IPackageInfo"/>. 98 /// Default implementation of <see cref="IPackageInfo"/>.
68 /// </summary> 99 /// </summary>
69 public class PackageInfo : IPackageInfo 100 public class PackageInfo : IPackageInfo
@@ -117,6 +148,9 @@ namespace WixToolset.Mba.Core
117 public string PrereqLicenseUrl { get; internal set; } 148 public string PrereqLicenseUrl { get; internal set; }
118 149
119 /// <inheritdoc/> 150 /// <inheritdoc/>
151 public PrimaryPackageType PrimaryPackageType { get; internal set; }
152
153 /// <inheritdoc/>
120 public object CustomData { get; set; } 154 public object CustomData { get; set; }
121 155
122 internal PackageInfo() { } 156 internal PackageInfo() { }
diff --git a/src/api/burn/balutil/balinfo.cpp b/src/api/burn/balutil/balinfo.cpp
index 5832281f..52a7f911 100644
--- a/src/api/burn/balutil/balinfo.cpp
+++ b/src/api/burn/balutil/balinfo.cpp
@@ -529,6 +529,33 @@ static HRESULT ParseBalPackageInfoFromXml(
529 hr = XmlGetAttributeEx(pNode, L"DisplayInternalUICondition", &pPackage->sczDisplayInternalUICondition); 529 hr = XmlGetAttributeEx(pNode, L"DisplayInternalUICondition", &pPackage->sczDisplayInternalUICondition);
530 ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get DisplayInternalUICondition setting for package."); 530 ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get DisplayInternalUICondition setting for package.");
531 531
532 hr = XmlGetAttributeEx(pNode, L"PrimaryPackageType", &scz);
533 ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get PrimaryPackageType setting for package.");
534
535 if (fXmlFound)
536 {
537 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"default", -1))
538 {
539 pPackage->primaryPackageType = BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT;
540 }
541 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"x86", -1))
542 {
543 pPackage->primaryPackageType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X86;
544 }
545 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"x64", -1))
546 {
547 pPackage->primaryPackageType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X64;
548 }
549 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"arm64", -1))
550 {
551 pPackage->primaryPackageType = BAL_INFO_PRIMARY_PACKAGE_TYPE_ARM64;
552 }
553 else
554 {
555 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid value for WixBalPackageInfo/@PrimaryPackageType: %ls", scz);
556 }
557 }
558
532 ReleaseNullObject(pNode); 559 ReleaseNullObject(pNode);
533 } 560 }
534 ExitOnFailure(hr, "Failed to parse all WixBalPackageInfo elements."); 561 ExitOnFailure(hr, "Failed to parse all WixBalPackageInfo elements.");
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
index aa1ca56f..c3223bee 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
@@ -451,8 +451,8 @@ public: // IBootstrapperApplication
451 __in BOOTSTRAPPER_ERROR_TYPE errorType, 451 __in BOOTSTRAPPER_ERROR_TYPE errorType,
452 __in_z LPCWSTR wzPackageId, 452 __in_z LPCWSTR wzPackageId,
453 __in DWORD dwCode, 453 __in DWORD dwCode,
454 __in_z LPCWSTR /*wzError*/, 454 __in_z LPCWSTR wzError,
455 __in DWORD /*dwUIHint*/, 455 __in DWORD dwUIHint,
456 __in DWORD /*cData*/, 456 __in DWORD /*cData*/,
457 __in_ecount_z_opt(cData) LPCWSTR* /*rgwzData*/, 457 __in_ecount_z_opt(cData) LPCWSTR* /*rgwzData*/,
458 __in int /*nRecommendation*/, 458 __in int /*nRecommendation*/,
@@ -461,7 +461,15 @@ public: // IBootstrapperApplication
461 { 461 {
462 BalRetryErrorOccurred(wzPackageId, dwCode); 462 BalRetryErrorOccurred(wzPackageId, dwCode);
463 463
464 if (CheckCanceled()) 464 if (BOOTSTRAPPER_DISPLAY_EMBEDDED == m_display)
465 {
466 HRESULT hr = m_pEngine->SendEmbeddedError(dwCode, wzError, dwUIHint, pResult);
467 if (FAILED(hr))
468 {
469 *pResult = IDERROR;
470 }
471 }
472 else if (CheckCanceled())
465 { 473 {
466 *pResult = IDCANCEL; 474 *pResult = IDCANCEL;
467 } 475 }
diff --git a/src/api/burn/balutil/inc/balinfo.h b/src/api/burn/balutil/inc/balinfo.h
index cd61f34d..818ff5ef 100644
--- a/src/api/burn/balutil/inc/balinfo.h
+++ b/src/api/burn/balutil/inc/balinfo.h
@@ -20,6 +20,15 @@ typedef enum BAL_INFO_PACKAGE_TYPE
20 BAL_INFO_PACKAGE_TYPE_BUNDLE_CHAIN, 20 BAL_INFO_PACKAGE_TYPE_BUNDLE_CHAIN,
21} BAL_INFO_PACKAGE_TYPE; 21} BAL_INFO_PACKAGE_TYPE;
22 22
23typedef enum _BAL_INFO_PRIMARY_PACKAGE_TYPE
24{
25 BAL_INFO_PRIMARY_PACKAGE_TYPE_NONE,
26 BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT,
27 BAL_INFO_PRIMARY_PACKAGE_TYPE_X86,
28 BAL_INFO_PRIMARY_PACKAGE_TYPE_X64,
29 BAL_INFO_PRIMARY_PACKAGE_TYPE_ARM64,
30} BAL_INFO_PRIMARY_PACKAGE_TYPE;
31
23typedef enum _BAL_INFO_RESTART 32typedef enum _BAL_INFO_RESTART
24{ 33{
25 BAL_INFO_RESTART_UNKNOWN, 34 BAL_INFO_RESTART_UNKNOWN,
@@ -54,6 +63,7 @@ typedef struct _BAL_INFO_PACKAGE
54 BOOL fPrereqPackage; 63 BOOL fPrereqPackage;
55 LPWSTR sczPrereqLicenseFile; 64 LPWSTR sczPrereqLicenseFile;
56 LPWSTR sczPrereqLicenseUrl; 65 LPWSTR sczPrereqLicenseUrl;
66 BAL_INFO_PRIMARY_PACKAGE_TYPE primaryPackageType;
57 LPVOID pvCustomData; 67 LPVOID pvCustomData;
58} BAL_INFO_PACKAGE; 68} BAL_INFO_PACKAGE;
59 69
diff --git a/src/api/wix/WixToolset.Data/Burn/BurnConstants.cs b/src/api/wix/WixToolset.Data/Burn/BurnConstants.cs
index 1ecccbd2..8eeb030b 100644
--- a/src/api/wix/WixToolset.Data/Burn/BurnConstants.cs
+++ b/src/api/wix/WixToolset.Data/Burn/BurnConstants.cs
@@ -14,6 +14,9 @@ namespace WixToolset.Data.Burn
14 public const string BundleExtensionDataWixOutputStreamName = "wix-bextdata"; 14 public const string BundleExtensionDataWixOutputStreamName = "wix-bextdata";
15 public const string BootstrapperApplicationDataWixOutputStreamName = "wix-badata.xml"; 15 public const string BootstrapperApplicationDataWixOutputStreamName = "wix-badata.xml";
16 16
17 public const string BootstrapperApplicationDataNamespace = "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData";
18 public const string BundleExtensionDataNamespace = "http://wixtoolset.org/schemas/v4/BundleExtensionData";
19
17 public const string BootstrapperApplicationDataSymbolDefinitionTag = "WixBootstrapperApplicationData"; 20 public const string BootstrapperApplicationDataSymbolDefinitionTag = "WixBootstrapperApplicationData";
18 public const string BundleExtensionSearchSymbolDefinitionTag = "WixBundleExtensionSearch"; 21 public const string BundleExtensionSearchSymbolDefinitionTag = "WixBundleExtensionSearch";
19 22
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index 90bbc8f6..bfd979de 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -447,9 +447,6 @@ extern "C" HRESULT CorePlan(
447 BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL; 447 BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL;
448 BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies. 448 BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies.
449 449
450 hr = PlanSetVariables(action, &pEngineState->variables);
451 ExitOnFailure(hr, "Failed to update action.");
452
453 LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action)); 450 LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action));
454 451
455 fPlanBegan = TRUE; 452 fPlanBegan = TRUE;
@@ -469,6 +466,9 @@ extern "C" HRESULT CorePlan(
469 pEngineState->fPlanned = FALSE; 466 pEngineState->fPlanned = FALSE;
470 PlanReset(&pEngineState->plan, &pEngineState->variables, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); 467 PlanReset(&pEngineState->plan, &pEngineState->variables, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads);
471 468
469 hr = PlanSetVariables(action, &pEngineState->variables);
470 ExitOnFailure(hr, "Failed to update action.");
471
472 // Remember the overall action state in the plan since it shapes the changes 472 // Remember the overall action state in the plan since it shapes the changes
473 // we make everywhere. 473 // we make everywhere.
474 pEngineState->plan.action = action; 474 pEngineState->plan.action = action;
diff --git a/src/burn/engine/splashscreen.cpp b/src/burn/engine/splashscreen.cpp
index ff61996f..7ba4e630 100644
--- a/src/burn/engine/splashscreen.cpp
+++ b/src/burn/engine/splashscreen.cpp
@@ -170,7 +170,7 @@ static DWORD WINAPI ThreadProc(
170 SPLASHSCREEN_INFO splashScreenInfo = { }; 170 SPLASHSCREEN_INFO splashScreenInfo = { };
171 171
172 WNDCLASSW wc = { }; 172 WNDCLASSW wc = { };
173 BOOL fRegistered = TRUE; 173 BOOL fRegistered = FALSE;
174 174
175 BOOL fRet = FALSE; 175 BOOL fRet = FALSE;
176 MSG msg = { }; 176 MSG msg = { };
diff --git a/src/burn/engine/uithread.cpp b/src/burn/engine/uithread.cpp
index cdd8613c..fe1c21b8 100644
--- a/src/burn/engine/uithread.cpp
+++ b/src/burn/engine/uithread.cpp
@@ -98,7 +98,7 @@ static DWORD WINAPI ThreadProc(
98 UITHREAD_INFO info = { }; 98 UITHREAD_INFO info = { };
99 99
100 WNDCLASSW wc = { }; 100 WNDCLASSW wc = { };
101 BOOL fRegistered = TRUE; 101 BOOL fRegistered = FALSE;
102 HWND hWnd = NULL; 102 HWND hWnd = NULL;
103 103
104 BOOL fRet = FALSE; 104 BOOL fRet = FALSE;
diff --git a/src/ext/Bal/Bal.wixext.sln b/src/ext/Bal/Bal.wixext.sln
index 9f77d79e..be7149f4 100644
--- a/src/ext/Bal/Bal.wixext.sln
+++ b/src/ext/Bal/Bal.wixext.sln
@@ -9,6 +9,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bafunctions", "Samples\bafu
9EndProject 9EndProject
10Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mbahost", "mbahost\mbahost.vcxproj", "{12C87C77-3547-44F8-8134-29BC915CB19D}" 10Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mbahost", "mbahost\mbahost.vcxproj", "{12C87C77-3547-44F8-8134-29BC915CB19D}"
11EndProject 11EndProject
12Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wixiuiba", "wixiuiba\wixiuiba.vcxproj", "{0F73E566-925C-448D-99CB-3A7F5DF399C8}"
13EndProject
12Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wixstdba", "wixstdba\wixstdba.vcxproj", "{41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}" 14Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wixstdba", "wixstdba\wixstdba.vcxproj", "{41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}"
13EndProject 15EndProject
14Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "bal", "wixlib\bal.wixproj", "{3444D952-F21C-496F-AB6B-56435BFD0787}" 16Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "bal", "wixlib\bal.wixproj", "{3444D952-F21C-496F-AB6B-56435BFD0787}"
@@ -83,6 +85,22 @@ Global
83 {12C87C77-3547-44F8-8134-29BC915CB19D}.Release|x64.Build.0 = Release|x64 85 {12C87C77-3547-44F8-8134-29BC915CB19D}.Release|x64.Build.0 = Release|x64
84 {12C87C77-3547-44F8-8134-29BC915CB19D}.Release|x86.ActiveCfg = Release|Win32 86 {12C87C77-3547-44F8-8134-29BC915CB19D}.Release|x86.ActiveCfg = Release|Win32
85 {12C87C77-3547-44F8-8134-29BC915CB19D}.Release|x86.Build.0 = Release|Win32 87 {12C87C77-3547-44F8-8134-29BC915CB19D}.Release|x86.Build.0 = Release|Win32
88 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Debug|Any CPU.ActiveCfg = Debug|Win32
89 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Debug|Any CPU.Build.0 = Debug|Win32
90 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Debug|ARM64.ActiveCfg = Debug|ARM64
91 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Debug|ARM64.Build.0 = Debug|ARM64
92 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Debug|x64.ActiveCfg = Debug|x64
93 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Debug|x64.Build.0 = Debug|x64
94 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Debug|x86.ActiveCfg = Debug|Win32
95 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Debug|x86.Build.0 = Debug|Win32
96 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Release|Any CPU.ActiveCfg = Release|Win32
97 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Release|Any CPU.Build.0 = Release|Win32
98 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Release|ARM64.ActiveCfg = Release|ARM64
99 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Release|ARM64.Build.0 = Release|ARM64
100 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Release|x64.ActiveCfg = Release|x64
101 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Release|x64.Build.0 = Release|x64
102 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Release|x86.ActiveCfg = Release|Win32
103 {0F73E566-925C-448D-99CB-3A7F5DF399C8}.Release|x86.Build.0 = Release|Win32
86 {41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}.Debug|Any CPU.ActiveCfg = Debug|Win32 104 {41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}.Debug|Any CPU.ActiveCfg = Debug|Win32
87 {41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}.Debug|Any CPU.Build.0 = Debug|Win32 105 {41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}.Debug|Any CPU.Build.0 = Debug|Win32
88 {41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}.Debug|ARM64.ActiveCfg = Debug|ARM64 106 {41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
diff --git a/src/ext/Bal/bal.cmd b/src/ext/Bal/bal.cmd
index 7a005cfe..aedf9b64 100644
--- a/src/ext/Bal/bal.cmd
+++ b/src/ext/Bal/bal.cmd
@@ -14,14 +14,6 @@ nuget restore dnchost\packages.config || exit /b
14msbuild -t:Restore -p:Configuration=%_C% || exit /b 14msbuild -t:Restore -p:Configuration=%_C% || exit /b
15 15
16:: Build 16:: Build
17msbuild -p:Configuration=%_C%;Platform=x86 dnchost\dnchost.vcxproj || exit /b
18msbuild -p:Configuration=%_C%;Platform=x64 dnchost\dnchost.vcxproj || exit /b
19msbuild -p:Configuration=%_C%;Platform=ARM64 dnchost\dnchost.vcxproj || exit /b
20
21msbuild -p:Configuration=%_C%;Platform=x86 mbahost\mbahost.vcxproj || exit /b
22msbuild -p:Configuration=%_C%;Platform=x64 mbahost\mbahost.vcxproj || exit /b
23msbuild -p:Configuration=%_C%;Platform=ARM64 mbahost\mbahost.vcxproj || exit /b
24
25msbuild -p:Configuration=%_C%;Platform=x86 test\examples\TestEngine\Example.TestEngine.vcxproj || exit /b 17msbuild -p:Configuration=%_C%;Platform=x86 test\examples\TestEngine\Example.TestEngine.vcxproj || exit /b
26msbuild -p:Configuration=%_C%;Platform=x64 test\examples\TestEngine\Example.TestEngine.vcxproj || exit /b 18msbuild -p:Configuration=%_C%;Platform=x64 test\examples\TestEngine\Example.TestEngine.vcxproj || exit /b
27msbuild -p:Configuration=%_C%;Platform=ARM64 test\examples\TestEngine\Example.TestEngine.vcxproj || exit /b 19msbuild -p:Configuration=%_C%;Platform=ARM64 test\examples\TestEngine\Example.TestEngine.vcxproj || exit /b
diff --git a/src/ext/Bal/dnchost/dnchost.cpp b/src/ext/Bal/dnchost/dnchost.cpp
index cdf204fb..8faf292c 100644
--- a/src/ext/Bal/dnchost/dnchost.cpp
+++ b/src/ext/Bal/dnchost/dnchost.cpp
@@ -110,18 +110,18 @@ extern "C" HRESULT WINAPI BootstrapperApplicationCreate(
110 { 110 {
111 if (DNCHOSTTYPE_SCD == vstate.type) 111 if (DNCHOSTTYPE_SCD == vstate.type)
112 { 112 {
113 vstate.prereqData.hrHostInitialization = E_DNCHOST_SCD_RUNTIME_FAILURE; 113 vstate.prereqData.hrFatalError = E_DNCHOST_SCD_RUNTIME_FAILURE;
114 BalLogError(hr, "The self-contained .NET Core runtime failed to load. This is an unrecoverable error."); 114 BalLogError(hr, "The self-contained .NET Core runtime failed to load. This is an unrecoverable error.");
115 } 115 }
116 else if (vstate.prereqData.fCompleted) 116 else if (vstate.prereqData.fCompleted)
117 { 117 {
118 hr = E_PREREQBA_INFINITE_LOOP; 118 hr = E_PREREQBA_INFINITE_LOOP;
119 BalLogError(hr, "The prerequisites were already installed. The bootstrapper application will not be reloaded to prevent an infinite loop."); 119 BalLogError(hr, "The prerequisites were already installed. The bootstrapper application will not be reloaded to prevent an infinite loop.");
120 vstate.prereqData.hrHostInitialization = hr; 120 vstate.prereqData.hrFatalError = hr;
121 } 121 }
122 else 122 else
123 { 123 {
124 vstate.prereqData.hrHostInitialization = S_OK; 124 vstate.prereqData.hrFatalError = S_OK;
125 } 125 }
126 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application because .NET Core host could not be loaded, error: 0x%08x.", hr); 126 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application because .NET Core host could not be loaded, error: 0x%08x.", hr);
127 127
@@ -233,6 +233,8 @@ static HRESULT LoadDncConfiguration(
233 BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get AlwaysInstallPrereqs value."); 233 BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get AlwaysInstallPrereqs value.");
234 } 234 }
235 235
236 pState->prereqData.fPerformHelp = !pState->prereqData.fAlwaysInstallPrereqs;
237
236 pState->type = DNCHOSTTYPE_FDD; 238 pState->type = DNCHOSTTYPE_FDD;
237 239
238 hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixDncOptions", &pixnHost); 240 hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixDncOptions", &pixnHost);
diff --git a/src/ext/Bal/mbahost/mbahost.cpp b/src/ext/Bal/mbahost/mbahost.cpp
index 0b89fc58..5edbe376 100644
--- a/src/ext/Bal/mbahost/mbahost.cpp
+++ b/src/ext/Bal/mbahost/mbahost.cpp
@@ -144,17 +144,17 @@ extern "C" HRESULT WINAPI BootstrapperApplicationCreate(
144 if (E_MBAHOST_NET452_ON_WIN7RTM == hr) 144 if (E_MBAHOST_NET452_ON_WIN7RTM == hr)
145 { 145 {
146 BalLogError(hr, "The Burn engine cannot run with an MBA under the .NET 4 CLR on Windows 7 RTM with .NET 4.5.2 (or greater) installed."); 146 BalLogError(hr, "The Burn engine cannot run with an MBA under the .NET 4 CLR on Windows 7 RTM with .NET 4.5.2 (or greater) installed.");
147 vstate.prereqData.hrHostInitialization = hr; 147 vstate.prereqData.hrFatalError = hr;
148 } 148 }
149 else if (vstate.prereqData.fCompleted) 149 else if (vstate.prereqData.fCompleted)
150 { 150 {
151 hr = E_PREREQBA_INFINITE_LOOP; 151 hr = E_PREREQBA_INFINITE_LOOP;
152 BalLogError(hr, "The prerequisites were already installed. The bootstrapper application will not be reloaded to prevent an infinite loop."); 152 BalLogError(hr, "The prerequisites were already installed. The bootstrapper application will not be reloaded to prevent an infinite loop.");
153 vstate.prereqData.hrHostInitialization = hr; 153 vstate.prereqData.hrFatalError = hr;
154 } 154 }
155 else 155 else
156 { 156 {
157 vstate.prereqData.hrHostInitialization = S_OK; 157 vstate.prereqData.hrFatalError = S_OK;
158 } 158 }
159 159
160 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application because managed host could not be loaded, error: 0x%08x.", hr); 160 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application because managed host could not be loaded, error: 0x%08x.", hr);
@@ -306,6 +306,8 @@ static HRESULT LoadMbaConfiguration(
306 BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get AlwaysInstallPrereqs value."); 306 BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get AlwaysInstallPrereqs value.");
307 } 307 }
308 308
309 pState->prereqData.fPerformHelp = !pState->prereqData.fAlwaysInstallPrereqs;
310
309LExit: 311LExit:
310 ReleaseObject(pixnHost); 312 ReleaseObject(pixnHost);
311 ReleaseObject(pixdManifest); 313 ReleaseObject(pixdManifest);
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs b/src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs
index d8197467..79e96316 100644
--- a/src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs
@@ -143,6 +143,12 @@ namespace WixToolsetTest.Bal
143 { 143 {
144 "<WixMbaPrereqOptions AlwaysInstallPrereqs='1' />", 144 "<WixMbaPrereqOptions AlwaysInstallPrereqs='1' />",
145 }, wixMbaPrereqOptionsElements); 145 }, wixMbaPrereqOptionsElements);
146
147 var wixMbaPrereqInformationElements = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixMbaPrereqInformation");
148 WixAssert.CompareLineByLine(new[]
149 {
150 "<WixMbaPrereqInformation PackageId='wixnative.exe' />",
151 }, wixMbaPrereqInformationElements);
146 } 152 }
147 } 153 }
148 154
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/InternalUIBAFixture.cs b/src/ext/Bal/test/WixToolsetTest.Bal/InternalUIBAFixture.cs
new file mode 100644
index 00000000..dba78da4
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/InternalUIBAFixture.cs
@@ -0,0 +1,474 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolsetTest.Bal
4{
5 using System;
6 using System.IO;
7 using System.Linq;
8 using WixBuildTools.TestSupport;
9 using WixToolset.Core.TestPackage;
10 using Xunit;
11
12 public class InternalUIBAFixture
13 {
14 [Fact]
15 public void CanBuildUsingWixIuiBa()
16 {
17 using (var fs = new DisposableFileSystem())
18 {
19 var baseFolder = fs.GetFolder();
20 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
21 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
22 var intermediateFolder = Path.Combine(baseFolder, "obj");
23 var baFolderPath = Path.Combine(baseFolder, "ba");
24 var extractFolderPath = Path.Combine(baseFolder, "extract");
25
26 var compileResult = WixRunner.Execute(new[]
27 {
28 "build",
29 Path.Combine(bundleSourceFolder, "SinglePrimaryPackage.wxs"),
30 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
31 "-intermediateFolder", intermediateFolder,
32 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
33 "-o", bundleFile,
34 });
35 compileResult.AssertSuccess();
36
37 Assert.True(File.Exists(bundleFile));
38
39 var extractResult = BundleExtractor.ExtractBAContainer(null, bundleFile, baFolderPath, extractFolderPath);
40 extractResult.AssertSuccess();
41
42 var balPackageInfos = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixBalPackageInfo");
43 WixAssert.CompareLineByLine(new string[]
44 {
45 "<WixBalPackageInfo PackageId='test.msi' PrimaryPackageType='default' />",
46 }, balPackageInfos);
47
48 Assert.True(File.Exists(Path.Combine(baFolderPath, "mbapreq.thm")));
49 Assert.True(File.Exists(Path.Combine(baFolderPath, "mbapreq.wxl")));
50 }
51 }
52
53 [Fact]
54 public void CanBuildUsingWixIuiBaWithImplicitPrimaryPackage()
55 {
56 using (var fs = new DisposableFileSystem())
57 {
58 var baseFolder = fs.GetFolder();
59 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
60 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
61 var intermediateFolder = Path.Combine(baseFolder, "obj");
62 var baFolderPath = Path.Combine(baseFolder, "ba");
63 var extractFolderPath = Path.Combine(baseFolder, "extract");
64
65 var compileResult = WixRunner.Execute(new[]
66 {
67 "build",
68 Path.Combine(bundleSourceFolder, "ImplicitPrimaryPackage.wxs"),
69 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
70 "-intermediateFolder", intermediateFolder,
71 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
72 "-o", bundleFile,
73 });
74 compileResult.AssertSuccess();
75
76 Assert.True(File.Exists(bundleFile));
77
78 var extractResult = BundleExtractor.ExtractBAContainer(null, bundleFile, baFolderPath, extractFolderPath);
79 extractResult.AssertSuccess();
80
81 var balPackageInfos = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixBalPackageInfo");
82 WixAssert.CompareLineByLine(new string[]
83 {
84 "<WixBalPackageInfo PackageId='test.msi' PrimaryPackageType='default' />",
85 }, balPackageInfos);
86
87 var mbaPrereqInfos = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixMbaPrereqInformation");
88 WixAssert.CompareLineByLine(new[]
89 {
90 "<WixMbaPrereqInformation PackageId='wixnative.exe' />",
91 }, mbaPrereqInfos);
92
93 Assert.True(File.Exists(Path.Combine(baFolderPath, "mbapreq.thm")));
94 Assert.True(File.Exists(Path.Combine(baFolderPath, "mbapreq.wxl")));
95 }
96 }
97
98 [Fact]
99 public void CanBuildUsingWixIuiBaWithWarnings()
100 {
101 using (var fs = new DisposableFileSystem())
102 {
103 var baseFolder = fs.GetFolder();
104 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
105 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
106 var intermediateFolder = Path.Combine(baseFolder, "obj");
107 var baFolderPath = Path.Combine(baseFolder, "ba");
108 var extractFolderPath = Path.Combine(baseFolder, "extract");
109
110 var compileResult = WixRunner.Execute(warningsAsErrors: false, new[]
111 {
112 "build",
113 Path.Combine(bundleSourceFolder, "IuiBaWarnings.wxs"),
114 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
115 "-intermediateFolder", intermediateFolder,
116 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
117 "-o", bundleFile,
118 });
119
120 WixAssert.CompareLineByLine(new[]
121 {
122 "WixInternalUIBootstrapperApplication does not support the value of 'force' for Cache on prereq packages. Prereq packages are only cached when they need to be installed.",
123 "WixInternalUIBootstrapperApplication ignores InstallCondition for the primary package so that the MSI UI is always shown.",
124 "WixInternalUIBootstrapperApplication ignores DisplayInternalUICondition for the primary package so that the MSI UI is always shown.",
125 "When using WixInternalUIBootstrapperApplication, all prereq packages should be before the primary package in the chain. The prereq packages are always installed before the primary package.",
126 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
127
128 compileResult.AssertSuccess();
129
130 Assert.True(File.Exists(bundleFile));
131
132 var extractResult = BundleExtractor.ExtractBAContainer(null, bundleFile, baFolderPath, extractFolderPath);
133 extractResult.AssertSuccess();
134
135 var balPackageInfos = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixBalPackageInfo");
136 WixAssert.CompareLineByLine(new string[]
137 {
138 "<WixBalPackageInfo PackageId='test.msi' DisplayInternalUICondition='DISPLAYTEST' PrimaryPackageType='default' />",
139 }, balPackageInfos);
140
141 var mbaPrereqInfos = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixMbaPrereqInformation");
142 WixAssert.CompareLineByLine(new[]
143 {
144 "<WixMbaPrereqInformation PackageId='wixnative.exe' />",
145 }, mbaPrereqInfos);
146
147 Assert.True(File.Exists(Path.Combine(baFolderPath, "mbapreq.thm")));
148 Assert.True(File.Exists(Path.Combine(baFolderPath, "mbapreq.wxl")));
149 }
150 }
151
152 [Fact]
153 public void CannotBuildUsingWixIuiBaWithAllPrereqPackages()
154 {
155 using (var fs = new DisposableFileSystem())
156 {
157 var baseFolder = fs.GetFolder();
158 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
159 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
160 var intermediateFolder = Path.Combine(baseFolder, "obj");
161
162 var compileResult = WixRunner.Execute(new[]
163 {
164 "build",
165 Path.Combine(bundleSourceFolder, "AllPrereqPackages.wxs"),
166 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
167 "-intermediateFolder", intermediateFolder,
168 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
169 "-o", bundleFile,
170 });
171
172 WixAssert.CompareLineByLine(new[]
173 {
174 "When using WixInternalUIBootstrapperApplication, there must be one package with bal:PrimaryPackageType=\"default\".",
175 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
176
177 Assert.Equal(6808, compileResult.ExitCode);
178 }
179 }
180
181 [Fact]
182 public void CannotBuildUsingWixIuiBaWithImplicitNonMsiPrimaryPackage()
183 {
184 using (var fs = new DisposableFileSystem())
185 {
186 var baseFolder = fs.GetFolder();
187 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
188 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
189 var intermediateFolder = Path.Combine(baseFolder, "obj");
190
191 var compileResult = WixRunner.Execute(new[]
192 {
193 "build",
194 Path.Combine(bundleSourceFolder, "ImplicitNonMsiPrimaryPackage.wxs"),
195 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
196 "-intermediateFolder", intermediateFolder,
197 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
198 "-o", bundleFile,
199 });
200
201 WixAssert.CompareLineByLine(new[]
202 {
203 "When using WixInternalUIBootstrapperApplication, packages must either be non-permanent and have the bal:PrimaryPackageType attribute, or be permanent and have the bal:PrereqPackage attribute set to 'yes'.",
204 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
205
206 Assert.Equal(6811, compileResult.ExitCode);
207 }
208 }
209
210 [Fact]
211 public void CannotBuildUsingWixIuiBaWithImplicitPrimaryPackageEnableFeatureSelection()
212 {
213 using (var fs = new DisposableFileSystem())
214 {
215 var baseFolder = fs.GetFolder();
216 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
217 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
218 var intermediateFolder = Path.Combine(baseFolder, "obj");
219
220 var compileResult = WixRunner.Execute(new[]
221 {
222 "build",
223 Path.Combine(bundleSourceFolder, "ImplicitPrimaryPackageEnableFeatureSelection.wxs"),
224 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
225 "-intermediateFolder", intermediateFolder,
226 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
227 "-o", bundleFile,
228 });
229
230 WixAssert.CompareLineByLine(new[]
231 {
232 "When using WixInternalUIBootstrapperApplication, packages must either be non-permanent and have the bal:PrimaryPackageType attribute, or be permanent and have the bal:PrereqPackage attribute set to 'yes'.",
233 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
234
235 Assert.Equal(6811, compileResult.ExitCode);
236 }
237 }
238
239 [Fact]
240 public void CannotBuildUsingWixIuiBaWithMultipleNonPermanentNonPrimaryPackages()
241 {
242 using (var fs = new DisposableFileSystem())
243 {
244 var baseFolder = fs.GetFolder();
245 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
246 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
247 var intermediateFolder = Path.Combine(baseFolder, "obj");
248
249 var compileResult = WixRunner.Execute(new[]
250 {
251 "build",
252 Path.Combine(bundleSourceFolder, "MultipleNonPermanentNonPrimaryPackages.wxs"),
253 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
254 "-intermediateFolder", intermediateFolder,
255 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
256 "-o", bundleFile,
257 });
258
259 WixAssert.CompareLineByLine(new[]
260 {
261 "When using WixInternalUIBootstrapperApplication, packages must either be non-permanent and have the bal:PrimaryPackageType attribute, or be permanent and have the bal:PrereqPackage attribute set to 'yes'.",
262 "When using WixInternalUIBootstrapperApplication, packages must either be non-permanent and have the bal:PrimaryPackageType attribute, or be permanent and have the bal:PrereqPackage attribute set to 'yes'.",
263 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
264
265 Assert.Equal(6811, compileResult.ExitCode);
266 }
267 }
268
269 [Fact]
270 public void CannotBuildUsingWixIuiBaWithMultiplePrimaryPackagesOfSameType()
271 {
272 using (var fs = new DisposableFileSystem())
273 {
274 var baseFolder = fs.GetFolder();
275 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
276 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
277 var intermediateFolder = Path.Combine(baseFolder, "obj");
278
279 var compileResult = WixRunner.Execute(new[]
280 {
281 "build",
282 Path.Combine(bundleSourceFolder, "MultipleDefaultPrimaryPackages.wxs"),
283 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
284 "-intermediateFolder", intermediateFolder,
285 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
286 "-o", bundleFile,
287 });
288
289 WixAssert.CompareLineByLine(new[]
290 {
291 "There may only be one package in the bundle with PrimaryPackageType of 'default'.",
292 "The location of the package related to the previous error.",
293 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
294
295 Assert.Equal(6810, compileResult.ExitCode);
296 }
297 }
298
299 [Fact]
300 public void CannotBuildUsingWixIuiBaWithNoDefaultPrimaryPackage()
301 {
302 using (var fs = new DisposableFileSystem())
303 {
304 var baseFolder = fs.GetFolder();
305 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
306 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
307 var intermediateFolder = Path.Combine(baseFolder, "obj");
308
309 var compileResult = WixRunner.Execute(new[]
310 {
311 "build",
312 Path.Combine(bundleSourceFolder, "NoDefaultPrimaryPackage.wxs"),
313 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
314 "-intermediateFolder", intermediateFolder,
315 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
316 "-o", bundleFile,
317 });
318
319 WixAssert.CompareLineByLine(new[]
320 {
321 "When using WixInternalUIBootstrapperApplication, there must be one package with bal:PrimaryPackageType=\"default\".",
322 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
323
324 Assert.Equal(6808, compileResult.ExitCode);
325 }
326 }
327
328 [Fact]
329 public void CannotBuildUsingWixIuiBaWithNonMsiPrimaryPackage()
330 {
331 using (var fs = new DisposableFileSystem())
332 {
333 var baseFolder = fs.GetFolder();
334 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
335 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
336 var intermediateFolder = Path.Combine(baseFolder, "obj");
337
338 var compileResult = WixRunner.Execute(new[]
339 {
340 "build",
341 Path.Combine(bundleSourceFolder, "NonMsiPrimaryPackage.wxs"),
342 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
343 "-intermediateFolder", intermediateFolder,
344 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
345 "-o", bundleFile,
346 });
347
348 WixAssert.CompareLineByLine(new[]
349 {
350 "When using WixInternalUIBootstrapperApplication, each primary package must be an MsiPackage.",
351 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
352
353 Assert.Equal(6814, compileResult.ExitCode);
354 }
355 }
356
357 [Fact]
358 public void CannotBuildUsingWixIuiBaWithNonPermanentPrereqPackage()
359 {
360 using (var fs = new DisposableFileSystem())
361 {
362 var baseFolder = fs.GetFolder();
363 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
364 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
365 var intermediateFolder = Path.Combine(baseFolder, "obj");
366
367 var compileResult = WixRunner.Execute(new[]
368 {
369 "build",
370 Path.Combine(bundleSourceFolder, "NonPermanentPrereqPackage.wxs"),
371 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
372 "-intermediateFolder", intermediateFolder,
373 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
374 "-o", bundleFile,
375 });
376
377 WixAssert.CompareLineByLine(new[]
378 {
379 "When using WixInternalUIBootstrapperApplication and bal:PrereqPackage is set to 'yes', the package must be permanent.",
380 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
381
382 Assert.Equal(6812, compileResult.ExitCode);
383 }
384 }
385
386 [Fact]
387 public void CannotBuildUsingWixIuiBaWithPermanentPrimaryPackage()
388 {
389 using (var fs = new DisposableFileSystem())
390 {
391 var baseFolder = fs.GetFolder();
392 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
393 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
394 var intermediateFolder = Path.Combine(baseFolder, "obj");
395
396 var compileResult = WixRunner.Execute(new[]
397 {
398 "build",
399 Path.Combine(bundleSourceFolder, "PermanentPrimaryPackage.wxs"),
400 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
401 "-intermediateFolder", intermediateFolder,
402 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
403 "-o", bundleFile,
404 });
405
406 WixAssert.CompareLineByLine(new[]
407 {
408 "When using WixInternalUIBootstrapperApplication, packages with the bal:PrimaryPackageType attribute must not be permanent.",
409 "When using WixInternalUIBootstrapperApplication, there must be one package with bal:PrimaryPackageType=\"default\".",
410 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
411
412 Assert.Equal(6808, compileResult.ExitCode);
413 }
414 }
415
416 [Fact]
417 public void CannotBuildUsingWixIuiBaWithPrimaryPackageEnableFeatureSelection()
418 {
419 using (var fs = new DisposableFileSystem())
420 {
421 var baseFolder = fs.GetFolder();
422 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
423 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
424 var intermediateFolder = Path.Combine(baseFolder, "obj");
425
426 var compileResult = WixRunner.Execute(new[]
427 {
428 "build",
429 Path.Combine(bundleSourceFolder, "PrimaryPackageEnableFeatureSelection.wxs"),
430 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
431 "-intermediateFolder", intermediateFolder,
432 "-bindpath", TestData.Get(@"TestData\WixStdBa\Data"),
433 "-o", bundleFile,
434 });
435
436 WixAssert.CompareLineByLine(new[]
437 {
438 "When using WixInternalUIBootstrapperApplication, primary packages must not have feature selection enabled because it interferes with the user selecting feature through the MSI UI.",
439 "When using WixInternalUIBootstrapperApplication, there must be one package with bal:PrimaryPackageType=\"default\".",
440 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
441
442 Assert.Equal(6808, compileResult.ExitCode);
443 }
444 }
445
446 [Fact]
447 public void CannotBuildUsingWixIuiBaWithPrimaryPrereqPackage()
448 {
449 using (var fs = new DisposableFileSystem())
450 {
451 var baseFolder = fs.GetFolder();
452 var wixlibFile = Path.Combine(baseFolder, "bin", "test.wixlib");
453 var bundleSourceFolder = TestData.Get(@"TestData\WixIuiBa");
454 var intermediateFolder = Path.Combine(baseFolder, "obj");
455
456 var compileResult = WixRunner.Execute(new[]
457 {
458 "build",
459 Path.Combine(bundleSourceFolder, "PrimaryPrereqPackage.wxs"),
460 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
461 "-intermediateFolder", intermediateFolder,
462 "-o", wixlibFile,
463 });
464
465 WixAssert.CompareLineByLine(new[]
466 {
467 "The MsiPackage/@PrereqPackage attribute's value, 'yes', cannot be specified with attribute PrimaryPackageType present.",
468 }, compileResult.Messages.Select(m => m.ToString()).ToArray());
469
470 Assert.Equal(193, compileResult.ExitCode);
471 }
472 }
473 }
474}
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/AllPrereqPackages.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/AllPrereqPackages.wxs
new file mode 100644
index 00000000..17f1ee77
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/AllPrereqPackages.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{1CD73801-0B08-4B39-B371-00DA49EF715F}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage bal:PrereqPackage="yes" Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
10 <MsiPackage bal:PrereqPackage="yes" Permanent="yes" SourceFile="test.msi" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitNonMsiPrimaryPackage.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitNonMsiPrimaryPackage.wxs
new file mode 100644
index 00000000..ca1f9358
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitNonMsiPrimaryPackage.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{1CD73801-0B08-4B39-B371-00DA49EF715F}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <MsiPackage SourceFile="test.msi" Permanent="yes" />
10 <ExePackage UninstallArguments="" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitPrimaryPackage.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitPrimaryPackage.wxs
new file mode 100644
index 00000000..16a99e92
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitPrimaryPackage.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{1CD73801-0B08-4B39-B371-00DA49EF715F}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
10 <MsiPackage SourceFile="test.msi" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitPrimaryPackageEnableFeatureSelection.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitPrimaryPackageEnableFeatureSelection.wxs
new file mode 100644
index 00000000..85b9df65
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/ImplicitPrimaryPackageEnableFeatureSelection.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{1CD73801-0B08-4B39-B371-00DA49EF715F}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
10 <MsiPackage SourceFile="test.msi" EnableFeatureSelection="yes" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/IuibaWarnings.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/IuibaWarnings.wxs
new file mode 100644
index 00000000..2cf9787d
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/IuibaWarnings.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{1CD73801-0B08-4B39-B371-00DA49EF715F}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <MsiPackage SourceFile="test.msi" InstallCondition="INSTALLTEST" bal:DisplayInternalUICondition="DISPLAYTEST" />
10 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" Cache="force" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/MultipleDefaultPrimaryPackages.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/MultipleDefaultPrimaryPackages.wxs
new file mode 100644
index 00000000..11736fbb
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/MultipleDefaultPrimaryPackages.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{5C7B7C41-B3A9-4FFF-952A-B6D68320B9B4}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <MsiPackage Id="One" SourceFile="test.msi" bal:PrimaryPackageType="default" />
10 <MsiPackage Id="Two" CacheId="dontdothis" SourceFile="test.msi" bal:PrimaryPackageType="default" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/MultipleNonPermanentNonPrimaryPackages.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/MultipleNonPermanentNonPrimaryPackages.wxs
new file mode 100644
index 00000000..c5b923df
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/MultipleNonPermanentNonPrimaryPackages.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{1CD73801-0B08-4B39-B371-00DA49EF715F}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage UninstallArguments="" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
10 <MsiPackage SourceFile="test.msi" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NoDefaultPrimaryPackage.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NoDefaultPrimaryPackage.wxs
new file mode 100644
index 00000000..7f7528d0
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NoDefaultPrimaryPackage.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{1CD73801-0B08-4B39-B371-00DA49EF715F}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
10 <MsiPackage bal:PrimaryPackageType="x86" SourceFile="test.msi" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NonMsiPrimaryPackage.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NonMsiPrimaryPackage.wxs
new file mode 100644
index 00000000..a6f93bcb
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NonMsiPrimaryPackage.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{1CD73801-0B08-4B39-B371-00DA49EF715F}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage UninstallArguments="" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" bal:PrimaryPackageType="x86" />
10 <MsiPackage SourceFile="test.msi" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NonPermanentPrereqPackage.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NonPermanentPrereqPackage.wxs
new file mode 100644
index 00000000..a60943b0
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/NonPermanentPrereqPackage.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{5C7B7C41-B3A9-4FFF-952A-B6D68320B9B4}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage UninstallArguments="" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" bal:PrereqPackage="yes" />
10 <MsiPackage SourceFile="test.msi" bal:PrimaryPackageType="default" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PermanentPrimaryPackage.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PermanentPrimaryPackage.wxs
new file mode 100644
index 00000000..43caaf86
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PermanentPrimaryPackage.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{5C7B7C41-B3A9-4FFF-952A-B6D68320B9B4}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" bal:PrereqPackage="yes" />
10 <MsiPackage SourceFile="test.msi" bal:PrimaryPackageType="default" Permanent="yes" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PrimaryPackageEnableFeatureSelection.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PrimaryPackageEnableFeatureSelection.wxs
new file mode 100644
index 00000000..4f1c40dd
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PrimaryPackageEnableFeatureSelection.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{5C7B7C41-B3A9-4FFF-952A-B6D68320B9B4}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" bal:PrereqPackage="yes" />
10 <MsiPackage SourceFile="test.msi" bal:PrimaryPackageType="default" EnableFeatureSelection="yes" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PrimaryPrereqPackage.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PrimaryPrereqPackage.wxs
new file mode 100644
index 00000000..bdb8c470
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/PrimaryPrereqPackage.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{5C7B7C41-B3A9-4FFF-952A-B6D68320B9B4}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" bal:PrereqPackage="yes" />
10 <MsiPackage SourceFile="test.msi" bal:PrimaryPackageType="default" bal:PrereqPackage="yes" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/SinglePrimaryPackage.wxs b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/SinglePrimaryPackage.wxs
new file mode 100644
index 00000000..1e9a87c2
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.Bal/TestData/WixIuiBa/SinglePrimaryPackage.wxs
@@ -0,0 +1,13 @@
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="WixIuiBa" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{5C7B7C41-B3A9-4FFF-952A-B6D68320B9B4}">
5 <BootstrapperApplication>
6 <bal:WixInternalUIBootstrapperApplication />
7 </BootstrapperApplication>
8 <Chain>
9 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" bal:PrereqPackage="yes" />
10 <MsiPackage SourceFile="test.msi" bal:PrimaryPackageType="default" />
11 </Chain>
12 </Bundle>
13</Wix>
diff --git a/src/ext/Bal/wixext/BalBurnBackendExtension.cs b/src/ext/Bal/wixext/BalBurnBackendExtension.cs
index d34c159a..6f615796 100644
--- a/src/ext/Bal/wixext/BalBurnBackendExtension.cs
+++ b/src/ext/Bal/wixext/BalBurnBackendExtension.cs
@@ -5,6 +5,8 @@ namespace WixToolset.Bal
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq; 7 using System.Linq;
8 using System.Text;
9 using System.Xml;
8 using WixToolset.Bal.Symbols; 10 using WixToolset.Bal.Symbols;
9 using WixToolset.Data; 11 using WixToolset.Data;
10 using WixToolset.Data.Burn; 12 using WixToolset.Data.Burn;
@@ -29,12 +31,48 @@ namespace WixToolset.Bal
29 31
30 protected override IReadOnlyCollection<IntermediateSymbolDefinition> SymbolDefinitions => BurnSymbolDefinitions; 32 protected override IReadOnlyCollection<IntermediateSymbolDefinition> SymbolDefinitions => BurnSymbolDefinitions;
31 33
34 public override bool TryProcessSymbol(IntermediateSection section, IntermediateSymbol symbol)
35 {
36 if (symbol is WixBalPackageInfoSymbol balPackageInfoSymbol)
37 {
38 // There might be a more efficient way to do this,
39 // but this is an easy way to ensure we're creating valid XML.
40 var sb = new StringBuilder();
41 using (var writer = XmlWriter.Create(sb))
42 {
43 writer.WriteStartElement(symbol.Definition.Name, BurnConstants.BootstrapperApplicationDataNamespace);
44
45 writer.WriteAttributeString("PackageId", balPackageInfoSymbol.PackageId);
46
47 if (balPackageInfoSymbol.DisplayInternalUICondition != null)
48 {
49 writer.WriteAttributeString("DisplayInternalUICondition", balPackageInfoSymbol.DisplayInternalUICondition);
50 }
51
52 if (balPackageInfoSymbol.PrimaryPackageType != BalPrimaryPackageType.None)
53 {
54 writer.WriteAttributeString("PrimaryPackageType", balPackageInfoSymbol.PrimaryPackageType.ToString().ToLower());
55 }
56
57 writer.WriteEndElement();
58 }
59
60 this.BackendHelper.AddBootstrapperApplicationData(sb.ToString());
61
62 return true;
63 }
64 else
65 {
66 return base.TryProcessSymbol(section, symbol);
67 }
68 }
69
32 public override void SymbolsFinalized(IntermediateSection section) 70 public override void SymbolsFinalized(IntermediateSection section)
33 { 71 {
34 base.SymbolsFinalized(section); 72 base.SymbolsFinalized(section);
35 73
36 this.VerifyBalConditions(section); 74 this.VerifyBalConditions(section);
37 this.VerifyBalPackageInfos(section); 75 this.VerifyDisplayInternalUICondition(section);
38 this.VerifyOverridableVariables(section); 76 this.VerifyOverridableVariables(section);
39 77
40 var baSymbol = section.Symbols.OfType<WixBootstrapperApplicationDllSymbol>().SingleOrDefault(); 78 var baSymbol = section.Symbols.OfType<WixBootstrapperApplicationDllSymbol>().SingleOrDefault();
@@ -44,24 +82,31 @@ namespace WixToolset.Bal
44 return; 82 return;
45 } 83 }
46 84
85 var isIuiBA = baId.StartsWith("WixInternalUIBootstrapperApplication");
47 var isStdBA = baId.StartsWith("WixStandardBootstrapperApplication"); 86 var isStdBA = baId.StartsWith("WixStandardBootstrapperApplication");
48 var isMBA = baId.StartsWith("WixManagedBootstrapperApplicationHost"); 87 var isMBA = baId.StartsWith("WixManagedBootstrapperApplicationHost");
49 var isDNC = baId.StartsWith("WixDotNetCoreBootstrapperApplicationHost"); 88 var isDNC = baId.StartsWith("WixDotNetCoreBootstrapperApplicationHost");
50 var isSCD = isDNC && this.VerifySCD(section); 89 var isSCD = isDNC && this.VerifySCD(section);
51 90
91 if (isIuiBA)
92 {
93 // This needs to happen before VerifyPrereqPackages because it can add prereq packages.
94 this.VerifyPrimaryPackages(section);
95 }
96
52 if (isDNC) 97 if (isDNC)
53 { 98 {
54 this.FinalizeBAFactorySymbol(section); 99 this.FinalizeBAFactorySymbol(section);
55 } 100 }
56 101
57 if (isStdBA || isMBA || isDNC) 102 if (isIuiBA || isStdBA || isMBA || isDNC)
58 { 103 {
59 this.VerifyBAFunctions(section); 104 this.VerifyBAFunctions(section);
60 } 105 }
61 106
62 if (isMBA || (isDNC && !isSCD)) 107 if (isIuiBA || isMBA || (isDNC && !isSCD))
63 { 108 {
64 this.VerifyPrereqPackages(section, isDNC); 109 this.VerifyPrereqPackages(section, isDNC, isIuiBA);
65 } 110 }
66 } 111 }
67 112
@@ -133,12 +178,241 @@ namespace WixToolset.Bal
133 } 178 }
134 } 179 }
135 180
136 private void VerifyBalPackageInfos(IntermediateSection section) 181 private void VerifyDisplayInternalUICondition(IntermediateSection section)
182 {
183 foreach (var balPackageInfoSymbol in section.Symbols.OfType<WixBalPackageInfoSymbol>().ToList())
184 {
185 if (balPackageInfoSymbol.DisplayInternalUICondition != null)
186 {
187 this.BackendHelper.ValidateBundleCondition(balPackageInfoSymbol.SourceLineNumbers, "*Package", "bal:DisplayInternalUICondition", balPackageInfoSymbol.DisplayInternalUICondition, BundleConditionPhase.Plan);
188 }
189 }
190 }
191
192 private void VerifyPrimaryPackages(IntermediateSection section)
193 {
194 WixBalPackageInfoSymbol defaultPrimaryPackage = null;
195 WixBalPackageInfoSymbol x86PrimaryPackage = null;
196 WixBalPackageInfoSymbol x64PrimaryPackage = null;
197 WixBalPackageInfoSymbol arm64PrimaryPackage = null;
198 var nonPermanentNonPrimaryPackages = new List<WixBundlePackageSymbol>();
199
200 var balPackageInfoSymbolsByPackageId = section.Symbols.OfType<WixBalPackageInfoSymbol>().ToDictionary(x => x.PackageId);
201 var mbaPrereqInfoSymbolsByPackageId = section.Symbols.OfType<WixMbaPrereqInformationSymbol>().ToDictionary(x => x.PackageId);
202 var msiPackageSymbolsByPackageId = section.Symbols.OfType<WixBundleMsiPackageSymbol>().ToDictionary(x => x.Id.Id);
203 var packageSymbols = section.Symbols.OfType<WixBundlePackageSymbol>().ToList();
204 foreach (var packageSymbol in packageSymbols)
205 {
206 var packageId = packageSymbol.Id?.Id;
207 var isPrereq = false;
208 var primaryPackageType = BalPrimaryPackageType.None;
209
210 if (mbaPrereqInfoSymbolsByPackageId.TryGetValue(packageId, out var _))
211 {
212 isPrereq = true;
213 }
214
215 if (balPackageInfoSymbolsByPackageId.TryGetValue(packageId, out var balPackageInfoSymbol))
216 {
217 primaryPackageType = balPackageInfoSymbol.PrimaryPackageType;
218 }
219
220 if (packageSymbol.Permanent)
221 {
222 if (primaryPackageType != BalPrimaryPackageType.None)
223 {
224 this.Messaging.Write(BalErrors.IuibaPermanentPrimaryPackageType(packageSymbol.SourceLineNumbers));
225 }
226 else
227 {
228 if (!isPrereq)
229 {
230 var prereqInfoSymbol = section.AddSymbol(new WixMbaPrereqInformationSymbol(packageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Global, packageId))
231 {
232 PackageId = packageId,
233 });
234
235 mbaPrereqInfoSymbolsByPackageId.Add(packageId, prereqInfoSymbol);
236 }
237
238 this.VerifyIuibaPrereqPackage(packageSymbol);
239 }
240 }
241 else
242 {
243 if (isPrereq)
244 {
245 if (primaryPackageType == BalPrimaryPackageType.None)
246 {
247 this.Messaging.Write(BalErrors.IuibaNonPermanentPrereqPackage(packageSymbol.SourceLineNumbers));
248 }
249 else
250 {
251 this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(
252 packageSymbol.SourceLineNumbers,
253 packageSymbol.Type + "Package",
254 "PrereqPackage",
255 "yes",
256 "PrimaryPackageType"));
257 }
258 }
259 else if (primaryPackageType == BalPrimaryPackageType.None)
260 {
261 nonPermanentNonPrimaryPackages.Add(packageSymbol);
262 }
263 else if (packageSymbol.Type != WixBundlePackageType.Msi)
264 {
265 this.Messaging.Write(BalErrors.IuibaNonMsiPrimaryPackage(packageSymbol.SourceLineNumbers));
266 }
267 else if (!msiPackageSymbolsByPackageId.TryGetValue(packageId, out var msiPackageSymbol))
268 {
269 throw new WixException($"Missing WixBundleMsiPackageSymbol for package '{packageId}'");
270 }
271 else if (msiPackageSymbol.EnableFeatureSelection)
272 {
273 this.Messaging.Write(BalErrors.IuibaPrimaryPackageEnableFeatureSelection(packageSymbol.SourceLineNumbers));
274 }
275 else
276 {
277 if (primaryPackageType == BalPrimaryPackageType.Default)
278 {
279 if (defaultPrimaryPackage == null)
280 {
281 defaultPrimaryPackage = balPackageInfoSymbol;
282 }
283 else
284 {
285 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType(balPackageInfoSymbol.SourceLineNumbers, "default"));
286 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType2(defaultPrimaryPackage.SourceLineNumbers));
287 }
288 }
289 else if (balPackageInfoSymbol.PrimaryPackageType == BalPrimaryPackageType.X86)
290 {
291 if (x86PrimaryPackage == null)
292 {
293 x86PrimaryPackage = balPackageInfoSymbol;
294 }
295 else
296 {
297 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType(balPackageInfoSymbol.SourceLineNumbers, "x86"));
298 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType2(x86PrimaryPackage.SourceLineNumbers));
299 }
300 }
301 else if (balPackageInfoSymbol.PrimaryPackageType == BalPrimaryPackageType.X64)
302 {
303 if (x64PrimaryPackage == null)
304 {
305 x64PrimaryPackage = balPackageInfoSymbol;
306 }
307 else
308 {
309 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType(balPackageInfoSymbol.SourceLineNumbers, "x64"));
310 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType2(x64PrimaryPackage.SourceLineNumbers));
311 }
312 }
313 else if (balPackageInfoSymbol.PrimaryPackageType == BalPrimaryPackageType.ARM64)
314 {
315 if (arm64PrimaryPackage == null)
316 {
317 arm64PrimaryPackage = balPackageInfoSymbol;
318 }
319 else
320 {
321 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType(balPackageInfoSymbol.SourceLineNumbers, "arm64"));
322 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType2(arm64PrimaryPackage.SourceLineNumbers));
323 }
324 }
325 else
326 {
327 throw new NotImplementedException();
328 }
329
330 this.VerifyIuibaPrimaryPackage(packageSymbol, balPackageInfoSymbol);
331 }
332 }
333 }
334
335 if (defaultPrimaryPackage == null && nonPermanentNonPrimaryPackages.Count == 1)
336 {
337 var packageSymbol = nonPermanentNonPrimaryPackages[0];
338
339 if (packageSymbol.Type == WixBundlePackageType.Msi)
340 {
341 var packageId = packageSymbol.Id?.Id;
342 var msiPackageSymbol = section.Symbols.OfType<WixBundleMsiPackageSymbol>()
343 .SingleOrDefault(x => x.Id.Id == packageId);
344 if (!msiPackageSymbol.EnableFeatureSelection)
345 {
346 if (!balPackageInfoSymbolsByPackageId.TryGetValue(packageId, out var balPackageInfoSymbol))
347 {
348 balPackageInfoSymbol = section.AddSymbol(new WixBalPackageInfoSymbol(packageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Global, packageId))
349 {
350 PackageId = packageId,
351 });
352
353 balPackageInfoSymbolsByPackageId.Add(packageId, balPackageInfoSymbol);
354 }
355
356 balPackageInfoSymbol.PrimaryPackageType = BalPrimaryPackageType.Default;
357 defaultPrimaryPackage = balPackageInfoSymbol;
358 nonPermanentNonPrimaryPackages.RemoveAt(0);
359
360 this.VerifyIuibaPrimaryPackage(packageSymbol, balPackageInfoSymbol);
361 }
362 }
363 }
364
365 if (nonPermanentNonPrimaryPackages.Count > 0)
366 {
367 foreach (var packageSymbol in nonPermanentNonPrimaryPackages)
368 {
369 this.Messaging.Write(BalErrors.IuibaNonPermanentNonPrimaryPackage(packageSymbol.SourceLineNumbers));
370 }
371 }
372 else if (defaultPrimaryPackage == null)
373 {
374 this.Messaging.Write(BalErrors.MissingIUIPrimaryPackage());
375 }
376 else
377 {
378 var foundPrimaryPackage = false;
379 var chainPackageGroupSymbols = section.Symbols.OfType<WixGroupSymbol>()
380 .Where(x => x.ChildType == ComplexReferenceChildType.Package &&
381 x.ParentType == ComplexReferenceParentType.PackageGroup &&
382 x.ParentId == BurnConstants.BundleChainPackageGroupId);
383 foreach (var chainPackageGroupSymbol in chainPackageGroupSymbols)
384 {
385 var packageId = chainPackageGroupSymbol.ChildId;
386 if (balPackageInfoSymbolsByPackageId.TryGetValue(packageId, out var balPackageInfo) && balPackageInfo.PrimaryPackageType != BalPrimaryPackageType.None)
387 {
388 foundPrimaryPackage = true;
389 }
390 else if (foundPrimaryPackage && mbaPrereqInfoSymbolsByPackageId.TryGetValue(packageId, out var mbaPrereqInformationSymbol))
391 {
392 this.Messaging.Write(BalWarnings.IuibaPrereqPackageAfterPrimaryPackage(chainPackageGroupSymbol.SourceLineNumbers));
393 }
394 }
395 }
396 }
397
398 private void VerifyIuibaPrereqPackage(WixBundlePackageSymbol packageSymbol)
399 {
400 if (packageSymbol.Cache == BundleCacheType.Force)
401 {
402 this.Messaging.Write(BalWarnings.IuibaForceCachePrereq(packageSymbol.SourceLineNumbers));
403 }
404 }
405
406 private void VerifyIuibaPrimaryPackage(WixBundlePackageSymbol packageSymbol, WixBalPackageInfoSymbol balPackageInfoSymbol)
137 { 407 {
138 var balPackageInfoSymbols = section.Symbols.OfType<WixBalPackageInfoSymbol>().ToList(); 408 if (packageSymbol.InstallCondition != null)
139 foreach (var balPackageInfoSymbol in balPackageInfoSymbols) 409 {
410 this.Messaging.Write(BalWarnings.IuibaPrimaryPackageInstallCondition(packageSymbol.SourceLineNumbers));
411 }
412
413 if (balPackageInfoSymbol.DisplayInternalUICondition != null)
140 { 414 {
141 this.BackendHelper.ValidateBundleCondition(balPackageInfoSymbol.SourceLineNumbers, "*Package", "bal:DisplayInternalUICondition", balPackageInfoSymbol.DisplayInternalUICondition, BundleConditionPhase.Plan); 415 this.Messaging.Write(BalWarnings.IuibaPrimaryPackageDisplayInternalUICondition(packageSymbol.SourceLineNumbers));
142 } 416 }
143 } 417 }
144 418
@@ -161,10 +435,10 @@ namespace WixToolset.Bal
161 } 435 }
162 } 436 }
163 437
164 private void VerifyPrereqPackages(IntermediateSection section, bool isDNC) 438 private void VerifyPrereqPackages(IntermediateSection section, bool isDNC, bool isIuiBA)
165 { 439 {
166 var prereqInfoSymbols = section.Symbols.OfType<WixMbaPrereqInformationSymbol>().ToList(); 440 var prereqInfoSymbols = section.Symbols.OfType<WixMbaPrereqInformationSymbol>().ToList();
167 if (prereqInfoSymbols.Count == 0) 441 if (!isIuiBA && prereqInfoSymbols.Count == 0)
168 { 442 {
169 var message = isDNC ? BalErrors.MissingDNCPrereq() : BalErrors.MissingMBAPrereq(); 443 var message = isDNC ? BalErrors.MissingDNCPrereq() : BalErrors.MissingMBAPrereq();
170 this.Messaging.Write(message); 444 this.Messaging.Write(message);
diff --git a/src/ext/Bal/wixext/BalCompiler.cs b/src/ext/Bal/wixext/BalCompiler.cs
index 1721f252..bc2ba861 100644
--- a/src/ext/Bal/wixext/BalCompiler.cs
+++ b/src/ext/Bal/wixext/BalCompiler.cs
@@ -16,7 +16,8 @@ namespace WixToolset.Bal
16 /// </summary> 16 /// </summary>
17 public sealed class BalCompiler : BaseCompilerExtension 17 public sealed class BalCompiler : BaseCompilerExtension
18 { 18 {
19 private readonly Dictionary<string, WixMbaPrereqInformationSymbol> prereqInfoSymbolsByPackageId; 19 private readonly Dictionary<string, WixBalPackageInfoSymbol> packageInfoSymbolsByPackageId = new Dictionary<string, WixBalPackageInfoSymbol>();
20 private readonly Dictionary<string, WixMbaPrereqInformationSymbol> prereqInfoSymbolsByPackageId = new Dictionary<string, WixMbaPrereqInformationSymbol>();
20 21
21 private enum WixDotNetCoreBootstrapperApplicationHostTheme 22 private enum WixDotNetCoreBootstrapperApplicationHostTheme
22 { 23 {
@@ -32,6 +33,13 @@ namespace WixToolset.Bal
32 Standard, 33 Standard,
33 } 34 }
34 35
36 private enum WixInternalUIBootstrapperApplicationTheme
37 {
38 Unknown,
39 None,
40 Standard,
41 }
42
35 private enum WixStandardBootstrapperApplicationTheme 43 private enum WixStandardBootstrapperApplicationTheme
36 { 44 {
37 Unknown, 45 Unknown,
@@ -43,14 +51,6 @@ namespace WixToolset.Bal
43 RtfLicense, 51 RtfLicense,
44 } 52 }
45 53
46 /// <summary>
47 /// Instantiate a new BalCompiler.
48 /// </summary>
49 public BalCompiler()
50 {
51 this.prereqInfoSymbolsByPackageId = new Dictionary<string, WixMbaPrereqInformationSymbol>();
52 }
53
54 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/bal"; 54 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/bal";
55 55
56 /// <summary> 56 /// <summary>
@@ -83,6 +83,9 @@ namespace WixToolset.Bal
83 case "BootstrapperApplication": 83 case "BootstrapperApplication":
84 switch (element.Name.LocalName) 84 switch (element.Name.LocalName)
85 { 85 {
86 case "WixInternalUIBootstrapperApplication":
87 this.ParseWixInternalUIBootstrapperApplicationElement(intermediate, section, element);
88 break;
86 case "WixStandardBootstrapperApplication": 89 case "WixStandardBootstrapperApplication":
87 this.ParseWixStandardBootstrapperApplicationElement(intermediate, section, element); 90 this.ParseWixStandardBootstrapperApplicationElement(intermediate, section, element);
88 break; 91 break;
@@ -113,7 +116,6 @@ namespace WixToolset.Bal
113 public override void ParseAttribute(Intermediate intermediate, IntermediateSection section, XElement parentElement, XAttribute attribute, IDictionary<string, string> context) 116 public override void ParseAttribute(Intermediate intermediate, IntermediateSection section, XElement parentElement, XAttribute attribute, IDictionary<string, string> context)
114 { 117 {
115 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(parentElement); 118 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(parentElement);
116 WixMbaPrereqInformationSymbol prereqInfo;
117 119
118 switch (parentElement.Name.LocalName) 120 switch (parentElement.Name.LocalName)
119 { 121 {
@@ -137,42 +139,63 @@ namespace WixToolset.Bal
137 case "MsiPackage": 139 case "MsiPackage":
138 case "MspPackage": 140 case "MspPackage":
139 var displayInternalUICondition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); 141 var displayInternalUICondition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
140 section.AddSymbol(new WixBalPackageInfoSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, packageId)) 142 var packageInfo = this.GetBalPackageInfoSymbol(section, sourceLineNumbers, packageId);
141 { 143 packageInfo.DisplayInternalUICondition = displayInternalUICondition;
142 PackageId = packageId,
143 DisplayInternalUICondition = displayInternalUICondition,
144 });
145 break; 144 break;
146 default: 145 default:
147 this.ParseHelper.UnexpectedAttribute(parentElement, attribute); 146 this.ParseHelper.UnexpectedAttribute(parentElement, attribute);
148 break; 147 break;
149 } 148 }
150 break; 149 break;
151 case "PrereqLicenseFile": 150 case "PrimaryPackageType":
152 151 {
153 if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out prereqInfo)) 152 var primaryPackageType = BalPrimaryPackageType.None;
153 var primaryPackageTypeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
154 switch (primaryPackageTypeValue)
154 { 155 {
155 // at the time the extension attribute is parsed, the compiler might not yet have 156 case "default":
156 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element. 157 primaryPackageType = BalPrimaryPackageType.Default;
157 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage"); 158 break;
158 159 case "x86":
159 if (null != prereqPackage && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, prereqPackage)) 160 primaryPackageType = BalPrimaryPackageType.X86;
160 { 161 break;
161 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers) 162 case "x64":
162 { 163 primaryPackageType = BalPrimaryPackageType.X64;
163 PackageId = packageId, 164 break;
164 }); 165 case "arm64":
165 166 primaryPackageType = BalPrimaryPackageType.ARM64;
166 this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo); 167 break;
167 } 168 default:
168 else 169 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, parentElement.Name.LocalName, "PrimaryPackageType", primaryPackageTypeValue, "default", "x86", "x64", "arm64"));
169 {
170 this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile"));
171 break; 170 break;
172 }
173 } 171 }
174 172
175 if (null != prereqInfo.LicenseUrl) 173 // at the time the extension attribute is parsed, the compiler might not yet have
174 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element.
175 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage");
176 var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId);
177 if (prereqInfo != null)
178 {
179 this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqPackage", "yes", "PrimaryPackageType"));
180 }
181 else
182 {
183 var packageInfo = this.GetBalPackageInfoSymbol(section, sourceLineNumbers, packageId);
184 packageInfo.PrimaryPackageType = primaryPackageType;
185 }
186 break;
187 }
188 case "PrereqLicenseFile":
189 {
190 // at the time the extension attribute is parsed, the compiler might not yet have
191 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element.
192 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage");
193 var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId);
194 if (prereqInfo == null)
195 {
196 this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile"));
197 }
198 else if (null != prereqInfo.LicenseUrl)
176 { 199 {
177 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile", "PrereqLicenseUrl")); 200 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile", "PrereqLicenseUrl"));
178 } 201 }
@@ -181,31 +204,19 @@ namespace WixToolset.Bal
181 prereqInfo.LicenseFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); 204 prereqInfo.LicenseFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
182 } 205 }
183 break; 206 break;
207 }
184 case "PrereqLicenseUrl": 208 case "PrereqLicenseUrl":
209 {
210 // at the time the extension attribute is parsed, the compiler might not yet have
211 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element.
212 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage");
213 var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId);
185 214
186 if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out prereqInfo)) 215 if (prereqInfo == null)
187 { 216 {
188 // at the time the extension attribute is parsed, the compiler might not yet have 217 this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl"));
189 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element.
190 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage");
191
192 if (null != prereqPackage && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, prereqPackage))
193 {
194 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers)
195 {
196 PackageId = packageId,
197 });
198
199 this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo);
200 }
201 else
202 {
203 this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl"));
204 break;
205 }
206 } 218 }
207 219 else if (null != prereqInfo.LicenseFile)
208 if (null != prereqInfo.LicenseFile)
209 { 220 {
210 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl", "PrereqLicenseFile")); 221 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl", "PrereqLicenseFile"));
211 } 222 }
@@ -214,19 +225,9 @@ namespace WixToolset.Bal
214 prereqInfo.LicenseUrl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); 225 prereqInfo.LicenseUrl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
215 } 226 }
216 break; 227 break;
228 }
217 case "PrereqPackage": 229 case "PrereqPackage":
218 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute)) 230 this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, attribute, packageId);
219 {
220 if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out _))
221 {
222 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers)
223 {
224 PackageId = packageId,
225 });
226
227 this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo);
228 }
229 }
230 break; 231 break;
231 default: 232 default:
232 this.ParseHelper.UnexpectedAttribute(parentElement, attribute); 233 this.ParseHelper.UnexpectedAttribute(parentElement, attribute);
@@ -300,6 +301,41 @@ namespace WixToolset.Bal
300 } 301 }
301 } 302 }
302 303
304 private WixBalPackageInfoSymbol GetBalPackageInfoSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, string packageId)
305 {
306 if (!this.packageInfoSymbolsByPackageId.TryGetValue(packageId, out var packageInfo))
307 {
308 packageInfo = section.AddSymbol(new WixBalPackageInfoSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, packageId))
309 {
310 PackageId = packageId,
311 });
312
313 this.packageInfoSymbolsByPackageId.Add(packageId, packageInfo);
314 }
315
316 return packageInfo;
317 }
318
319 private WixMbaPrereqInformationSymbol GetMbaPrereqInformationSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute prereqAttribute, string packageId)
320 {
321 WixMbaPrereqInformationSymbol prereqInfo = null;
322
323 if (prereqAttribute != null && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, prereqAttribute))
324 {
325 if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out _))
326 {
327 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, packageId))
328 {
329 PackageId = packageId,
330 });
331
332 this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo);
333 }
334 }
335
336 return prereqInfo;
337 }
338
303 /// <summary> 339 /// <summary>
304 /// Parses a Condition element for Bundles. 340 /// Parses a Condition element for Bundles.
305 /// </summary> 341 /// </summary>
@@ -418,6 +454,101 @@ namespace WixToolset.Bal
418 } 454 }
419 } 455 }
420 456
457 private void ParseWixInternalUIBootstrapperApplicationElement(Intermediate intermediate, IntermediateSection section, XElement node)
458 {
459 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
460 WixInternalUIBootstrapperApplicationTheme? theme = null;
461 string themeFile = null;
462 string logoFile = null;
463 string localizationFile = null;
464
465 foreach (var attrib in node.Attributes())
466 {
467 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
468 {
469 switch (attrib.Name.LocalName)
470 {
471 case "LogoFile":
472 logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
473 break;
474 case "ThemeFile":
475 themeFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
476 break;
477 case "LocalizationFile":
478 localizationFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
479 break;
480 case "Theme":
481 var themeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
482 switch (themeValue)
483 {
484 case "none":
485 theme = WixInternalUIBootstrapperApplicationTheme.None;
486 break;
487 case "standard":
488 theme = WixInternalUIBootstrapperApplicationTheme.Standard;
489 break;
490 default:
491 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Theme", themeValue, "none", "standard"));
492 theme = WixInternalUIBootstrapperApplicationTheme.Unknown;
493 break;
494 }
495 break;
496 default:
497 this.ParseHelper.UnexpectedAttribute(node, attrib);
498 break;
499 }
500 }
501 else
502 {
503 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
504 }
505 }
506
507 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
508
509 if (!theme.HasValue)
510 {
511 theme = WixInternalUIBootstrapperApplicationTheme.Standard;
512 }
513
514 if (!this.Messaging.EncounteredError)
515 {
516 if (!String.IsNullOrEmpty(logoFile))
517 {
518 section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaLogo"))
519 {
520 Value = logoFile,
521 });
522 }
523
524 if (!String.IsNullOrEmpty(themeFile))
525 {
526 section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaThemeXml"))
527 {
528 Value = themeFile,
529 });
530 }
531
532 if (!String.IsNullOrEmpty(localizationFile))
533 {
534 section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaThemeWxl"))
535 {
536 Value = localizationFile,
537 });
538 }
539
540 var baId = "WixInternalUIBootstrapperApplication";
541 switch (theme)
542 {
543 case WixInternalUIBootstrapperApplicationTheme.Standard:
544 baId = "WixInternalUIBootstrapperApplication.Standard";
545 break;
546 }
547
548 this.CreateBARef(section, sourceLineNumbers, node, baId);
549 }
550 }
551
421 /// <summary> 552 /// <summary>
422 /// Parses a WixStandardBootstrapperApplication element for Bundles. 553 /// Parses a WixStandardBootstrapperApplication element for Bundles.
423 /// </summary> 554 /// </summary>
diff --git a/src/ext/Bal/wixext/BalErrors.cs b/src/ext/Bal/wixext/BalErrors.cs
index e9f68b24..a7a00a4b 100644
--- a/src/ext/Bal/wixext/BalErrors.cs
+++ b/src/ext/Bal/wixext/BalErrors.cs
@@ -18,11 +18,41 @@ namespace WixToolset.Bal
18 return Message(sourceLineNumbers, Ids.BAFunctionsPayloadRequiredInUXContainer, "The BAFunctions DLL Payload element must be located inside the BootstrapperApplication container."); 18 return Message(sourceLineNumbers, Ids.BAFunctionsPayloadRequiredInUXContainer, "The BAFunctions DLL Payload element must be located inside the BootstrapperApplication container.");
19 } 19 }
20 20
21 public static Message IuibaNonMsiPrimaryPackage(SourceLineNumber sourceLineNumbers)
22 {
23 return Message(sourceLineNumbers, Ids.IuibaNonMsiPrimaryPackage, "When using WixInternalUIBootstrapperApplication, each primary package must be an MsiPackage.");
24 }
25
26 public static Message IuibaNonPermanentNonPrimaryPackage(SourceLineNumber sourceLineNumbers)
27 {
28 return Message(sourceLineNumbers, Ids.IuibaNonPermanentNonPrimaryPackage, "When using WixInternalUIBootstrapperApplication, packages must either be non-permanent and have the bal:PrimaryPackageType attribute, or be permanent and have the bal:PrereqPackage attribute set to 'yes'.");
29 }
30
31 public static Message IuibaNonPermanentPrereqPackage(SourceLineNumber sourceLineNumbers)
32 {
33 return Message(sourceLineNumbers, Ids.IuibaNonPermanentPrereqPackage, "When using WixInternalUIBootstrapperApplication and bal:PrereqPackage is set to 'yes', the package must be permanent.");
34 }
35
36 public static Message IuibaPermanentPrimaryPackageType(SourceLineNumber sourceLineNumbers)
37 {
38 return Message(sourceLineNumbers, Ids.IuibaPermanentPrimaryPackageType, "When using WixInternalUIBootstrapperApplication, packages with the bal:PrimaryPackageType attribute must not be permanent.");
39 }
40
41 public static Message IuibaPrimaryPackageEnableFeatureSelection(SourceLineNumber sourceLineNumbers)
42 {
43 return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageEnableFeatureSelection, "When using WixInternalUIBootstrapperApplication, primary packages must not have feature selection enabled because it interferes with the user selecting feature through the MSI UI.");
44 }
45
21 public static Message MissingDNCPrereq() 46 public static Message MissingDNCPrereq()
22 { 47 {
23 return Message(null, Ids.MissingDNCPrereq, "There must be at least one PrereqPackage when using the DotNetCoreBootstrapperApplicationHost with SelfContainedDeployment set to \"no\"."); 48 return Message(null, Ids.MissingDNCPrereq, "There must be at least one PrereqPackage when using the DotNetCoreBootstrapperApplicationHost with SelfContainedDeployment set to \"no\".");
24 } 49 }
25 50
51 public static Message MissingIUIPrimaryPackage()
52 {
53 return Message(null, Ids.MissingIUIPrimaryPackage, "When using WixInternalUIBootstrapperApplication, there must be one package with bal:PrimaryPackageType=\"default\".");
54 }
55
26 public static Message MissingMBAPrereq() 56 public static Message MissingMBAPrereq()
27 { 57 {
28 return Message(null, Ids.MissingMBAPrereq, "There must be at least one PrereqPackage when using the ManagedBootstrapperApplicationHost.\nThis is typically done by using the WixNetFxExtension and referencing one of the NetFxAsPrereq package groups."); 58 return Message(null, Ids.MissingMBAPrereq, "There must be at least one PrereqPackage when using the ManagedBootstrapperApplicationHost.\nThis is typically done by using the WixNetFxExtension and referencing one of the NetFxAsPrereq package groups.");
@@ -38,6 +68,16 @@ namespace WixToolset.Bal
38 return Message(sourceLineNumbers, Ids.MultiplePrereqLicenses, "There may only be one package in the bundle that has either the PrereqLicenseFile attribute or the PrereqLicenseUrl attribute."); 68 return Message(sourceLineNumbers, Ids.MultiplePrereqLicenses, "There may only be one package in the bundle that has either the PrereqLicenseFile attribute or the PrereqLicenseUrl attribute.");
39 } 69 }
40 70
71 public static Message MultiplePrimaryPackageType(SourceLineNumber sourceLineNumbers, string primaryPackageType)
72 {
73 return Message(sourceLineNumbers, Ids.MultiplePrimaryPackageType, "There may only be one package in the bundle with PrimaryPackageType of '{0}'.", primaryPackageType);
74 }
75
76 public static Message MultiplePrimaryPackageType2(SourceLineNumber sourceLineNumbers)
77 {
78 return Message(sourceLineNumbers, Ids.MultiplePrimaryPackageType2, "The location of the package related to the previous error.");
79 }
80
41 public static Message NonUpperCaseOverridableVariable(SourceLineNumber sourceLineNumbers, string name, string expectedName) 81 public static Message NonUpperCaseOverridableVariable(SourceLineNumber sourceLineNumbers, string name, string expectedName)
42 { 82 {
43 return Message(sourceLineNumbers, Ids.NonUpperCaseOverridableVariable, "Overridable variable '{0}' must be '{1}' with Bundle/@CommandLineVariables value 'upperCase'.", name, expectedName); 83 return Message(sourceLineNumbers, Ids.NonUpperCaseOverridableVariable, "Overridable variable '{0}' must be '{1}' with Bundle/@CommandLineVariables value 'upperCase'.", name, expectedName);
@@ -62,6 +102,14 @@ namespace WixToolset.Bal
62 BAFunctionsPayloadRequiredInUXContainer = 6805, 102 BAFunctionsPayloadRequiredInUXContainer = 6805,
63 MissingDNCPrereq = 6806, 103 MissingDNCPrereq = 6806,
64 NonUpperCaseOverridableVariable = 6807, 104 NonUpperCaseOverridableVariable = 6807,
105 MissingIUIPrimaryPackage = 6808,
106 MultiplePrimaryPackageType = 6809,
107 MultiplePrimaryPackageType2 = 6810,
108 IuibaNonPermanentNonPrimaryPackage = 6811,
109 IuibaNonPermanentPrereqPackage = 6812,
110 IuibaPermanentPrimaryPackageType = 6813,
111 IuibaNonMsiPrimaryPackage = 6814,
112 IuibaPrimaryPackageEnableFeatureSelection = 6815,
65 } 113 }
66 } 114 }
67} 115}
diff --git a/src/ext/Bal/wixext/BalWarnings.cs b/src/ext/Bal/wixext/BalWarnings.cs
index 18b25062..96e7a523 100644
--- a/src/ext/Bal/wixext/BalWarnings.cs
+++ b/src/ext/Bal/wixext/BalWarnings.cs
@@ -8,6 +8,26 @@ namespace WixToolset.Bal
8 8
9 public static class BalWarnings 9 public static class BalWarnings
10 { 10 {
11 public static Message IuibaForceCachePrereq(SourceLineNumber sourceLineNumbers)
12 {
13 return Message(sourceLineNumbers, Ids.IuibaForceCachePrereq, "WixInternalUIBootstrapperApplication does not support the value of 'force' for Cache on prereq packages. Prereq packages are only cached when they need to be installed.");
14 }
15
16 public static Message IuibaPrereqPackageAfterPrimaryPackage(SourceLineNumber sourceLineNumbers)
17 {
18 return Message(sourceLineNumbers, Ids.IuibaPrereqPackageAfterPrimaryPackage, "When using WixInternalUIBootstrapperApplication, all prereq packages should be before the primary package in the chain. The prereq packages are always installed before the primary package.");
19 }
20
21 public static Message IuibaPrimaryPackageDisplayInternalUICondition(SourceLineNumber sourceLineNumbers)
22 {
23 return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageDisplayInternalUICondition, "WixInternalUIBootstrapperApplication ignores DisplayInternalUICondition for the primary package so that the MSI UI is always shown.");
24 }
25
26 public static Message IuibaPrimaryPackageInstallCondition(SourceLineNumber sourceLineNumbers)
27 {
28 return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageInstallCondition, "WixInternalUIBootstrapperApplication ignores InstallCondition for the primary package so that the MSI UI is always shown.");
29 }
30
11 public static Message UnmarkedBAFunctionsDLL(SourceLineNumber sourceLineNumbers) 31 public static Message UnmarkedBAFunctionsDLL(SourceLineNumber sourceLineNumbers)
12 { 32 {
13 return Message(sourceLineNumbers, Ids.UnmarkedBAFunctionsDLL, "WixStandardBootstrapperApplication doesn't automatically load BAFunctions.dll. Use the bal:BAFunctions attribute to indicate that it should be loaded."); 33 return Message(sourceLineNumbers, Ids.UnmarkedBAFunctionsDLL, "WixStandardBootstrapperApplication doesn't automatically load BAFunctions.dll. Use the bal:BAFunctions attribute to indicate that it should be loaded.");
@@ -26,6 +46,10 @@ namespace WixToolset.Bal
26 public enum Ids 46 public enum Ids
27 { 47 {
28 UnmarkedBAFunctionsDLL = 6501, 48 UnmarkedBAFunctionsDLL = 6501,
49 IuibaForceCachePrereq = 6502,
50 IuibaPrimaryPackageInstallCondition = 6503,
51 IuibaPrimaryPackageDisplayInternalUICondition = 6504,
52 IuibaPrereqPackageAfterPrimaryPackage = 6505,
29 } 53 }
30 } 54 }
31} 55}
diff --git a/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs b/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
index b09cb191..08d4ce4e 100644
--- a/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
+++ b/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
@@ -13,6 +13,7 @@ namespace WixToolset.Bal
13 { 13 {
14 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.PackageId), IntermediateFieldType.String), 14 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.PackageId), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.DisplayInternalUICondition), IntermediateFieldType.String), 15 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.DisplayInternalUICondition), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.PrimaryPackageType), IntermediateFieldType.Number),
16 }, 17 },
17 typeof(WixBalPackageInfoSymbol)); 18 typeof(WixBalPackageInfoSymbol));
18 } 19 }
@@ -26,6 +27,16 @@ namespace WixToolset.Bal.Symbols
26 { 27 {
27 PackageId, 28 PackageId,
28 DisplayInternalUICondition, 29 DisplayInternalUICondition,
30 PrimaryPackageType,
31 }
32
33 public enum BalPrimaryPackageType
34 {
35 None,
36 Default,
37 X86,
38 X64,
39 ARM64,
29 } 40 }
30 41
31 public class WixBalPackageInfoSymbol : IntermediateSymbol 42 public class WixBalPackageInfoSymbol : IntermediateSymbol
@@ -51,5 +62,11 @@ namespace WixToolset.Bal.Symbols
51 get => this.Fields[(int)WixBalPackageInfoSymbolFields.DisplayInternalUICondition].AsString(); 62 get => this.Fields[(int)WixBalPackageInfoSymbolFields.DisplayInternalUICondition].AsString();
52 set => this.Set((int)WixBalPackageInfoSymbolFields.DisplayInternalUICondition, value); 63 set => this.Set((int)WixBalPackageInfoSymbolFields.DisplayInternalUICondition, value);
53 } 64 }
65
66 public BalPrimaryPackageType PrimaryPackageType
67 {
68 get => (BalPrimaryPackageType)this.Fields[(int)WixBalPackageInfoSymbolFields.PrimaryPackageType].AsNumber();
69 set => this.Set((int)WixBalPackageInfoSymbolFields.PrimaryPackageType, (int)value);
70 }
54 } 71 }
55} 72}
diff --git a/src/ext/Bal/wixiuiba/WixInternalUIBootstrapperApplication.cpp b/src/ext/Bal/wixiuiba/WixInternalUIBootstrapperApplication.cpp
new file mode 100644
index 00000000..dbb97366
--- /dev/null
+++ b/src/ext/Bal/wixiuiba/WixInternalUIBootstrapperApplication.cpp
@@ -0,0 +1,918 @@
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 "BalBaseBootstrapperApplicationProc.h"
5#include "BalBaseBootstrapperApplication.h"
6
7static const LPCWSTR WIXIUIBA_WINDOW_CLASS = L"WixInternalUIBA";
8
9enum WM_WIXIUIBA
10{
11 WM_WIXIUIBA_DETECT_PACKAGES = WM_APP + 100,
12 WM_WIXIUIBA_PLAN_PACKAGES,
13 WM_WIXIUIBA_APPLY_PACKAGES,
14 WM_WIXIUIBA_DETECT_FOR_CLEANUP,
15 WM_WIXIUIBA_PLAN_PACKAGES_FOR_CLEANUP,
16};
17
18
19class CWixInternalUIBootstrapperApplication : public CBalBaseBootstrapperApplication
20{
21public: // IBootstrapperApplication
22 virtual STDMETHODIMP OnStartup()
23 {
24 HRESULT hr = S_OK;
25 DWORD dwUIThreadId = 0;
26
27 // create UI thread
28 m_hUiThread = ::CreateThread(NULL, 0, UiThreadProc, this, 0, &dwUIThreadId);
29 if (!m_hUiThread)
30 {
31 BalExitWithLastError(hr, "Failed to create UI thread.");
32 }
33
34 LExit:
35 return hr;
36 }
37
38
39 virtual STDMETHODIMP OnShutdown(
40 __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction
41 )
42 {
43 // wait for UI thread to terminate
44 if (m_hUiThread)
45 {
46 ::WaitForSingleObject(m_hUiThread, INFINITE);
47 ReleaseHandle(m_hUiThread);
48 }
49
50 if (m_fFailedToLoadPackage)
51 {
52 Assert(FAILED(m_hrFinal));
53 m_pPrereqData->hrFatalError = m_hrFinal;
54 BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load primary package as the BA. The bootstrapper application will be reloaded to show the error.");
55 *pAction = BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER;
56 }
57
58 return S_OK;
59 }
60
61
62 virtual STDMETHODIMP OnDetectPackageComplete(
63 __in_z LPCWSTR wzPackageId,
64 __in HRESULT hrStatus,
65 __in BOOTSTRAPPER_PACKAGE_STATE state,
66 __in BOOL fCached
67 )
68 {
69 BAL_INFO_PACKAGE* pPackage = NULL;
70
71 if (SUCCEEDED(hrStatus) && SUCCEEDED(BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage)) &&
72 BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT == pPackage->primaryPackageType)
73 {
74 BOOL fInstalled = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < state;
75
76 // Maybe modify the action state if the primary package is or is not already installed.
77 if (fInstalled && BOOTSTRAPPER_ACTION_INSTALL == m_command.action)
78 {
79 m_command.action = BOOTSTRAPPER_ACTION_MODIFY;
80 }
81 else if (!fInstalled && (BOOTSTRAPPER_ACTION_MODIFY == m_command.action || BOOTSTRAPPER_ACTION_REPAIR == m_command.action))
82 {
83 m_command.action = BOOTSTRAPPER_ACTION_INSTALL;
84 }
85
86 if (m_fApplied && !fInstalled && fCached)
87 {
88 m_fAutomaticRemoval = TRUE;
89 }
90 }
91
92 return __super::OnDetectPackageComplete(wzPackageId, hrStatus, state, fCached);
93 }
94
95
96 virtual STDMETHODIMP OnDetectComplete(
97 __in HRESULT hrStatus,
98 __in BOOL fEligibleForCleanup
99 )
100 {
101 if (m_fAutomaticRemoval && SUCCEEDED(hrStatus))
102 {
103 ::PostMessageW(m_hWnd, WM_WIXIUIBA_PLAN_PACKAGES_FOR_CLEANUP, 0, BOOTSTRAPPER_ACTION_UNINSTALL);
104 ExitFunction();
105 }
106 else if (m_fApplied)
107 {
108 ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0);
109 ExitFunction();
110 }
111
112 // If we're performing an action that modifies machine state then evaluate conditions.
113 BOOL fEvaluateConditions = SUCCEEDED(hrStatus) &&
114 (BOOTSTRAPPER_ACTION_LAYOUT < m_command.action && BOOTSTRAPPER_ACTION_UPDATE_REPLACE > m_command.action);
115
116 if (fEvaluateConditions)
117 {
118 hrStatus = EvaluateConditions();
119 }
120
121 if (SUCCEEDED(hrStatus))
122 {
123 ::PostMessageW(m_hWnd, WM_WIXIUIBA_PLAN_PACKAGES, 0, m_command.action);
124 }
125 else
126 {
127 SetLoadPackageFailure(hrStatus);
128 }
129
130 LExit:
131 return __super::OnDetectComplete(hrStatus, fEligibleForCleanup);
132 }
133
134
135 virtual STDMETHODIMP OnPlanPackageBegin(
136 __in_z LPCWSTR wzPackageId,
137 __in BOOTSTRAPPER_PACKAGE_STATE state,
138 __in BOOL fCached,
139 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition,
140 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition,
141 __in BOOTSTRAPPER_REQUEST_STATE recommendedState,
142 __in BOOTSTRAPPER_CACHE_TYPE recommendedCacheType,
143 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState,
144 __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType,
145 __inout BOOL* pfCancel
146 )
147 {
148 HRESULT hr = S_OK;
149 BAL_INFO_PACKAGE* pPackage = NULL;
150
151 hr = BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage);
152 if (FAILED(hr))
153 {
154 // Non-chain package, keep default.
155 }
156 else if (BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT != pPackage->primaryPackageType)
157 {
158 // Only the primary package should be cached or executed.
159 if (BOOTSTRAPPER_CACHE_TYPE_FORCE == *pRequestedCacheType)
160 {
161 *pRequestedCacheType = BOOTSTRAPPER_CACHE_TYPE_KEEP;
162 }
163
164 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
165 }
166 else if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display && !m_fAutomaticRemoval)
167 {
168 // Make sure the MSI UI is shown regardless of the current state of the package.
169 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
170 }
171
172 return __super::OnPlanPackageBegin(wzPackageId, state, fCached, installCondition, repairCondition, recommendedState, recommendedCacheType, pRequestState, pRequestedCacheType, pfCancel);
173 }
174
175
176 virtual STDMETHODIMP OnPlanMsiPackage(
177 __in_z LPCWSTR wzPackageId,
178 __in BOOL fExecute,
179 __in BOOTSTRAPPER_ACTION_STATE action,
180 __in BOOTSTRAPPER_MSI_FILE_VERSIONING recommendedFileVersioning,
181 __inout BOOL* pfCancel,
182 __inout BURN_MSI_PROPERTY* pActionMsiProperty,
183 __inout INSTALLUILEVEL* pUiLevel,
184 __inout BOOL* pfDisableExternalUiHandler,
185 __inout BOOTSTRAPPER_MSI_FILE_VERSIONING* pFileVersioning
186 )
187 {
188 INSTALLUILEVEL uiLevel = INSTALLUILEVEL_NOCHANGE;
189
190 if (m_fAutomaticRemoval)
191 {
192 ExitFunction();
193 }
194
195 switch (m_command.display)
196 {
197 case BOOTSTRAPPER_DISPLAY_FULL:
198 uiLevel = INSTALLUILEVEL_FULL;
199 break;
200
201 case BOOTSTRAPPER_DISPLAY_PASSIVE:
202 uiLevel = INSTALLUILEVEL_REDUCED;
203 break;
204 }
205
206 if (INSTALLUILEVEL_NOCHANGE != uiLevel)
207 {
208 *pUiLevel = uiLevel;
209 }
210
211 *pActionMsiProperty = BURN_MSI_PROPERTY_NONE;
212 *pfDisableExternalUiHandler = TRUE;
213
214 LExit:
215 return __super::OnPlanMsiPackage(wzPackageId, fExecute, action, recommendedFileVersioning, pfCancel, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler, pFileVersioning);
216 }
217
218
219 virtual STDMETHODIMP OnPlanComplete(
220 __in HRESULT hrStatus
221 )
222 {
223 if (SUCCEEDED(hrStatus))
224 {
225 ::PostMessageW(m_hWnd, WM_WIXIUIBA_APPLY_PACKAGES, 0, 0);
226 }
227 else if (m_fAutomaticRemoval)
228 {
229 ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0);
230 }
231 else
232 {
233 SetLoadPackageFailure(hrStatus);
234 }
235
236 return __super::OnPlanComplete(hrStatus);
237 }
238
239
240 virtual STDMETHODIMP OnApplyBegin(
241 __in DWORD dwPhaseCount,
242 __inout BOOL* pfCancel
243 )
244 {
245 m_fApplying = TRUE;
246 return __super::OnApplyBegin(dwPhaseCount, pfCancel);
247 }
248
249
250 virtual STDMETHODIMP OnCacheComplete(
251 __in HRESULT hrStatus
252 )
253 {
254 if (FAILED(hrStatus) && !m_fAutomaticRemoval)
255 {
256 SetLoadPackageFailure(hrStatus);
257 }
258
259 return __super::OnCacheComplete(hrStatus);
260 }
261
262
263 virtual STDMETHODIMP OnExecuteBegin(
264 __in DWORD cExecutingPackages,
265 __in BOOL* pfCancel
266 )
267 {
268 m_pEngine->CloseSplashScreen();
269
270 return __super::OnExecuteBegin(cExecutingPackages, pfCancel);
271 }
272
273
274 virtual STDMETHODIMP OnApplyComplete(
275 __in HRESULT hrStatus,
276 __in BOOTSTRAPPER_APPLY_RESTART restart,
277 __in BOOTSTRAPPER_APPLYCOMPLETE_ACTION recommendation,
278 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
279 )
280 {
281 HRESULT hr = __super::OnApplyComplete(hrStatus, restart, recommendation, pAction);
282
283 *pAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
284 m_fApplying = FALSE;
285
286 if (m_fAutomaticRemoval)
287 {
288 ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0);
289 }
290 else
291 {
292 m_restartResult = restart; // remember the restart result so we return the correct error code.
293 m_fApplied = TRUE;
294
295 if (FAILED(hrStatus))
296 {
297 m_hrFinal = hrStatus;
298 }
299
300 ::PostMessageW(m_hWnd, WM_WIXIUIBA_DETECT_FOR_CLEANUP, 0, 0);
301 }
302
303 return hr;
304 }
305
306
307public: //CBalBaseBootstrapperApplication
308 virtual STDMETHODIMP Initialize(
309 __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs
310 )
311 {
312 HRESULT hr = S_OK;
313
314 hr = __super::Initialize(pCreateArgs);
315 BalExitOnFailure(hr, "CBalBaseBootstrapperApplication initialization failed.");
316
317 memcpy_s(&m_command, sizeof(m_command), pCreateArgs->pCommand, sizeof(BOOTSTRAPPER_COMMAND));
318 memcpy_s(&m_createArgs, sizeof(m_createArgs), pCreateArgs, sizeof(BOOTSTRAPPER_CREATE_ARGS));
319 m_createArgs.pCommand = &m_command;
320
321 LExit:
322 return hr;
323 }
324
325 void Uninitialize(
326 __in const BOOTSTRAPPER_DESTROY_ARGS* /*pArgs*/,
327 __in BOOTSTRAPPER_DESTROY_RESULTS* /*pResults*/
328 )
329 {
330 }
331
332
333private:
334 //
335 // UiThreadProc - entrypoint for UI thread.
336 //
337 static DWORD WINAPI UiThreadProc(
338 __in LPVOID pvContext
339 )
340 {
341 HRESULT hr = S_OK;
342 CWixInternalUIBootstrapperApplication* pThis = (CWixInternalUIBootstrapperApplication*)pvContext;
343 BOOL fComInitialized = FALSE;
344 BOOL fRet = FALSE;
345 MSG msg = { };
346 DWORD dwQuit = 0;
347
348 // Initialize COM and theme.
349 hr = ::CoInitialize(NULL);
350 BalExitOnFailure(hr, "Failed to initialize COM.");
351 fComInitialized = TRUE;
352
353 hr = pThis->InitializeData();
354 BalExitOnFailure(hr, "Failed to initialize data in bootstrapper application.");
355
356 // Create main window.
357 hr = pThis->CreateMainWindow();
358 BalExitOnFailure(hr, "Failed to create main window.");
359
360 ::PostMessageW(pThis->m_hWnd, WM_WIXIUIBA_DETECT_PACKAGES, 0, 0);
361
362 // message pump
363 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
364 {
365 if (-1 == fRet)
366 {
367 hr = E_UNEXPECTED;
368 BalExitOnFailure(hr, "Unexpected return value from message pump.");
369 }
370 else if (!::IsDialogMessageW(pThis->m_hWnd, &msg))
371 {
372 ::TranslateMessage(&msg);
373 ::DispatchMessageW(&msg);
374 }
375 }
376
377 // Succeeded thus far, check to see if anything went wrong while actually
378 // executing changes.
379 if (FAILED(pThis->m_hrFinal))
380 {
381 hr = pThis->m_hrFinal;
382 }
383 else if (pThis->CheckCanceled())
384 {
385 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
386 }
387
388 LExit:
389 // destroy main window
390 pThis->DestroyMainWindow();
391
392 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == pThis->m_restartResult)
393 {
394 dwQuit = ERROR_SUCCESS_REBOOT_INITIATED;
395 }
396 else if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == pThis->m_restartResult)
397 {
398 dwQuit = ERROR_SUCCESS_REBOOT_REQUIRED;
399 }
400 else if (SEVERITY_ERROR == HRESULT_SEVERITY(hr) && FACILITY_WIN32 == HRESULT_FACILITY(hr))
401 {
402 // Convert Win32 HRESULTs back to the error code.
403 dwQuit = HRESULT_CODE(hr);
404 }
405 else
406 {
407 dwQuit = hr;
408 }
409
410 // initiate engine shutdown
411 pThis->m_pEngine->Quit(dwQuit);
412
413 // uninitialize COM
414 if (fComInitialized)
415 {
416 ::CoUninitialize();
417 }
418
419 return hr;
420 }
421
422
423 //
424 // InitializeData - initializes all the package and prerequisite information.
425 //
426 HRESULT InitializeData()
427 {
428 HRESULT hr = S_OK;
429 IXMLDOMDocument* pixdManifest = NULL;
430
431 hr = BalManifestLoad(m_hModule, &pixdManifest);
432 BalExitOnFailure(hr, "Failed to load bootstrapper application manifest.");
433
434 hr = BalInfoParseFromXml(&m_Bundle, pixdManifest);
435 BalExitOnFailure(hr, "Failed to load bundle information.");
436
437 hr = EnsureSinglePrimaryPackage();
438 BalExitOnFailure(hr, "Failed to ensure single primary package.");
439
440 hr = ProcessCommandLine();
441 ExitOnFailure(hr, "Unknown commandline parameters.");
442
443 hr = BalConditionsParseFromXml(&m_Conditions, pixdManifest, NULL);
444 BalExitOnFailure(hr, "Failed to load conditions from XML.");
445
446 LExit:
447 ReleaseObject(pixdManifest);
448
449 return hr;
450 }
451
452
453 //
454 // ProcessCommandLine - process the provided command line arguments.
455 //
456 HRESULT ProcessCommandLine()
457 {
458 HRESULT hr = S_OK;
459 int argc = 0;
460 LPWSTR* argv = NULL;
461
462 argc = m_BalInfoCommand.cUnknownArgs;
463 argv = m_BalInfoCommand.rgUnknownArgs;
464
465 for (int i = 0; i < argc; ++i)
466 {
467 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]);
468 }
469
470 hr = BalSetOverridableVariablesFromEngine(&m_Bundle.overridableVariables, &m_BalInfoCommand, m_pEngine);
471 BalExitOnFailure(hr, "Failed to set overridable variables from the command line.");
472
473 LExit:
474 return hr;
475 }
476
477 HRESULT EnsureSinglePrimaryPackage()
478 {
479 HRESULT hr = S_OK;
480 BAL_INFO_PACKAGE* pDefaultPackage = NULL;
481 BOOL fPrimaryArchSpecific = FALSE;
482 USHORT usNativeMachine = 0;
483 BAL_INFO_PRIMARY_PACKAGE_TYPE nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_NONE;
484
485 hr = ProcNativeMachine(::GetCurrentProcess(), &usNativeMachine);
486 BalExitOnFailure(hr, "Failed to get native machine value.");
487
488 if (S_FALSE != hr)
489 {
490 switch (usNativeMachine)
491 {
492 case IMAGE_FILE_MACHINE_I386:
493 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X86;
494 break;
495 case IMAGE_FILE_MACHINE_AMD64:
496 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X64;
497 break;
498 case IMAGE_FILE_MACHINE_ARM64:
499 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_ARM64;
500 break;
501 }
502 }
503 else
504 {
505#if !defined(_WIN64)
506 BOOL fIsWow64 = FALSE;
507
508 ProcWow64(::GetCurrentProcess(), &fIsWow64);
509 if (!fIsWow64)
510 {
511 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X86;
512 }
513 else
514#endif
515 {
516 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X64;
517 }
518 }
519
520 for (DWORD i = 0; i < m_Bundle.packages.cPackages; ++i)
521 {
522 BAL_INFO_PACKAGE* pPackage = m_Bundle.packages.rgPackages + i;
523
524 if (BAL_INFO_PRIMARY_PACKAGE_TYPE_NONE == pPackage->primaryPackageType)
525 {
526 // Skip.
527 }
528 else if (nativeType == pPackage->primaryPackageType)
529 {
530 if (fPrimaryArchSpecific)
531 {
532 BalExitWithRootFailure(hr, E_INVALIDDATA, "Bundle contains multiple primary packages for same architecture: %u.", nativeType);
533 }
534
535 pPackage->primaryPackageType = BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT;
536 fPrimaryArchSpecific = TRUE;
537 }
538 else if (BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT == pPackage->primaryPackageType)
539 {
540 if (pDefaultPackage)
541 {
542 BalExitWithRootFailure(hr, E_INVALIDDATA, "Bundle contains multiple default primary packages.");
543 }
544
545 pDefaultPackage = pPackage;
546 }
547 }
548
549 BalExitOnNull(pDefaultPackage, hr, E_INVALIDSTATE, "Bundle did not contain default primary package.");
550
551 if (fPrimaryArchSpecific)
552 {
553 pDefaultPackage->primaryPackageType = BAL_INFO_PRIMARY_PACKAGE_TYPE_NONE;
554 }
555
556 LExit:
557 return hr;
558 }
559
560
561 //
562 // CreateMainWindow - creates the main install window.
563 //
564 HRESULT CreateMainWindow()
565 {
566 HRESULT hr = S_OK;
567 WNDCLASSW wc = { };
568 DWORD dwWindowStyle = WS_POPUP;
569
570 wc.lpfnWndProc = CWixInternalUIBootstrapperApplication::WndProc;
571 wc.hInstance = m_hModule;
572 wc.lpszClassName = WIXIUIBA_WINDOW_CLASS;
573
574 if (!::RegisterClassW(&wc))
575 {
576 ExitWithLastError(hr, "Failed to register window.");
577 }
578
579 m_fRegistered = TRUE;
580
581 // If the UI should be visible, allow it to be visible and activated so we are the foreground window.
582 // This allows the UAC prompt and MSI UI to automatically be activated.
583 if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display)
584 {
585 dwWindowStyle |= WS_VISIBLE;
586 }
587
588 m_hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, dwWindowStyle, 0, 0, 0, 0, HWND_DESKTOP, NULL, m_hModule, this);
589 ExitOnNullWithLastError(m_hWnd, hr, "Failed to create window.");
590
591 LExit:
592 return hr;
593 }
594
595 //
596 // DestroyMainWindow - clean up all the window registration.
597 //
598 void DestroyMainWindow()
599 {
600 if (::IsWindow(m_hWnd))
601 {
602 ::DestroyWindow(m_hWnd);
603 m_hWnd = NULL;
604 }
605
606 if (m_fRegistered)
607 {
608 ::UnregisterClassW(WIXIUIBA_WINDOW_CLASS, m_hModule);
609 m_fRegistered = FALSE;
610 }
611 }
612
613 //
614 // WndProc - standard windows message handler.
615 //
616 static LRESULT CALLBACK WndProc(
617 __in HWND hWnd,
618 __in UINT uMsg,
619 __in WPARAM wParam,
620 __in LPARAM lParam
621 )
622 {
623#pragma warning(suppress:4312)
624 CWixInternalUIBootstrapperApplication* pBA = reinterpret_cast<CWixInternalUIBootstrapperApplication*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
625
626 switch (uMsg)
627 {
628 case WM_NCCREATE:
629 {
630 LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
631 pBA = reinterpret_cast<CWixInternalUIBootstrapperApplication*>(lpcs->lpCreateParams);
632#pragma warning(suppress:4244)
633 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));
634 }
635 break;
636
637 case WM_NCDESTROY:
638 {
639 LRESULT lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
640 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
641 ::PostQuitMessage(0);
642 return lres;
643 }
644
645 case WM_CLOSE:
646 // If the user chose not to close, do *not* let the default window proc handle the message.
647 if (!pBA->OnClose())
648 {
649 return 0;
650 }
651 break;
652
653 case WM_WIXIUIBA_DETECT_PACKAGES: __fallthrough;
654 case WM_WIXIUIBA_DETECT_FOR_CLEANUP:
655 pBA->OnDetect();
656 return 0;
657
658 case WM_WIXIUIBA_PLAN_PACKAGES:
659 case WM_WIXIUIBA_PLAN_PACKAGES_FOR_CLEANUP:
660 pBA->OnPlan(static_cast<BOOTSTRAPPER_ACTION>(lParam));
661 return 0;
662
663 case WM_WIXIUIBA_APPLY_PACKAGES:
664 pBA->OnApply();
665 return 0;
666 }
667
668 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
669 }
670
671
672 //
673 // OnDetect - start the processing of packages.
674 //
675 void OnDetect()
676 {
677 HRESULT hr = S_OK;
678
679 hr = m_pEngine->Detect();
680 BalExitOnFailure(hr, "Failed to start detecting chain.");
681
682 LExit:
683 if (FAILED(hr))
684 {
685 SetLoadPackageFailure(hr);
686 }
687 }
688
689
690 //
691 // OnPlan - plan the detected changes.
692 //
693 void OnPlan(
694 __in BOOTSTRAPPER_ACTION action
695 )
696 {
697 HRESULT hr = S_OK;
698
699 m_plannedAction = action;
700
701 hr = m_pEngine->Plan(action);
702 BalExitOnFailure(hr, "Failed to start planning packages.");
703
704 LExit:
705 if (FAILED(hr))
706 {
707 SetLoadPackageFailure(hr);
708 }
709 }
710
711
712 //
713 // OnApply - apply the packages.
714 //
715 void OnApply()
716 {
717 HRESULT hr = S_OK;
718
719 hr = m_pEngine->Apply(m_hWnd);
720 BalExitOnFailure(hr, "Failed to start applying packages.");
721
722 LExit:
723 if (FAILED(hr))
724 {
725 SetLoadPackageFailure(hr);
726 }
727 }
728
729
730 //
731 // OnClose - called when the window is trying to be closed.
732 //
733 BOOL OnClose()
734 {
735 BOOL fClose = FALSE;
736
737 // If we've already applied, just close.
738 if (m_fApplied)
739 {
740 fClose = TRUE;
741 }
742 else
743 {
744 PromptCancel(m_hWnd, TRUE, NULL, NULL);
745
746 // If we're inside Apply then we never close, we just cancel to let rollback occur.
747 fClose = !m_fApplying;
748 }
749
750 return fClose;
751 }
752
753
754 HRESULT EvaluateConditions()
755 {
756 HRESULT hr = S_OK;
757 BOOL fResult = FALSE;
758
759 for (DWORD i = 0; i < m_Conditions.cConditions; ++i)
760 {
761 BAL_CONDITION* pCondition = m_Conditions.rgConditions + i;
762
763 hr = BalConditionEvaluate(pCondition, m_pEngine, &fResult, &m_sczFailedMessage);
764 BalExitOnFailure(hr, "Failed to evaluate condition.");
765
766 if (!fResult)
767 {
768 hr = E_WIXSTDBA_CONDITION_FAILED;
769 BalExitOnFailure(hr, "%ls", m_sczFailedMessage);
770 }
771 }
772
773 ReleaseNullStrSecure(m_sczFailedMessage);
774
775 LExit:
776 return hr;
777 }
778
779
780 void SetLoadPackageFailure(
781 __in HRESULT hrStatus
782 )
783 {
784 Assert(FAILED(hrStatus));
785
786 if (!m_fApplied)
787 {
788 m_hrFinal = hrStatus;
789 m_fFailedToLoadPackage = TRUE;
790 }
791
792 // Quietly exit.
793 ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0);
794 }
795
796
797public:
798 //
799 // Constructor - initialize member variables.
800 //
801 CWixInternalUIBootstrapperApplication(
802 __in HMODULE hModule,
803 __in_opt PREQBA_DATA* pPrereqData,
804 __in IBootstrapperEngine* pEngine
805 ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000)
806 {
807 m_hModule = hModule;
808 m_command = { };
809 m_createArgs = { };
810
811 m_plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN;
812
813 m_Bundle = { };
814 m_Conditions = { };
815 m_sczConfirmCloseMessage = NULL;
816 m_sczFailedMessage = NULL;
817
818 m_hUiThread = NULL;
819 m_fRegistered = FALSE;
820 m_hWnd = NULL;
821
822 m_hrFinal = S_OK;
823
824 m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;
825
826 m_fApplying = FALSE;
827 m_fApplied = FALSE;
828 m_fAutomaticRemoval = FALSE;
829 m_fFailedToLoadPackage = FALSE;
830 m_pPrereqData = pPrereqData;
831
832 pEngine->AddRef();
833 m_pEngine = pEngine;
834 }
835
836
837 //
838 // Destructor - release member variables.
839 //
840 ~CWixInternalUIBootstrapperApplication()
841 {
842 ReleaseStr(m_sczFailedMessage);
843 ReleaseStr(m_sczConfirmCloseMessage);
844 BalConditionsUninitialize(&m_Conditions);
845 BalInfoUninitialize(&m_Bundle);
846
847 ReleaseNullObject(m_pEngine);
848 }
849
850private:
851 HMODULE m_hModule;
852 BOOTSTRAPPER_CREATE_ARGS m_createArgs;
853 BOOTSTRAPPER_COMMAND m_command;
854 IBootstrapperEngine* m_pEngine;
855 BOOTSTRAPPER_ACTION m_plannedAction;
856
857 BAL_INFO_BUNDLE m_Bundle;
858 BAL_CONDITIONS m_Conditions;
859 LPWSTR m_sczFailedMessage;
860 LPWSTR m_sczConfirmCloseMessage;
861
862 HANDLE m_hUiThread;
863 BOOL m_fRegistered;
864 HWND m_hWnd;
865
866 HRESULT m_hrFinal;
867
868 BOOTSTRAPPER_APPLY_RESTART m_restartResult;
869
870 BOOL m_fApplying;
871 BOOL m_fApplied;
872 BOOL m_fAutomaticRemoval;
873 BOOL m_fFailedToLoadPackage;
874 PREQBA_DATA* m_pPrereqData;
875};
876
877
878//
879// CreateBootstrapperApplication - creates a new IBootstrapperApplication object.
880//
881HRESULT CreateBootstrapperApplication(
882 __in HMODULE hModule,
883 __in_opt PREQBA_DATA* pPrereqData,
884 __in IBootstrapperEngine* pEngine,
885 __in const BOOTSTRAPPER_CREATE_ARGS* pArgs,
886 __inout BOOTSTRAPPER_CREATE_RESULTS* pResults,
887 __out IBootstrapperApplication** ppApplication
888 )
889{
890 HRESULT hr = S_OK;
891 CWixInternalUIBootstrapperApplication* pApplication = NULL;
892
893 pApplication = new CWixInternalUIBootstrapperApplication(hModule, pPrereqData, pEngine);
894 BalExitOnNull(pApplication, hr, E_OUTOFMEMORY, "Failed to create new InternalUI bootstrapper application object.");
895
896 hr = pApplication->Initialize(pArgs);
897 ExitOnFailure(hr, "CWixInternalUIBootstrapperApplication initialization failed.");
898
899 pResults->pfnBootstrapperApplicationProc = BalBaseBootstrapperApplicationProc;
900 pResults->pvBootstrapperApplicationProcContext = pApplication;
901 *ppApplication = pApplication;
902 pApplication = NULL;
903
904LExit:
905 ReleaseObject(pApplication);
906 return hr;
907}
908
909
910void DestroyBootstrapperApplication(
911 __in IBootstrapperApplication* pApplication,
912 __in const BOOTSTRAPPER_DESTROY_ARGS* pArgs,
913 __inout BOOTSTRAPPER_DESTROY_RESULTS* pResults
914 )
915{
916 CWixInternalUIBootstrapperApplication* pBA = (CWixInternalUIBootstrapperApplication*)pApplication;
917 pBA->Uninitialize(pArgs, pResults);
918}
diff --git a/src/ext/Bal/wixiuiba/WixInternalUIBootstrapperApplication.h b/src/ext/Bal/wixiuiba/WixInternalUIBootstrapperApplication.h
new file mode 100644
index 00000000..b0b782dd
--- /dev/null
+++ b/src/ext/Bal/wixiuiba/WixInternalUIBootstrapperApplication.h
@@ -0,0 +1,18 @@
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
5HRESULT CreateBootstrapperApplication(
6 __in HMODULE hModule,
7 __in_opt PREQBA_DATA* pPrereqData,
8 __in IBootstrapperEngine* pEngine,
9 __in const BOOTSTRAPPER_CREATE_ARGS* pArgs,
10 __inout BOOTSTRAPPER_CREATE_RESULTS* pResults,
11 __out IBootstrapperApplication** ppApplication
12 );
13
14void DestroyBootstrapperApplication(
15 __in IBootstrapperApplication* pApplication,
16 __in const BOOTSTRAPPER_DESTROY_ARGS* pArgs,
17 __inout BOOTSTRAPPER_DESTROY_RESULTS* pResults
18 );
diff --git a/src/ext/Bal/wixiuiba/precomp.cpp b/src/ext/Bal/wixiuiba/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/ext/Bal/wixiuiba/precomp.cpp
@@ -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"
diff --git a/src/ext/Bal/wixiuiba/precomp.h b/src/ext/Bal/wixiuiba/precomp.h
new file mode 100644
index 00000000..89ec6eab
--- /dev/null
+++ b/src/ext/Bal/wixiuiba/precomp.h
@@ -0,0 +1,30 @@
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
8#include <dutil.h>
9#include <dictutil.h>
10#include <locutil.h>
11#include <osutil.h>
12#include <pathutil.h>
13#include <procutil.h>
14#include <regutil.h>
15#include <strutil.h>
16#include <xmlutil.h>
17
18#include <BootstrapperEngine.h>
19#include <BootstrapperApplication.h>
20#include <IBootstrapperEngine.h>
21#include <IBootstrapperApplication.h>
22
23#include <balutil.h>
24#include <balinfo.h>
25#include <balcondition.h>
26
27#include <preqba.h>
28
29#include "WixInternalUIBootstrapperApplication.h"
30#include "wixiuiba.h"
diff --git a/src/ext/Bal/wixiuiba/wixiuiba.cpp b/src/ext/Bal/wixiuiba/wixiuiba.cpp
new file mode 100644
index 00000000..3e751893
--- /dev/null
+++ b/src/ext/Bal/wixiuiba/wixiuiba.cpp
@@ -0,0 +1,192 @@
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
5static INTERNAL_UI_BA_STATE vstate = { };
6
7
8// internal function declarations
9
10static HRESULT LoadModulePaths(
11 __in INTERNAL_UI_BA_STATE* pState
12 );
13static HRESULT LoadInternalUIBAConfiguration(
14 __in INTERNAL_UI_BA_STATE* pState,
15 __in const BOOTSTRAPPER_CREATE_ARGS* pArgs
16 );
17static HRESULT CreatePrerequisiteBA(
18 __in INTERNAL_UI_BA_STATE* pState,
19 __in IBootstrapperEngine* pEngine,
20 __in const BOOTSTRAPPER_CREATE_ARGS* pArgs,
21 __inout BOOTSTRAPPER_CREATE_RESULTS* pResults
22 );
23
24
25// function definitions
26
27extern "C" BOOL WINAPI DllMain(
28 __in HINSTANCE hInstance,
29 __in DWORD dwReason,
30 __in LPVOID /*pvReserved*/
31 )
32{
33 switch (dwReason)
34 {
35 case DLL_PROCESS_ATTACH:
36 ::DisableThreadLibraryCalls(hInstance);
37 vstate.hInstance = hInstance;
38 break;
39
40 case DLL_PROCESS_DETACH:
41 vstate.hInstance = NULL;
42 break;
43 }
44
45 return TRUE;
46}
47
48// Note: This function assumes that COM was already initialized on the thread.
49extern "C" HRESULT WINAPI BootstrapperApplicationCreate(
50 __in const BOOTSTRAPPER_CREATE_ARGS* pArgs,
51 __inout BOOTSTRAPPER_CREATE_RESULTS* pResults
52 )
53{
54 HRESULT hr = S_OK;
55 IBootstrapperEngine* pEngine = NULL;
56
57 hr = BalInitializeFromCreateArgs(pArgs, &pEngine);
58 ExitOnFailure(hr, "Failed to initialize Bal.");
59
60 if (!vstate.fInitialized)
61 {
62 hr = XmlInitialize();
63 BalExitOnFailure(hr, "Failed to initialize XML.");
64
65 hr = LoadModulePaths(&vstate);
66 BalExitOnFailure(hr, "Failed to load the module paths.");
67
68 hr = LoadInternalUIBAConfiguration(&vstate, pArgs);
69 BalExitOnFailure(hr, "Failed to get the InternalUIBA configuration.");
70
71 vstate.fInitialized = TRUE;
72 }
73
74 if (vstate.prereqData.fAlwaysInstallPrereqs && !vstate.prereqData.fCompleted ||
75 FAILED(vstate.prereqData.hrFatalError))
76 {
77 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application.");
78
79 hr = CreatePrerequisiteBA(&vstate, pEngine, pArgs, pResults);
80 BalExitOnFailure(hr, "Failed to create the pre-requisite bootstrapper application.");
81 }
82 else
83 {
84 hr = CreateBootstrapperApplication(vstate.hInstance, &vstate.prereqData, pEngine, pArgs, pResults, &vstate.pApplication);
85 BalExitOnFailure(hr, "Failed to create bootstrapper application interface.");
86 }
87
88LExit:
89 ReleaseNullObject(pEngine);
90
91 return hr;
92}
93
94extern "C" void WINAPI BootstrapperApplicationDestroy(
95 __in const BOOTSTRAPPER_DESTROY_ARGS* pArgs,
96 __in BOOTSTRAPPER_DESTROY_RESULTS* pResults
97 )
98{
99 BOOTSTRAPPER_DESTROY_RESULTS childResults = { };
100
101 if (vstate.hPrereqModule)
102 {
103 PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = reinterpret_cast<PFN_BOOTSTRAPPER_APPLICATION_DESTROY>(::GetProcAddress(vstate.hPrereqModule, "PrereqBootstrapperApplicationDestroy"));
104 if (pfnDestroy)
105 {
106 (*pfnDestroy)(pArgs, &childResults);
107 }
108
109 ::FreeLibrary(vstate.hPrereqModule);
110 vstate.hPrereqModule = NULL;
111 }
112
113 if (vstate.pApplication)
114 {
115 DestroyBootstrapperApplication(vstate.pApplication, pArgs, pResults);
116 ReleaseNullObject(vstate.pApplication);
117 }
118
119 BalUninitialize();
120
121 // Need to keep track of state between reloads.
122 pResults->fDisableUnloading = TRUE;
123}
124
125static HRESULT LoadModulePaths(
126 __in INTERNAL_UI_BA_STATE* pState
127 )
128{
129 HRESULT hr = S_OK;
130 LPWSTR sczFullPath = NULL;
131
132 hr = PathForCurrentProcess(&sczFullPath, pState->hInstance);
133 ExitOnFailure(hr, "Failed to get the full host path.");
134
135 hr = PathGetDirectory(sczFullPath, &pState->sczAppBase);
136 ExitOnFailure(hr, "Failed to get the directory of the full process path.");
137
138LExit:
139 ReleaseStr(sczFullPath);
140
141 return hr;
142}
143
144static HRESULT LoadInternalUIBAConfiguration(
145 __in INTERNAL_UI_BA_STATE* pState,
146 __in const BOOTSTRAPPER_CREATE_ARGS* /*pArgs*/
147 )
148{
149 HRESULT hr = S_OK;
150
151 pState->prereqData.fAlwaysInstallPrereqs = TRUE;
152 pState->prereqData.fPerformHelp = TRUE;
153 pState->prereqData.fPerformLayout = TRUE;
154
155 return hr;
156}
157
158static HRESULT CreatePrerequisiteBA(
159 __in INTERNAL_UI_BA_STATE* pState,
160 __in IBootstrapperEngine* pEngine,
161 __in const BOOTSTRAPPER_CREATE_ARGS* pArgs,
162 __inout BOOTSTRAPPER_CREATE_RESULTS* pResults
163 )
164{
165 HRESULT hr = S_OK;
166 LPWSTR sczPrereqPath = NULL;
167 HMODULE hModule = NULL;
168
169 hr = PathConcat(pState->sczAppBase, L"prereqba.dll", &sczPrereqPath);
170 BalExitOnFailure(hr, "Failed to get path to pre-requisite BA.");
171
172 hModule = ::LoadLibraryExW(sczPrereqPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
173 ExitOnNullWithLastError(hModule, hr, "Failed to load pre-requisite BA DLL.");
174
175 PFN_PREQ_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = reinterpret_cast<PFN_PREQ_BOOTSTRAPPER_APPLICATION_CREATE>(::GetProcAddress(hModule, "PrereqBootstrapperApplicationCreate"));
176 ExitOnNullWithLastError(pfnCreate, hr, "Failed to get PrereqBootstrapperApplicationCreate entry-point from: %ls", sczPrereqPath);
177
178 hr = pfnCreate(&pState->prereqData, pEngine, pArgs, pResults);
179 ExitOnFailure(hr, "Failed to create prequisite bootstrapper app.");
180
181 pState->hPrereqModule = hModule;
182 hModule = NULL;
183
184LExit:
185 if (hModule)
186 {
187 ::FreeLibrary(hModule);
188 }
189 ReleaseStr(sczPrereqPath);
190
191 return hr;
192}
diff --git a/src/ext/Bal/wixiuiba/wixiuiba.def b/src/ext/Bal/wixiuiba/wixiuiba.def
new file mode 100644
index 00000000..4488df94
--- /dev/null
+++ b/src/ext/Bal/wixiuiba/wixiuiba.def
@@ -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
4EXPORTS
5 BootstrapperApplicationCreate
6 BootstrapperApplicationDestroy
diff --git a/src/ext/Bal/wixiuiba/wixiuiba.h b/src/ext/Bal/wixiuiba/wixiuiba.h
new file mode 100644
index 00000000..76077f42
--- /dev/null
+++ b/src/ext/Bal/wixiuiba/wixiuiba.h
@@ -0,0 +1,13 @@
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
5struct INTERNAL_UI_BA_STATE
6{
7 BOOL fInitialized;
8 HINSTANCE hInstance;
9 LPWSTR sczAppBase;
10 HMODULE hPrereqModule;
11 PREQBA_DATA prereqData;
12 IBootstrapperApplication* pApplication;
13};
diff --git a/src/ext/Bal/wixiuiba/wixiuiba.vcxproj b/src/ext/Bal/wixiuiba/wixiuiba.vcxproj
new file mode 100644
index 00000000..dfadbb95
--- /dev/null
+++ b/src/ext/Bal/wixiuiba/wixiuiba.vcxproj
@@ -0,0 +1,72 @@
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>{0F73E566-925C-448D-99CB-3A7F5DF399C8}</ProjectGuid>
34 <ConfigurationType>DynamicLibrary</ConfigurationType>
35 <CharacterSet>Unicode</CharacterSet>
36 <TargetName>wixiuiba</TargetName>
37 <ProjectModuleDefinitionFile>wixiuiba.def</ProjectModuleDefinitionFile>
38 </PropertyGroup>
39
40 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
41 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
42
43 <PropertyGroup>
44 <ProjectAdditionalIncludeDirectories>..\wixstdba\inc</ProjectAdditionalIncludeDirectories>
45 <ProjectAdditionalLinkLibraries>shlwapi.lib</ProjectAdditionalLinkLibraries>
46 </PropertyGroup>
47
48 <ItemGroup>
49 <ClCompile Include="precomp.cpp">
50 <PrecompiledHeader>Create</PrecompiledHeader>
51 </ClCompile>
52 <ClCompile Include="WixInternalUIBootstrapperApplication.cpp" />
53 <ClCompile Include="wixiuiba.cpp" />
54 </ItemGroup>
55 <ItemGroup>
56 <ClInclude Include="precomp.h" />
57 <ClInclude Include="WixInternalUIBootstrapperApplication.h" />
58 <ClInclude Include="wixiuiba.h" />
59 </ItemGroup>
60 <ItemGroup>
61 <None Include="wixiuiba.def" />
62 </ItemGroup>
63
64 <ItemGroup>
65 <PackageReference Include="WixToolset.BalUtil" />
66
67 <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
68 <PackageReference Include="GitInfo" PrivateAssets="All" />
69 </ItemGroup>
70
71 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
72</Project>
diff --git a/src/ext/Bal/wixlib/BalExtension_platform.wxi b/src/ext/Bal/wixlib/BalExtension_platform.wxi
index b2750eee..5b0d78d0 100644
--- a/src/ext/Bal/wixlib/BalExtension_platform.wxi
+++ b/src/ext/Bal/wixlib/BalExtension_platform.wxi
@@ -19,6 +19,20 @@
19 </Fragment> 19 </Fragment>
20 20
21 <Fragment> 21 <Fragment>
22 <BootstrapperApplication Id="WixInternalUIBootstrapperApplication$(var.Suffix)">
23 <BootstrapperApplicationDll Id="WixInternalUIBootstrapperApplication" SourceFile="!(bindpath.$(var.platform))\wixiuiba.dll" />
24 <Payload SourceFile="!(bindpath.$(var.platform))\wixstdba.dll" Name="prereqba.dll" />
25 </BootstrapperApplication>
26 </Fragment>
27
28 <Fragment>
29 <BootstrapperApplication Id="WixInternalUIBootstrapperApplication.Standard$(var.Suffix)">
30 <PayloadGroupRef Id="WixIuibaStandardPayloads" />
31 </BootstrapperApplication>
32 <BootstrapperApplicationRef Id="WixInternalUIBootstrapperApplication$(var.Suffix)" />
33 </Fragment>
34
35 <Fragment>
22 <BootstrapperApplication Id="WixStandardBootstrapperApplication$(var.Suffix)"> 36 <BootstrapperApplication Id="WixStandardBootstrapperApplication$(var.Suffix)">
23 <BootstrapperApplicationDll Id="WixStandardBootstrapperApplication" SourceFile="!(bindpath.$(var.platform))\wixstdba.dll" /> 37 <BootstrapperApplicationDll Id="WixStandardBootstrapperApplication" SourceFile="!(bindpath.$(var.platform))\wixstdba.dll" />
24 </BootstrapperApplication> 38 </BootstrapperApplication>
diff --git a/src/ext/Bal/wixlib/bal.wixproj b/src/ext/Bal/wixlib/bal.wixproj
index 627faecc..a7ae9a96 100644
--- a/src/ext/Bal/wixlib/bal.wixproj
+++ b/src/ext/Bal/wixlib/bal.wixproj
@@ -22,6 +22,9 @@
22 <ProjectReference Include="..\mbahost\mbahost.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" /> 22 <ProjectReference Include="..\mbahost\mbahost.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" />
23 <ProjectReference Include="..\mbahost\mbahost.vcxproj" Properties="Platform=x64" ReferenceOutputAssembly="false" /> 23 <ProjectReference Include="..\mbahost\mbahost.vcxproj" Properties="Platform=x64" ReferenceOutputAssembly="false" />
24 <ProjectReference Include="..\mbahost\mbahost.vcxproj" Properties="Platform=ARM64" ReferenceOutputAssembly="false" /> 24 <ProjectReference Include="..\mbahost\mbahost.vcxproj" Properties="Platform=ARM64" ReferenceOutputAssembly="false" />
25 <ProjectReference Include="..\wixiuiba\wixiuiba.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" />
26 <ProjectReference Include="..\wixiuiba\wixiuiba.vcxproj" Properties="Platform=x64" ReferenceOutputAssembly="false" />
27 <ProjectReference Include="..\wixiuiba\wixiuiba.vcxproj" Properties="Platform=ARM64" ReferenceOutputAssembly="false" />
25 <ProjectReference Include="..\wixstdba\wixstdba.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" /> 28 <ProjectReference Include="..\wixstdba\wixstdba.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" />
26 <ProjectReference Include="..\wixstdba\wixstdba.vcxproj" Properties="Platform=x64" ReferenceOutputAssembly="false" /> 29 <ProjectReference Include="..\wixstdba\wixstdba.vcxproj" Properties="Platform=x64" ReferenceOutputAssembly="false" />
27 <ProjectReference Include="..\wixstdba\wixstdba.vcxproj" Properties="Platform=ARM64" ReferenceOutputAssembly="false" /> 30 <ProjectReference Include="..\wixstdba\wixstdba.vcxproj" Properties="Platform=ARM64" ReferenceOutputAssembly="false" />
diff --git a/src/ext/Bal/wixlib/wixiuiba.wxs b/src/ext/Bal/wixlib/wixiuiba.wxs
new file mode 100644
index 00000000..5501a23f
--- /dev/null
+++ b/src/ext/Bal/wixlib/wixiuiba.wxs
@@ -0,0 +1,12 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3
4<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <Fragment>
6 <PayloadGroup Id="WixIuibaStandardPayloads">
7 <Payload Name="mbapreq.thm" SourceFile="!(wix.WixIuibaThemeXml=SourceDir\iuipreq.thm)" />
8 <Payload Name="mbapreq.png" SourceFile="!(wix.WixIuibaLogo=SourceDir\mbapreq.png)" />
9 <Payload Name="mbapreq.wxl" SourceFile="!(wix.WixIuibaThemeWxl=SourceDir\iuipreq.wxl)" />
10 </PayloadGroup>
11 </Fragment>
12</Wix>
diff --git a/src/ext/Bal/wixstdba/Resources/iuipreq.thm b/src/ext/Bal/wixstdba/Resources/iuipreq.thm
new file mode 100644
index 00000000..5429b3d2
--- /dev/null
+++ b/src/ext/Bal/wixstdba/Resources/iuipreq.thm
@@ -0,0 +1,67 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Theme xmlns="http://wixtoolset.org/schemas/v4/thmutil">
3 <Font Id="0" Height="-12" Weight="500" Foreground="windowtext" Background="window">Segoe UI</Font>
4 <Font Id="1" Height="-24" Weight="500" Foreground="windowtext">Segoe UI</Font>
5 <Font Id="2" Height="-22" Weight="500" Foreground="graytext">Segoe UI</Font>
6 <Font Id="3" Height="-12" Weight="500" Foreground="windowtext" Background="window">Segoe UI</Font>
7
8 <Window Width="485" Height="300" HexStyle="100a0000" FontId="0" Caption="#(loc.Caption)">
9 <ImageControl X="11" Y="11" Width="64" Height="64" ImageFile="mbapreq.png" Visible="yes"/>
10 <Label X="80" Y="11" Width="-11" Height="96" FontId="1" Visible="yes" DisablePrefix="yes">
11 <Text>#(loc.Title)</Text>
12 <Text Condition="WixBundleAction = 2 OR WixBundleCommandLineAction = 1">#(loc.NonPrereqTitle)</Text>
13 </Label>
14
15 <Page Name="Help">
16 <Label X="11" Y="112" Width="-11" Height="30" FontId="2" DisablePrefix="yes">#(loc.HelpHeader)</Label>
17 <Label X="11" Y="153" Width="-11" Height="-35" FontId="3" DisablePrefix="yes">#(loc.HelpText)</Label>
18 <Button Name="HelpCloseButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">
19 <Text>#(loc.HelpCloseButton)</Text>
20 <CloseWindowAction />
21 </Button>
22 </Page>
23 <Page Name="Install">
24 <Hypertext Name="EulaHyperlink" X="11" Y="121" Width="-11" Height="34" TabStop="yes" FontId="3">#(loc.InstallLicenseTerms)</Hypertext>
25 <Button Name="InstallButton" X="-91" Y="-11" Width="130" Height="23" TabStop="yes" FontId="0">#(loc.InstallAcceptAndInstallButton)</Button>
26 <Button Name="InstallDeclineButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">
27 <Text>#(loc.InstallDeclineButton)</Text>
28 <CloseWindowAction />
29 </Button>
30 </Page>
31 <Page Name="Progress">
32 <Label X="11" Y="112" Width="-11" Height="30" FontId="2" DisablePrefix="yes">#(loc.ProgressHeader)</Label>
33 <Label X="11" Y="153" Width="70" Height="17" FontId="3" DisablePrefix="yes">#(loc.ProgressLabel)</Label>
34 <Label Name="OverallProgressPackageText" X="85" Y="153" Width="-11" Height="17" FontId="3" DisablePrefix="yes">[ProgressPackageName]</Label>
35 <Progressbar Name="OverallCalculatedProgressbar" X="11" Y="175" Width="-11" Height="15" />
36 <Button Name="ProgressCancelButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.ProgressCancelButton)</Button>
37 </Page>
38 <Page Name="Success">
39 <Label X="11" Y="112" Width="-11" Height="30" FontId="2" DisablePrefix="yes">
40 <Text>#(loc.SuccessHeader)</Text>
41 <Text Condition="WixBundleAction = 2">#(loc.SuccessLayoutHeader)</Text>
42 </Label>
43 <Label X="-11" Y="-20" Width="400" Height="34" FontId="3" DisablePrefix="yes" VisibleCondition="WixStdBARestartRequired">
44 <Text>#(loc.SuccessRestartText)</Text>
45 </Label>
46 <Button Name="SuccessRestartButton" X="-91" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.SuccessRestartButton)</Button>
47 <Button Name="SuccessCloseButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">
48 <Text>#(loc.SuccessCloseButton)</Text>
49 <CloseWindowAction />
50 </Button>
51 </Page>
52 <Page Name="Failure">
53 <Label X="11" Y="112" Width="-11" Height="30" FontId="2" DisablePrefix="yes">
54 <Text>#(loc.FailureHeader)</Text>
55 <Text Condition="WixBundleAction = 2">#(loc.FailureLayoutHeader)</Text>
56 </Label>
57 <Hypertext Name="FailureLogFileLink" X="11" Y="153" Width="-11" Height="51" FontId="3" TabStop="yes" HideWhenDisabled="yes">#(loc.FailureLogLinkText)</Hypertext>
58 <Hypertext Name="FailureMessageText" X="22" Y="190" Width="-11" Height="51" FontId="3" TabStop="yes" HideWhenDisabled="yes"/>
59 <Label X="-11" Y="-20" Width="400" Height="34" FontId="3" DisablePrefix="yes" VisibleCondition="WixStdBARestartRequired">#(loc.FailureRestartText)</Label>
60 <Button Name="FailureRestartButton" X="-91" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.FailureRestartButton)</Button>
61 <Button Name="FailureCloseButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">
62 <Text>#(loc.FailureCloseButton)</Text>
63 <CloseWindowAction />
64 </Button>
65 </Page>
66 </Window>
67</Theme>
diff --git a/src/ext/Bal/wixstdba/Resources/iuipreq.wxl b/src/ext/Bal/wixstdba/Resources/iuipreq.wxl
new file mode 100644
index 00000000..4afcd10f
--- /dev/null
+++ b/src/ext/Bal/wixstdba/Resources/iuipreq.wxl
@@ -0,0 +1,34 @@
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<WixLocalization Culture="en-us" Language="1033" xmlns="http://wixtoolset.org/schemas/v4/wxl">
6 <String Id="Caption">[WixBundleName] Prerequisite Setup</String>
7 <String Id="Title">Prerequisite required for [WixBundleName] setup</String>
8 <String Id="NonPrereqTitle">[WixBundleName] setup</String>
9 <String Id="ConfirmCancelMessage">Are you sure you want to cancel?</String>
10 <String Id="HelpHeader">Setup Help</String>
11 <String Id="HelpText">/passive | /quiet - displays minimal UI with no prompts or displays no UI and
12 no prompts. By default UI and all prompts are displayed.
13
14/log log.txt - logs to a specific file. By default a log file is created in %TEMP%.</String>
15 <String Id="HelpCloseButton">&amp;Close</String>
16 <String Id="InstallLicenseTerms">Click the "Accept and Install" button to accept the prerequisite &lt;a href="#"&gt;license terms&lt;/a&gt;.</String>
17 <String Id="InstallAcceptAndInstallButton">&amp;Accept and Install</String>
18 <String Id="InstallDeclineButton">&amp;Decline</String>
19 <String Id="ProgressHeader">Setup Progress</String>
20 <String Id="ProgressLabel">Processing:</String>
21 <String Id="ProgressCancelButton">&amp;Cancel</String>
22 <String Id="SuccessHeader">Prerequisite Setup Successful</String>
23 <String Id="SuccessLayoutHeader">Layout Successfully Completed</String>
24 <String Id="SuccessRestartText">You must restart your computer before [WixBundleName] setup can continue.</String>
25 <String Id="SuccessRestartButton">&amp;Restart</String>
26 <String Id="SuccessCloseButton">&amp;Close</String>
27 <String Id="FailureHeader">Setup Failed</String>
28 <String Id="FailureLayoutHeader">Layout Failed</String>
29 <String Id="FailureLogLinkText">One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the &lt;a href="#"&gt;log file&lt;/a&gt;.</String>
30 <String Id="FailureRestartText">You must restart your computer to complete the rollback of the software.</String>
31 <String Id="FailureRestartButton">&amp;Restart</String>
32 <String Id="FailureCloseButton">&amp;Close</String>
33 <String Id="ErrorFailNoActionReboot">No action was taken as a system reboot is required.</String>
34</WixLocalization>
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
index 9aa58a28..1af1abeb 100644
--- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
@@ -248,10 +248,20 @@ public: // IBootstrapperApplication
248 248
249 if (m_fPrereq) 249 if (m_fPrereq)
250 { 250 {
251 // Pre-req BA should only show help or do an install (to launch the Managed BA which can then do the right action). 251 if (m_pPrereqData->fPerformLayout && BOOTSTRAPPER_ACTION_LAYOUT == m_command.action)
252 if (BOOTSTRAPPER_ACTION_HELP != m_command.action)
253 { 252 {
254 m_command.action = BOOTSTRAPPER_ACTION_INSTALL; 253 // The parent BA has requested that this BA be in charge of layout.
254 m_fPrereq = FALSE;
255 }
256 else
257 {
258 m_fPreplanPrereqs = m_pPrereqData->fAlwaysInstallPrereqs;
259
260 // Pre-req BA should only show help or do an install (to launch the parent BA which can then do the right action).
261 if (BOOTSTRAPPER_ACTION_HELP != m_command.action)
262 {
263 m_command.action = BOOTSTRAPPER_ACTION_INSTALL;
264 }
255 } 265 }
256 } 266 }
257 else // maybe modify the action state if the bundle is or is not already installed. 267 else // maybe modify the action state if the bundle is or is not already installed.
@@ -527,11 +537,11 @@ public: // IBootstrapperApplication
527 ) 537 )
528 { 538 {
529 HRESULT hr = S_OK; 539 HRESULT hr = S_OK;
530 BOOL fPlannedPrereqs = WIXSTDBA_STATE_PLANNING_PREREQS == m_state; 540 BOOL fPreplannedPrereqs = WIXSTDBA_STATE_PLANNING_PREREQS == m_state;
531 WIXSTDBA_STATE completedState = WIXSTDBA_STATE_PLANNED; 541 WIXSTDBA_STATE completedState = WIXSTDBA_STATE_PLANNED;
532 BOOL fApply = TRUE; 542 BOOL fApply = TRUE;
533 543
534 if (fPlannedPrereqs) 544 if (fPreplannedPrereqs)
535 { 545 {
536 if (SUCCEEDED(hrStatus) && !m_fPrereqPackagePlanned) 546 if (SUCCEEDED(hrStatus) && !m_fPrereqPackagePlanned)
537 { 547 {
@@ -547,7 +557,7 @@ public: // IBootstrapperApplication
547 // Need to force the state change since normally moving backwards is prevented. 557 // Need to force the state change since normally moving backwards is prevented.
548 ::PostMessageW(m_hWnd, WM_WIXSTDBA_CHANGE_STATE, 0, WIXSTDBA_STATE_HELP); 558 ::PostMessageW(m_hWnd, WM_WIXSTDBA_CHANGE_STATE, 0, WIXSTDBA_STATE_HELP);
549 559
550 ::PostMessageW(m_hWnd, WM_WIXSTDBA_SHOW_HELP, 0, 0); 560 ::PostMessageW(m_hWnd, WM_WIXSTDBA_SHOW_HELP, 0, 0);
551 561
552 ExitFunction(); 562 ExitFunction();
553 } 563 }
@@ -562,7 +572,7 @@ public: // IBootstrapperApplication
562 ExitFunction(); 572 ExitFunction();
563 } 573 }
564 574
565 if (fPlannedPrereqs) 575 if (fPreplannedPrereqs)
566 { 576 {
567 // If the UI should be visible, display it now and hide the splash screen 577 // If the UI should be visible, display it now and hide the splash screen
568 if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display) 578 if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display)
@@ -1056,13 +1066,14 @@ public: // IBootstrapperApplication
1056 1066
1057 hr = __super::OnExecutePackageComplete(wzPackageId, hrStatus, restart, recommendation, pAction); 1067 hr = __super::OnExecutePackageComplete(wzPackageId, hrStatus, restart, recommendation, pAction);
1058 1068
1059 BAL_INFO_PACKAGE* pPackage = NULL; 1069 if (m_fPrereq && BOOTSTRAPPER_APPLY_RESTART_NONE != restart)
1060 HRESULT hrPrereq = BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage);
1061 if (SUCCEEDED(hrPrereq))
1062 { 1070 {
1071 BAL_INFO_PACKAGE* pPackage = NULL;
1072 HRESULT hrPrereq = BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage);
1073
1063 // If the prerequisite required a restart (any restart) then do an immediate 1074 // If the prerequisite required a restart (any restart) then do an immediate
1064 // restart to ensure that the bundle will get launched again post reboot. 1075 // restart to ensure that the bundle will get launched again post reboot.
1065 if (m_fPrereq && pPackage->fPrereqPackage && BOOTSTRAPPER_APPLY_RESTART_NONE != restart) 1076 if (SUCCEEDED(hrPrereq) && pPackage->fPrereqPackage)
1066 { 1077 {
1067 *pAction = BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART; 1078 *pAction = BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART;
1068 } 1079 }
@@ -2283,7 +2294,7 @@ private:
2283 // Okay, we're ready for packages now. 2294 // Okay, we're ready for packages now.
2284 pThis->SetState(WIXSTDBA_STATE_INITIALIZED, hr); 2295 pThis->SetState(WIXSTDBA_STATE_INITIALIZED, hr);
2285 2296
2286 if (!pThis->m_fPreplanPrereqs && BOOTSTRAPPER_ACTION_HELP == pThis->m_command.action) 2297 if (pThis->m_fPerformHelp && BOOTSTRAPPER_ACTION_HELP == pThis->m_command.action)
2287 { 2298 {
2288 firstAction = WM_WIXSTDBA_SHOW_HELP; 2299 firstAction = WM_WIXSTDBA_SHOW_HELP;
2289 } 2300 }
@@ -4246,7 +4257,7 @@ public:
4246 m_hWnd = NULL; 4257 m_hWnd = NULL;
4247 4258
4248 m_state = WIXSTDBA_STATE_INITIALIZING; 4259 m_state = WIXSTDBA_STATE_INITIALIZING;
4249 m_hrFinal = pPrereqData ? pPrereqData->hrHostInitialization : S_OK; 4260 m_hrFinal = pPrereqData ? pPrereqData->hrFatalError : S_OK;
4250 4261
4251 m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE; 4262 m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;
4252 m_fRestartRequired = FALSE; 4263 m_fRestartRequired = FALSE;
@@ -4269,7 +4280,8 @@ public:
4269 4280
4270 m_pPrereqData = pPrereqData; 4281 m_pPrereqData = pPrereqData;
4271 m_fPrereq = NULL != pPrereqData; 4282 m_fPrereq = NULL != pPrereqData;
4272 m_fPreplanPrereqs = m_fPrereq && m_pPrereqData->fAlwaysInstallPrereqs; 4283 m_fPreplanPrereqs = FALSE;
4284 m_fPerformHelp = !m_fPrereq || m_pPrereqData->fPerformHelp;
4273 m_fPrereqPackagePlanned = FALSE; 4285 m_fPrereqPackagePlanned = FALSE;
4274 m_fPrereqInstalled = FALSE; 4286 m_fPrereqInstalled = FALSE;
4275 m_fPrereqSkipped = FALSE; 4287 m_fPrereqSkipped = FALSE;
@@ -4554,6 +4566,7 @@ private:
4554 PREQBA_DATA* m_pPrereqData; 4566 PREQBA_DATA* m_pPrereqData;
4555 BOOL m_fPrereq; 4567 BOOL m_fPrereq;
4556 BOOL m_fPreplanPrereqs; 4568 BOOL m_fPreplanPrereqs;
4569 BOOL m_fPerformHelp;
4557 BOOL m_fPrereqPackagePlanned; 4570 BOOL m_fPrereqPackagePlanned;
4558 BOOL m_fPrereqInstalled; 4571 BOOL m_fPrereqInstalled;
4559 BOOL m_fPrereqSkipped; 4572 BOOL m_fPrereqSkipped;
diff --git a/src/ext/Bal/wixstdba/inc/preqba.h b/src/ext/Bal/wixstdba/inc/preqba.h
index ed339730..25fa7105 100644
--- a/src/ext/Bal/wixstdba/inc/preqba.h
+++ b/src/ext/Bal/wixstdba/inc/preqba.h
@@ -4,8 +4,10 @@
4 4
5struct PREQBA_DATA 5struct PREQBA_DATA
6{ 6{
7 HRESULT hrHostInitialization; 7 HRESULT hrFatalError;
8 BOOL fAlwaysInstallPrereqs; 8 BOOL fAlwaysInstallPrereqs;
9 BOOL fPerformHelp;
10 BOOL fPerformLayout;
9 BOOL fCompleted; 11 BOOL fCompleted;
10}; 12};
11 13
diff --git a/src/test/burn/TestData/Templates/Bundle.wxs b/src/test/burn/TestData/Templates/Bundle.wxs
index 612e67f5..c55f67a7 100644
--- a/src/test/burn/TestData/Templates/Bundle.wxs
+++ b/src/test/burn/TestData/Templates/Bundle.wxs
@@ -33,6 +33,10 @@
33 <BootstrapperApplication> 33 <BootstrapperApplication>
34 <bal:WixStandardBootstrapperApplication LicenseUrl="" Theme="hyperlinkLicense" /> 34 <bal:WixStandardBootstrapperApplication LicenseUrl="" Theme="hyperlinkLicense" />
35 </BootstrapperApplication> 35 </BootstrapperApplication>
36 <?elseif $(var.BA) = "iui"?>
37 <BootstrapperApplication>
38 <bal:WixInternalUIBootstrapperApplication />
39 </BootstrapperApplication>
36 <?else?> 40 <?else?>
37 <BootstrapperApplicationRef Id="$(var.BA)" /> 41 <BootstrapperApplicationRef Id="$(var.BA)" />
38 <?endif?> 42 <?endif?>
diff --git a/src/test/burn/TestData/TestData.proj b/src/test/burn/TestData/TestData.proj
index 27bfb02b..0f7cbee9 100644
--- a/src/test/burn/TestData/TestData.proj
+++ b/src/test/burn/TestData/TestData.proj
@@ -4,7 +4,7 @@
4<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 4<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemGroup> 5 <ItemGroup>
6 <TestDataProject Include="**\*.wixproj" /> 6 <TestDataProject Include="**\*.wixproj" />
7 7
8 <!-- https://github.com/wixtoolset/issues/issues/6401 --> 8 <!-- https://github.com/wixtoolset/issues/issues/6401 -->
9 <TestDataProject Remove="DependencyTests\BundleC\BundleC.wixproj" /> 9 <TestDataProject Remove="DependencyTests\BundleC\BundleC.wixproj" />
10 <TestDataProject Remove="DependencyTests\BundleF_PatchAv1_0_1\BundleF_PatchAv1_0_1.wixproj" /> 10 <TestDataProject Remove="DependencyTests\BundleF_PatchAv1_0_1\BundleF_PatchAv1_0_1.wixproj" />
diff --git a/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wixproj b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wixproj
new file mode 100644
index 00000000..9e3cba8b
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wixproj
@@ -0,0 +1,17 @@
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>iui</BA>
6 <UpgradeCode>{22B5ADAF-74D3-424A-8D32-9901FCF97E6D}</UpgradeCode>
7 </PropertyGroup>
8 <ItemGroup>
9 <ProjectReference Include="..\InternalUIPackage\InternalUIPackage.wixproj" />
10 <ProjectReference Include="..\InternalUIarm64Package\InternalUIarm64Package.wixproj" />
11 <ProjectReference Include="..\InternalUIx64Package\InternalUIx64Package.wixproj" />
12 <ProjectReference Include="..\InternalUIx86Package\InternalUIx86Package.wixproj" />
13 </ItemGroup>
14 <ItemGroup>
15 <PackageReference Include="WixToolset.Bal.wixext" />
16 </ItemGroup>
17</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wxs b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wxs
new file mode 100644
index 00000000..71b8665f
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wxs
@@ -0,0 +1,13 @@
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">
5 <Fragment>
6 <PackageGroup Id="BundlePackages">
7 <MsiPackage Id="InternalUIPackage" SourceFile="$(var.InternalUIPackage.TargetPath)" />
8 <MsiPackage Id="InternalUIarm64Package" SourceFile="$(var.InternalUIarm64Package.TargetPath)" bal:PrimaryPackageType="arm64" />
9 <MsiPackage Id="InternalUIx64Package" SourceFile="$(var.InternalUIx64Package.TargetPath)" bal:PrimaryPackageType="x64" />
10 <MsiPackage Id="InternalUIx86Package" SourceFile="$(var.InternalUIx86Package.TargetPath)" bal:PrimaryPackageType="x86" />
11 </PackageGroup>
12 </Fragment>
13</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/Bundle.wxs b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/Bundle.wxs
new file mode 100644
index 00000000..195c159e
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/Bundle.wxs
@@ -0,0 +1,63 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3<?ifndef Version?>
4<?define Version = 1.0.0.0?>
5<?endif?>
6<?ifndef BundleLogDirectory?>
7<?define BundleLogDirectory = .?>
8<?endif?>
9
10<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
11 <!-- The only difference from the template should be the SplashScreen and uncompressed -->
12 <Bundle Name="~$(var.TestGroupName) - $(var.BundleName)" Version="$(var.Version)" UpgradeCode="$(var.UpgradeCode)" Compressed="no" SplashScreenSourceFile="..\..\Manual\BafThmutilTesting\theme\star_transparent.bmp">
13 <Log Prefix="$(var.BundleLogDirectory)\~$(var.TestGroupName)_$(var.BundleName)" />
14
15 <Variable Name="TestGroupName" Value="$(var.TestGroupName)" />
16
17 <?ifdef SoftwareTag?>
18 <SoftwareTag Regid="regid.1995-08.com.example" InstallPath="[CommonAppDataFolder]regid.1995-08.com.example" />
19 <?endif?>
20
21 <?ifndef BA?>
22 <!-- pulled in through the PackageGroupRef below -->
23 <?elseif $(var.BA) = "TestBAdnc"?>
24 <!-- pulled in through the PackageGroupRef below -->
25 <?elseif $(var.BA) = "TestBA_x64"?>
26 <!-- pulled in through the PackageGroupRef below -->
27 <?elseif $(var.BA) = "TestBAdnc_x64"?>
28 <!-- pulled in through the PackageGroupRef below -->
29 <?elseif $(var.BA) = "WixBA"?>
30 <!-- pulled in through the PackageGroupRef below -->
31 <?elseif $(var.BA) = "WixBAdnc_x64"?>
32 <!-- pulled in through the PackageGroupRef below -->
33 <?elseif $(var.BA) = "hyperlinkLicense"?>
34 <BootstrapperApplication>
35 <bal:WixStandardBootstrapperApplication LicenseUrl="" Theme="hyperlinkLicense" />
36 </BootstrapperApplication>
37 <?elseif $(var.BA) = "iui"?>
38 <BootstrapperApplication>
39 <bal:WixInternalUIBootstrapperApplication />
40 </BootstrapperApplication>
41 <?else?>
42 <BootstrapperApplicationRef Id="$(var.BA)" />
43 <?endif?>
44
45 <Chain>
46 <?ifndef BA?>
47 <PackageGroupRef Id="TestBA" />
48 <?elseif $(var.BA) = "TestBAdnc"?>
49 <PackageGroupRef Id="TestBAdnc" />
50 <?elseif $(var.BA) = "TestBA_x64"?>
51 <PackageGroupRef Id="TestBA_x64" />
52 <?elseif $(var.BA) = "TestBAdnc_x64"?>
53 <PackageGroupRef Id="TestBAdnc_x64" />
54 <?elseif $(var.BA) = "WixBA"?>
55 <PackageGroupRef Id="WixBA" />
56 <?elseif $(var.BA) = "WixBAdnc_x64"?>
57 <PackageGroupRef Id="WixBAdnc_x64" />
58 <?endif?>
59
60 <PackageGroupRef Id="BundlePackages" />
61 </Chain>
62 </Bundle>
63</Wix>
diff --git a/src/test/dtf/DtfE2ETests.sln b/src/test/burn/TestData/WixIuiBaTests/DtfSamples.sln
index 39d8cf08..1e6c2082 100644
--- a/src/test/dtf/DtfE2ETests.sln
+++ b/src/test/burn/TestData/WixIuiBaTests/DtfSamples.sln
@@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.30114.105
5MinimumVisualStudioVersion = 10.0.40219.1 5MinimumVisualStudioVersion = 10.0.40219.1
6Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedUI", "EmbeddedUI\EmbeddedUI.csproj", "{864B8C50-7895-4485-AC89-900D86FD8C0D}" 6Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedUI", "EmbeddedUI\EmbeddedUI.csproj", "{864B8C50-7895-4485-AC89-900D86FD8C0D}"
7EndProject 7EndProject
8Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleCA", "SampleCA\SampleCA.csproj", "{8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}" 8Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCA", "ManagedCA\ManagedCA.csproj", "{8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}"
9EndProject 9EndProject
10Global 10Global
11 GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/src/test/dtf/EmbeddedUI/EmbeddedUI.config b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.config
index 700aff6f..700aff6f 100644
--- a/src/test/dtf/EmbeddedUI/EmbeddedUI.config
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.config
diff --git a/src/test/dtf/EmbeddedUI/EmbeddedUI.csproj b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.csproj
index a6339220..a6339220 100644
--- a/src/test/dtf/EmbeddedUI/EmbeddedUI.csproj
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.csproj
diff --git a/src/test/dtf/EmbeddedUI/InstallProgressCounter.cs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/InstallProgressCounter.cs
index 3d75081c..3d75081c 100644
--- a/src/test/dtf/EmbeddedUI/InstallProgressCounter.cs
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/InstallProgressCounter.cs
diff --git a/src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SampleEmbeddedUI.cs
index ae86dc97..ae86dc97 100644
--- a/src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SampleEmbeddedUI.cs
diff --git a/src/test/dtf/EmbeddedUI/SetupWizard.xaml b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml
index 97e406c2..97e406c2 100644
--- a/src/test/dtf/EmbeddedUI/SetupWizard.xaml
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml
diff --git a/src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml.cs
index a4345481..a4345481 100644
--- a/src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml.cs
diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs
new file mode 100644
index 00000000..195c159e
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs
@@ -0,0 +1,63 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3<?ifndef Version?>
4<?define Version = 1.0.0.0?>
5<?endif?>
6<?ifndef BundleLogDirectory?>
7<?define BundleLogDirectory = .?>
8<?endif?>
9
10<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
11 <!-- The only difference from the template should be the SplashScreen and uncompressed -->
12 <Bundle Name="~$(var.TestGroupName) - $(var.BundleName)" Version="$(var.Version)" UpgradeCode="$(var.UpgradeCode)" Compressed="no" SplashScreenSourceFile="..\..\Manual\BafThmutilTesting\theme\star_transparent.bmp">
13 <Log Prefix="$(var.BundleLogDirectory)\~$(var.TestGroupName)_$(var.BundleName)" />
14
15 <Variable Name="TestGroupName" Value="$(var.TestGroupName)" />
16
17 <?ifdef SoftwareTag?>
18 <SoftwareTag Regid="regid.1995-08.com.example" InstallPath="[CommonAppDataFolder]regid.1995-08.com.example" />
19 <?endif?>
20
21 <?ifndef BA?>
22 <!-- pulled in through the PackageGroupRef below -->
23 <?elseif $(var.BA) = "TestBAdnc"?>
24 <!-- pulled in through the PackageGroupRef below -->
25 <?elseif $(var.BA) = "TestBA_x64"?>
26 <!-- pulled in through the PackageGroupRef below -->
27 <?elseif $(var.BA) = "TestBAdnc_x64"?>
28 <!-- pulled in through the PackageGroupRef below -->
29 <?elseif $(var.BA) = "WixBA"?>
30 <!-- pulled in through the PackageGroupRef below -->
31 <?elseif $(var.BA) = "WixBAdnc_x64"?>
32 <!-- pulled in through the PackageGroupRef below -->
33 <?elseif $(var.BA) = "hyperlinkLicense"?>
34 <BootstrapperApplication>
35 <bal:WixStandardBootstrapperApplication LicenseUrl="" Theme="hyperlinkLicense" />
36 </BootstrapperApplication>
37 <?elseif $(var.BA) = "iui"?>
38 <BootstrapperApplication>
39 <bal:WixInternalUIBootstrapperApplication />
40 </BootstrapperApplication>
41 <?else?>
42 <BootstrapperApplicationRef Id="$(var.BA)" />
43 <?endif?>
44
45 <Chain>
46 <?ifndef BA?>
47 <PackageGroupRef Id="TestBA" />
48 <?elseif $(var.BA) = "TestBAdnc"?>
49 <PackageGroupRef Id="TestBAdnc" />
50 <?elseif $(var.BA) = "TestBA_x64"?>
51 <PackageGroupRef Id="TestBA_x64" />
52 <?elseif $(var.BA) = "TestBAdnc_x64"?>
53 <PackageGroupRef Id="TestBAdnc_x64" />
54 <?elseif $(var.BA) = "WixBA"?>
55 <PackageGroupRef Id="WixBA" />
56 <?elseif $(var.BA) = "WixBAdnc_x64"?>
57 <PackageGroupRef Id="WixBAdnc_x64" />
58 <?endif?>
59
60 <PackageGroupRef Id="BundlePackages" />
61 </Chain>
62 </Bundle>
63</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wixproj b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wixproj
new file mode 100644
index 00000000..7c856200
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wixproj
@@ -0,0 +1,15 @@
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>iui</BA>
6 <UpgradeCode>{5115D6AC-A1FE-40C6-BEB3-BEBB39E61579}</UpgradeCode>
7 </PropertyGroup>
8 <ItemGroup>
9 <ProjectReference Include="..\EmbeddedUIPackage\EmbeddedUIPackage.wixproj" />
10 <ProjectReference Include="..\InternalUIPackage\InternalUIPackage.wixproj" />
11 </ItemGroup>
12 <ItemGroup>
13 <PackageReference Include="WixToolset.Bal.wixext" />
14 </ItemGroup>
15</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wxs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wxs
new file mode 100644
index 00000000..6adda9de
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wxs
@@ -0,0 +1,11 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3
4<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <Fragment>
6 <PackageGroup Id="BundlePackages">
7 <MsiPackage Id="InternalUIPackage" SourceFile="$(var.InternalUIPackage.TargetPath)" Permanent="yes" />
8 <MsiPackage Id="EmbeddedUIPackage" SourceFile="$(var.EmbeddedUIPackage.TargetPath)" />
9 </PackageGroup>
10 </Fragment>
11</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wixproj b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wixproj
new file mode 100644
index 00000000..0a62f38b
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wixproj
@@ -0,0 +1,16 @@
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 <ProductComponentsRef>true</ProductComponentsRef>
5 <UpgradeCode>{A6826B6D-2F3F-456D-BABF-1B8CDCE3AE68}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <ProjectReference Include="..\EmbeddedUI\EmbeddedUI.csproj" />
12 </ItemGroup>
13 <ItemGroup>
14 <PackageReference Include="WixToolset.UI.wixext" />
15 </ItemGroup>
16</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wxs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wxs
new file mode 100644
index 00000000..e5d18c80
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wxs
@@ -0,0 +1,13 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
4 <Fragment>
5 <ComponentGroup Id="ProductComponents" />
6
7 <ui:WixUI Id="WixUI_Minimal" />
8
9 <UI>
10 <EmbeddedUI SourceFile="$(var.EmbeddedUI.TargetDir)$(var.EmbeddedUI.TargetName).CA.dll" />
11 </UI>
12 </Fragment>
13</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/Bundle.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/Bundle.wxs
new file mode 100644
index 00000000..195c159e
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/Bundle.wxs
@@ -0,0 +1,63 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3<?ifndef Version?>
4<?define Version = 1.0.0.0?>
5<?endif?>
6<?ifndef BundleLogDirectory?>
7<?define BundleLogDirectory = .?>
8<?endif?>
9
10<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
11 <!-- The only difference from the template should be the SplashScreen and uncompressed -->
12 <Bundle Name="~$(var.TestGroupName) - $(var.BundleName)" Version="$(var.Version)" UpgradeCode="$(var.UpgradeCode)" Compressed="no" SplashScreenSourceFile="..\..\Manual\BafThmutilTesting\theme\star_transparent.bmp">
13 <Log Prefix="$(var.BundleLogDirectory)\~$(var.TestGroupName)_$(var.BundleName)" />
14
15 <Variable Name="TestGroupName" Value="$(var.TestGroupName)" />
16
17 <?ifdef SoftwareTag?>
18 <SoftwareTag Regid="regid.1995-08.com.example" InstallPath="[CommonAppDataFolder]regid.1995-08.com.example" />
19 <?endif?>
20
21 <?ifndef BA?>
22 <!-- pulled in through the PackageGroupRef below -->
23 <?elseif $(var.BA) = "TestBAdnc"?>
24 <!-- pulled in through the PackageGroupRef below -->
25 <?elseif $(var.BA) = "TestBA_x64"?>
26 <!-- pulled in through the PackageGroupRef below -->
27 <?elseif $(var.BA) = "TestBAdnc_x64"?>
28 <!-- pulled in through the PackageGroupRef below -->
29 <?elseif $(var.BA) = "WixBA"?>
30 <!-- pulled in through the PackageGroupRef below -->
31 <?elseif $(var.BA) = "WixBAdnc_x64"?>
32 <!-- pulled in through the PackageGroupRef below -->
33 <?elseif $(var.BA) = "hyperlinkLicense"?>
34 <BootstrapperApplication>
35 <bal:WixStandardBootstrapperApplication LicenseUrl="" Theme="hyperlinkLicense" />
36 </BootstrapperApplication>
37 <?elseif $(var.BA) = "iui"?>
38 <BootstrapperApplication>
39 <bal:WixInternalUIBootstrapperApplication />
40 </BootstrapperApplication>
41 <?else?>
42 <BootstrapperApplicationRef Id="$(var.BA)" />
43 <?endif?>
44
45 <Chain>
46 <?ifndef BA?>
47 <PackageGroupRef Id="TestBA" />
48 <?elseif $(var.BA) = "TestBAdnc"?>
49 <PackageGroupRef Id="TestBAdnc" />
50 <?elseif $(var.BA) = "TestBA_x64"?>
51 <PackageGroupRef Id="TestBA_x64" />
52 <?elseif $(var.BA) = "TestBAdnc_x64"?>
53 <PackageGroupRef Id="TestBAdnc_x64" />
54 <?elseif $(var.BA) = "WixBA"?>
55 <PackageGroupRef Id="WixBA" />
56 <?elseif $(var.BA) = "WixBAdnc_x64"?>
57 <PackageGroupRef Id="WixBAdnc_x64" />
58 <?endif?>
59
60 <PackageGroupRef Id="BundlePackages" />
61 </Chain>
62 </Bundle>
63</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wixproj
new file mode 100644
index 00000000..193d0ca6
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wixproj
@@ -0,0 +1,14 @@
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>iui</BA>
6 <UpgradeCode>{62CB2BAE-129D-41D1-928B-5353FC542130}</UpgradeCode>
7 </PropertyGroup>
8 <ItemGroup>
9 <ProjectReference Include="..\InternalUIPackage\InternalUIPackage.wixproj" />
10 </ItemGroup>
11 <ItemGroup>
12 <PackageReference Include="WixToolset.Bal.wixext" />
13 </ItemGroup>
14</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wxs
new file mode 100644
index 00000000..332f0b08
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wxs
@@ -0,0 +1,10 @@
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 <MsiPackage Id="InternalUIPackage" SourceFile="$(var.InternalUIPackage.TargetPath)" />
8 </PackageGroup>
9 </Fragment>
10</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wixproj
new file mode 100644
index 00000000..2744d4b3
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wixproj
@@ -0,0 +1,16 @@
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 <ProductComponentsRef>true</ProductComponentsRef>
5 <UpgradeCode>{7A0FE267-9EAE-4780-B41E-60C9E7044F53}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <ProjectReference Include="..\ManagedCA\ManagedCA.csproj" />
12 </ItemGroup>
13 <ItemGroup>
14 <PackageReference Include="WixToolset.UI.wixext" />
15 </ItemGroup>
16</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wxs
new file mode 100644
index 00000000..d5f30729
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wxs
@@ -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
3<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
4 <Fragment>
5 <ComponentGroup Id="ProductComponents" />
6
7 <ui:WixUI Id="WixUI_Minimal" />
8
9 <Binary Id="TestData" SourceFile="$(var.ManagedCA.TargetDir)testsub\testfile.txt" />
10 <Binary Id="ManagedCA" SourceFile="$(var.ManagedCA.TargetDir)$(var.ManagedCA.TargetName).CA.dll" />
11 <CustomAction Id="SampleCA1" BinaryRef="ManagedCA" DllEntry="SampleCA1" Execute="firstSequence" />
12 <CustomAction Id="SampleCustomAction2" BinaryRef="ManagedCA" DllEntry="SampleCA2" Execute="firstSequence" Return="ignore" />
13
14 <InstallUISequence>
15 <Custom Action="SampleCA1" After="CostFinalize" Condition="NOT Installed" />
16 <Custom Action="SampleCustomAction2" After="SampleCA1" Condition="NOT Installed" />
17 </InstallUISequence>
18
19 <InstallExecuteSequence>
20 <Custom Action="SampleCA1" After="CostFinalize" Condition="NOT Installed" />
21 <Custom Action="SampleCustomAction2" After="SampleCA1" Condition="NOT Installed" />
22 </InstallExecuteSequence>
23 </Fragment>
24</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wixproj
new file mode 100644
index 00000000..e7d5b484
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wixproj
@@ -0,0 +1,14 @@
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 <InstallerPlatform>arm64</InstallerPlatform>
5 <ProductComponentsRef>true</ProductComponentsRef>
6 <UpgradeCode>{357EBF93-039C-4378-8BCB-8B53F9B9F69A}</UpgradeCode>
7 </PropertyGroup>
8 <ItemGroup>
9 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
10 </ItemGroup>
11 <ItemGroup>
12 <PackageReference Include="WixToolset.UI.wixext" />
13 </ItemGroup>
14</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wxs
new file mode 100644
index 00000000..c30cf607
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wxs
@@ -0,0 +1,9 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
4 <Fragment>
5 <ComponentGroup Id="ProductComponents" />
6
7 <ui:WixUI Id="WixUI_Minimal" />
8 </Fragment>
9</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wixproj
new file mode 100644
index 00000000..ff646914
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wixproj
@@ -0,0 +1,14 @@
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 <InstallerPlatform>x64</InstallerPlatform>
5 <ProductComponentsRef>true</ProductComponentsRef>
6 <UpgradeCode>{2EBACF8F-BECD-401C-94F2-CFA2C9C3C07F}</UpgradeCode>
7 </PropertyGroup>
8 <ItemGroup>
9 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
10 </ItemGroup>
11 <ItemGroup>
12 <PackageReference Include="WixToolset.UI.wixext" />
13 </ItemGroup>
14</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wxs
new file mode 100644
index 00000000..c30cf607
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wxs
@@ -0,0 +1,9 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
4 <Fragment>
5 <ComponentGroup Id="ProductComponents" />
6
7 <ui:WixUI Id="WixUI_Minimal" />
8 </Fragment>
9</Wix>
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wixproj
new file mode 100644
index 00000000..4107ab44
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wixproj
@@ -0,0 +1,14 @@
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 <InstallerPlatform>x64</InstallerPlatform>
5 <ProductComponentsRef>true</ProductComponentsRef>
6 <UpgradeCode>{1FA46DA0-5F83-459A-8A30-3E5A54D20A4D}</UpgradeCode>
7 </PropertyGroup>
8 <ItemGroup>
9 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
10 </ItemGroup>
11 <ItemGroup>
12 <PackageReference Include="WixToolset.UI.wixext" />
13 </ItemGroup>
14</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wxs
new file mode 100644
index 00000000..c30cf607
--- /dev/null
+++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wxs
@@ -0,0 +1,9 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
4 <Fragment>
5 <ComponentGroup Id="ProductComponents" />
6
7 <ui:WixUI Id="WixUI_Minimal" />
8 </Fragment>
9</Wix>
diff --git a/src/test/dtf/SampleCA/CustomAction.config b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/CustomAction.config
index 700aff6f..700aff6f 100644
--- a/src/test/dtf/SampleCA/CustomAction.config
+++ b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/CustomAction.config
diff --git a/src/test/dtf/SampleCA/SampleCA.csproj b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/ManagedCA.csproj
index 866b7575..866b7575 100644
--- a/src/test/dtf/SampleCA/SampleCA.csproj
+++ b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/ManagedCA.csproj
diff --git a/src/test/dtf/SampleCA/SampleCA.cs b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/SampleCA.cs
index fc9f30fe..fc9f30fe 100644
--- a/src/test/dtf/SampleCA/SampleCA.cs
+++ b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/SampleCA.cs
diff --git a/src/test/dtf/SampleCA/testsub/testfile.txt b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/testsub/testfile.txt
index 8056aefd..8056aefd 100644
--- a/src/test/dtf/SampleCA/testsub/testfile.txt
+++ b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/testsub/testfile.txt
diff --git a/src/test/burn/WixTestTools/PackageVerifier.cs b/src/test/burn/WixTestTools/PackageVerifier.cs
index fd8378e0..4545b9ec 100644
--- a/src/test/burn/WixTestTools/PackageVerifier.cs
+++ b/src/test/burn/WixTestTools/PackageVerifier.cs
@@ -63,17 +63,27 @@ namespace WixTestTools
63 return row.Value; 63 return row.Value;
64 } 64 }
65 65
66 public void VerifyInstalled(bool installed) 66 public bool IsInstalled()
67 { 67 {
68 var productCode = this.GetProperty("ProductCode"); 68 var productCode = this.GetProperty("ProductCode");
69 Assert.Equal(installed, MsiUtilities.IsProductInstalled(productCode)); 69 return MsiUtilities.IsProductInstalled(productCode);
70 } 70 }
71 71
72 public void VerifyInstalledWithVersion(bool installed) 72 public bool IsInstalledWithVersion()
73 { 73 {
74 var productCode = this.GetProperty("ProductCode"); 74 var productCode = this.GetProperty("ProductCode");
75 Version prodVersion = new Version(this.GetProperty("ProductVersion")); 75 Version prodVersion = new Version(this.GetProperty("ProductVersion"));
76 Assert.Equal(installed, MsiUtilities.IsProductInstalledWithVersion(productCode, prodVersion)); 76 return MsiUtilities.IsProductInstalledWithVersion(productCode, prodVersion);
77 }
78
79 public void VerifyInstalled(bool installed)
80 {
81 Assert.Equal(installed, this.IsInstalled());
82 }
83
84 public void VerifyInstalledWithVersion(bool installed)
85 {
86 Assert.Equal(installed, this.IsInstalledWithVersion());
77 } 87 }
78 88
79 public void DeleteTestRegistryValue(string name) 89 public void DeleteTestRegistryValue(string name)
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs
new file mode 100644
index 00000000..18dd41db
--- /dev/null
+++ b/src/test/burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs
@@ -0,0 +1,144 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolsetTest.BurnE2E
4{
5 using WixTestTools;
6 using Xunit;
7 using Xunit.Abstractions;
8
9 public class WixIuiBaTests : BurnE2ETests
10 {
11 public WixIuiBaTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
12
13 [RuntimeFact]
14 public void CanInstallArchitectureSpecificPrimaryPackage()
15 {
16 var defaultPackage = this.CreatePackageInstaller("InternalUIPackage");
17 var x86Package = this.CreatePackageInstaller("InternalUIx86Package");
18 var x64Package = this.CreatePackageInstaller("InternalUIx64Package");
19 var arm64Package = this.CreatePackageInstaller("InternalUIarm64Package");
20 var bundle = this.CreateBundleInstaller("ArchSpecificBundle");
21
22 defaultPackage.VerifyInstalled(false);
23 x86Package.VerifyInstalled(false);
24 x64Package.VerifyInstalled(false);
25 arm64Package.VerifyInstalled(false);
26 bundle.VerifyUnregisteredAndRemovedFromPackageCache();
27
28 bundle.Install();
29 bundle.VerifyRegisteredAndInPackageCache();
30 defaultPackage.VerifyInstalled(false);
31
32 var archSpecificInstalls = 0;
33 if (x86Package.IsInstalled())
34 {
35 ++archSpecificInstalls;
36 }
37
38 if (x64Package.IsInstalled())
39 {
40 ++archSpecificInstalls;
41 }
42
43 if (arm64Package.IsInstalled())
44 {
45 ++archSpecificInstalls;
46 }
47
48 Assert.Equal(1, archSpecificInstalls);
49
50 bundle.Uninstall();
51 bundle.VerifyUnregisteredAndRemovedFromPackageCache();
52 defaultPackage.VerifyInstalled(false);
53 x86Package.VerifyInstalled(false);
54 x64Package.VerifyInstalled(false);
55 arm64Package.VerifyInstalled(false);
56 }
57
58 [RuntimeFact]
59 public void CanSilentlyInstallAndUninstallEmbeddedUIBundle()
60 {
61 var prereqPackage = this.CreatePackageInstaller("InternalUIPackage");
62 var package = this.CreatePackageInstaller("EmbeddedUIPackage");
63 var bundle = this.CreateBundleInstaller("EmbeddedUIBundle");
64
65 prereqPackage.VerifyInstalled(false);
66 package.VerifyInstalled(false);
67 bundle.VerifyUnregisteredAndRemovedFromPackageCache();
68
69 bundle.Install();
70 bundle.VerifyRegisteredAndInPackageCache();
71 prereqPackage.VerifyInstalled(true);
72 package.VerifyInstalled(true);
73
74 bundle.Uninstall();
75 bundle.VerifyUnregisteredAndRemovedFromPackageCache();
76 prereqPackage.VerifyInstalled(true);
77 package.VerifyInstalled(false);
78 }
79
80 [RuntimeFact]
81 public void CanSilentlyInstallAndUninstallInternalUIBundle()
82 {
83 var package = this.CreatePackageInstaller("InternalUIPackage");
84 var bundle = this.CreateBundleInstaller("InternalUIBundle");
85
86 package.VerifyInstalled(false);
87 bundle.VerifyUnregisteredAndRemovedFromPackageCache();
88
89 bundle.Install();
90 bundle.VerifyRegisteredAndInPackageCache();
91 package.VerifyInstalled(true);
92
93 bundle.Uninstall();
94 bundle.VerifyUnregisteredAndRemovedFromPackageCache();
95 package.VerifyInstalled(false);
96 }
97
98 // Manual test for EmbeddedUIBundle:
99 // 1. Double click EmbeddedUIBundle.exe.
100 // 2. Verify that the prereq BA came up and click the install button (allow elevation).
101 // 3. Verify that the prereq BA automatically closed after installing the prereq.
102 // 4. Verify that the MSI UI came up and click the install button.
103 // 5. After it's finished, click the exit button.
104 // 6. Verify that no other UI is shown and that everything was installed.
105 // 7. Double click EmbeddedUIBundle.exe (allow elevation).
106 // 8. Verify that the prereq BA did not come up.
107 // 9. Verify that the MSI UI came up and click the uninstall button.
108 // 10. After it's finished, click the exit button.
109 // 11. Verify that no other UI is shown and that everything was uninstalled except for the prereq which was permanent.
110 // 12. Uninstall InternalUIPackage to make sure the machine is clean for other tests.
111
112 // Alternate EmbeddedUIBundle test - manually install InternalUIPackage first and verify that the prereq BA doesn't come up during install either.
113
114 // Manual test for InternalUIBundle:
115 // 1. Double click InternalUIBundle.exe on a machine that will prompt for elevation.
116 // 2. Verify that the splash screen appeared but the prereq BA did not come up.
117 // 3. Verify that the elevation prompt came up immediately instead of flashing on the taskbar. (This is currently broken)
118 // 4. Allow elevation.
119 // 5. Verify that the MSI UI came up and the splash screen disappeared.
120 // 6. Accept the two CA messages and click the install button.
121 // 7. After it's finished, click the exit button.
122 // 8. Verify that no other UI is shown and that everything was installed.
123 // 9. Double click InternalUIBundle.exe (allow elevation).
124 // 10. Verify that the prereq BA did not come up.
125 // 11. Verify that the MSI UI came up and click the uninstall button.
126 // 12. After it's finished, click the exit button.
127 // 13. Verify that no other UI is shown and that everything was uninstalled to make sure the machine is clean for other tests.
128
129 // Manual test for Help:
130 // 1. Run EmbeddedUIBundle.exe /help from the command line.
131 // 2. Verify that the prereq BA shows the help information without trying to install the prereqs.
132
133 // Manual test for Layout:
134 // 1. Run EmbeddedUIBundle.exe /layout from an unelevated command line on a machine that will prompt for elevation.
135 // 2. Verify that the prereq BA performs the layout without requiring any input from the user.
136 // 3. Verify that it never prompted for elevation.
137 // 4. Click the exit button.
138
139 // Manual test for Caching error:
140 // 1. Copy InternalUIBundle.exe to a separate folder so that it can't find InternalUIPackage.msi.
141 // 2. Attempt to install InternalUIBundle.exe (allow elevation).
142 // 3. Verify that the prereq BA comes up with the Failure page saying that a file couldn't be found.
143 }
144}
diff --git a/src/test/dtf/Directory.Build.props b/src/test/dtf/Directory.Build.props
deleted file mode 100644
index 0035a9e6..00000000
--- a/src/test/dtf/Directory.Build.props
+++ /dev/null
@@ -1,11 +0,0 @@
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<Project>
4 <PropertyGroup>
5 <SegmentName>IntegrationDtf</SegmentName>
6 <SignOutput>false</SignOutput>
7 </PropertyGroup>
8
9 <Import Project="..\..\Directory.Build.props" />
10 <Import Project="Directory$(MSBuildProjectExtension).props" Condition=" Exists('Directory$(MSBuildProjectExtension).props') " />
11</Project>
diff --git a/src/test/dtf/Directory.Build.targets b/src/test/dtf/Directory.Build.targets
deleted file mode 100644
index 4e97b6ca..00000000
--- a/src/test/dtf/Directory.Build.targets
+++ /dev/null
@@ -1,6 +0,0 @@
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<Project>
4 <Import Project="..\..\Directory.Build.targets" />
5 <Import Project="Directory$(MSBuildProjectExtension).targets" Condition=" Exists('Directory$(MSBuildProjectExtension).targets') " />
6</Project>
diff --git a/src/test/test.cmd b/src/test/test.cmd
index 1c2c154b..85deb61e 100644
--- a/src/test/test.cmd
+++ b/src/test/test.cmd
@@ -14,8 +14,6 @@
14@call msi\test_msi.cmd %_C% %_T% || exit /b 14@call msi\test_msi.cmd %_C% %_T% || exit /b
15@call burn\test_burn.cmd %_C% %_T% || exit /b 15@call burn\test_burn.cmd %_C% %_T% || exit /b
16 16
17msbuild -Restore dtf\DtfE2ETests.sln -p:Configuration=%_C% -nologo -m -warnaserror -bl:%_L%\dtfe2etests.binlog || exit /b
18
19dotnet test wix -c %_C% --nologo -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.WixE2ETests.trx" || exit /b 17dotnet test wix -c %_C% --nologo -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.WixE2ETests.trx" || exit /b
20 18
21@popd 19@popd
diff --git a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
index 740a6e26..9eeafee3 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
@@ -10,6 +10,7 @@ namespace WixToolset.Core.Burn.Bind
10 using WixToolset.Core.Burn.Bundles; 10 using WixToolset.Core.Burn.Bundles;
11 using WixToolset.Core.Burn.ExtensibilityServices; 11 using WixToolset.Core.Burn.ExtensibilityServices;
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.Burn;
13 using WixToolset.Data.Symbols; 14 using WixToolset.Data.Symbols;
14 using WixToolset.Extensibility; 15 using WixToolset.Extensibility;
15 using WixToolset.Extensibility.Services; 16 using WixToolset.Extensibility.Services;
@@ -191,10 +192,10 @@ namespace WixToolset.Core.Burn.Bind
191 switch (customDataSymbol.Type) 192 switch (customDataSymbol.Type)
192 { 193 {
193 case WixBundleCustomDataType.BootstrapperApplication: 194 case WixBundleCustomDataType.BootstrapperApplication:
194 writer.WriteStartElement(elementName, BurnCommon.BADataNamespace); 195 writer.WriteStartElement(elementName, BurnConstants.BootstrapperApplicationDataNamespace);
195 break; 196 break;
196 case WixBundleCustomDataType.BundleExtension: 197 case WixBundleCustomDataType.BundleExtension:
197 writer.WriteStartElement(elementName, BurnCommon.BundleExtensionDataNamespace); 198 writer.WriteStartElement(elementName, BurnConstants.BundleExtensionDataNamespace);
198 break; 199 break;
199 default: 200 default:
200 throw new NotImplementedException(); 201 throw new NotImplementedException();
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
index 25ad88cf..08ed7d56 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
@@ -24,10 +24,8 @@ namespace WixToolset.Core.Burn.Bundles
24 public const string BurnAuthoredContainerEmbeddedIdFormat = "a{0}"; 24 public const string BurnAuthoredContainerEmbeddedIdFormat = "a{0}";
25 25
26 public const string BADataFileName = "BootstrapperApplicationData.xml"; 26 public const string BADataFileName = "BootstrapperApplicationData.xml";
27 public const string BADataNamespace = "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData";
28 27
29 public const string BundleExtensionDataFileName = "BundleExtensionData.xml"; 28 public const string BundleExtensionDataFileName = "BundleExtensionData.xml";
30 public const string BundleExtensionDataNamespace = "http://wixtoolset.org/schemas/v4/BundleExtensionData";
31 29
32 // See WinNT.h for details about the PE format, including the 30 // See WinNT.h for details about the PE format, including the
33 // structure and offsets for IMAGE_DOS_HEADER, IMAGE_NT_HEADERS32, 31 // structure and offsets for IMAGE_DOS_HEADER, IMAGE_NT_HEADERS32,
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
index 3167ccd3..6d8e0822 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
@@ -67,7 +67,7 @@ namespace WixToolset.Core.Burn.Bundles
67 { 67 {
68 writer.Formatting = Formatting.Indented; 68 writer.Formatting = Formatting.Indented;
69 writer.WriteStartDocument(); 69 writer.WriteStartDocument();
70 writer.WriteStartElement("BootstrapperApplicationData", BurnCommon.BADataNamespace); 70 writer.WriteStartElement("BootstrapperApplicationData", BurnConstants.BootstrapperApplicationDataNamespace);
71 71
72 this.WriteBundleInfo(writer); 72 this.WriteBundleInfo(writer);
73 73
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExtensionManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExtensionManifestCommand.cs
index e587413e..ab6dcea1 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExtensionManifestCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExtensionManifestCommand.cs
@@ -53,7 +53,7 @@ namespace WixToolset.Core.Burn.Bundles
53 { 53 {
54 writer.Formatting = Formatting.Indented; 54 writer.Formatting = Formatting.Indented;
55 writer.WriteStartDocument(); 55 writer.WriteStartDocument();
56 writer.WriteStartElement("BundleExtensionData", BurnCommon.BundleExtensionDataNamespace); 56 writer.WriteStartElement("BundleExtensionData", BurnConstants.BundleExtensionDataNamespace);
57 57
58 this.InternalBurnBackendHelper.WriteBundleExtensionData(writer); 58 this.InternalBurnBackendHelper.WriteBundleExtensionData(writer);
59 59
diff --git a/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs b/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
index 52f01f58..e267369f 100644
--- a/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
+++ b/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
@@ -9,6 +9,7 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
9 using System.Xml; 9 using System.Xml;
10 using WixToolset.Core.Burn.Bundles; 10 using WixToolset.Core.Burn.Bundles;
11 using WixToolset.Data; 11 using WixToolset.Data;
12 using WixToolset.Data.Burn;
12 using WixToolset.Data.Symbols; 13 using WixToolset.Data.Symbols;
13 using WixToolset.Data.WindowsInstaller.Rows; 14 using WixToolset.Data.WindowsInstaller.Rows;
14 using WixToolset.Extensibility.Data; 15 using WixToolset.Extensibility.Data;
@@ -160,7 +161,7 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
160 161
161 public void AddBootstrapperApplicationData(IntermediateSymbol symbol, bool symbolIdIsIdAttribute = false) 162 public void AddBootstrapperApplicationData(IntermediateSymbol symbol, bool symbolIdIsIdAttribute = false)
162 { 163 {
163 this.BootstrapperApplicationManifestData.AddSymbol(symbol, symbolIdIsIdAttribute, BurnCommon.BADataNamespace); 164 this.BootstrapperApplicationManifestData.AddSymbol(symbol, symbolIdIsIdAttribute, BurnConstants.BootstrapperApplicationDataNamespace);
164 } 165 }
165 166
166 public void AddBundleExtensionData(string extensionId, string xml) 167 public void AddBundleExtensionData(string extensionId, string xml)
@@ -172,7 +173,7 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
172 public void AddBundleExtensionData(string extensionId, IntermediateSymbol symbol, bool symbolIdIsIdAttribute = false) 173 public void AddBundleExtensionData(string extensionId, IntermediateSymbol symbol, bool symbolIdIsIdAttribute = false)
173 { 174 {
174 var manifestData = this.GetBundleExtensionManifestData(extensionId); 175 var manifestData = this.GetBundleExtensionManifestData(extensionId);
175 manifestData.AddSymbol(symbol, symbolIdIsIdAttribute, BurnCommon.BundleExtensionDataNamespace); 176 manifestData.AddSymbol(symbol, symbolIdIsIdAttribute, BurnConstants.BundleExtensionDataNamespace);
176 } 177 }
177 178
178 #endregion 179 #endregion
diff --git a/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs b/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
index bd13a9b2..3cc98e3a 100644
--- a/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
+++ b/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
@@ -5,6 +5,7 @@ namespace WixToolset.Core.TestPackage
5 using System.IO; 5 using System.IO;
6 using System.Xml; 6 using System.Xml;
7 using WixToolset.Core.Burn.Bundles; 7 using WixToolset.Core.Burn.Bundles;
8 using WixToolset.Data.Burn;
8 using WixToolset.Extensibility.Services; 9 using WixToolset.Extensibility.Services;
9 10
10 /// <summary> 11 /// <summary>
@@ -72,7 +73,7 @@ namespace WixToolset.Core.TestPackage
72 public static XmlNamespaceManager GetBADataNamespaceManager(XmlDocument document, string prefix) 73 public static XmlNamespaceManager GetBADataNamespaceManager(XmlDocument document, string prefix)
73 { 74 {
74 var namespaceManager = new XmlNamespaceManager(document.NameTable); 75 var namespaceManager = new XmlNamespaceManager(document.NameTable);
75 namespaceManager.AddNamespace(prefix, BurnCommon.BADataNamespace); 76 namespaceManager.AddNamespace(prefix, BurnConstants.BootstrapperApplicationDataNamespace);
76 return namespaceManager; 77 return namespaceManager;
77 } 78 }
78 79
@@ -85,7 +86,7 @@ namespace WixToolset.Core.TestPackage
85 public static XmlNamespaceManager GetBundleExtensionDataNamespaceManager(XmlDocument document, string prefix) 86 public static XmlNamespaceManager GetBundleExtensionDataNamespaceManager(XmlDocument document, string prefix)
86 { 87 {
87 var namespaceManager = new XmlNamespaceManager(document.NameTable); 88 var namespaceManager = new XmlNamespaceManager(document.NameTable);
88 namespaceManager.AddNamespace(prefix, BurnCommon.BundleExtensionDataNamespace); 89 namespaceManager.AddNamespace(prefix, BurnConstants.BundleExtensionDataNamespace);
89 return namespaceManager; 90 return namespaceManager;
90 } 91 }
91 92