aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-04-22 16:56:21 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-04-23 15:40:21 -0500
commita981e29d7a3df566754356c3fe1eb938a5cac4c1 (patch)
tree9d2b2abac872ae3c4917003812951e68b72e7163 /src
parent72e20f682c0d64102e86439ba5527dd0d71932ae (diff)
downloadwix-a981e29d7a3df566754356c3fe1eb938a5cac4c1.tar.gz
wix-a981e29d7a3df566754356c3fe1eb938a5cac4c1.tar.bz2
wix-a981e29d7a3df566754356c3fe1eb938a5cac4c1.zip
Make the estimated size in ARP a little more accurate.
Fixes 4039
Diffstat (limited to 'src')
-rw-r--r--src/burn/engine/apply.cpp43
-rw-r--r--src/burn/engine/elevation.cpp10
-rw-r--r--src/burn/engine/elevation.h1
-rw-r--r--src/burn/engine/plan.cpp26
-rw-r--r--src/burn/engine/plan.h2
-rw-r--r--src/burn/engine/registration.cpp61
-rw-r--r--src/burn/engine/registration.h1
-rw-r--r--src/burn/test/BurnUnitTest/PlanTest.cpp25
-rw-r--r--src/burn/test/BurnUnitTest/RegistrationTest.cpp42
-rw-r--r--src/libs/dutil/WixToolset.DUtil/regutil.cpp5
-rw-r--r--src/test/burn/TestBA/TestBA.cs17
-rw-r--r--src/test/burn/WixTestTools/BundleVerifier.cs4
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/BasicFunctionalityTests.cs3
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs8
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/MsiTransactionTests.cs8
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/RegistrationTests.cs29
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs5
17 files changed, 177 insertions, 113 deletions
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
index 9ee7b58c..69106f65 100644
--- a/src/burn/engine/apply.cpp
+++ b/src/burn/engine/apply.cpp
@@ -79,7 +79,8 @@ static HRESULT WINAPI AuthenticationRequired(
79static void CalculateKeepRegistration( 79static void CalculateKeepRegistration(
80 __in BURN_ENGINE_STATE* pEngineState, 80 __in BURN_ENGINE_STATE* pEngineState,
81 __in BOOL fLog, 81 __in BOOL fLog,
82 __inout BOOTSTRAPPER_REGISTRATION_TYPE* pRegistrationType 82 __inout BOOTSTRAPPER_REGISTRATION_TYPE* pRegistrationType,
83 __inout DWORD64* pqwEstimatedSize
83 ); 84 );
84static HRESULT ExecuteDependentRegistrationActions( 85static HRESULT ExecuteDependentRegistrationActions(
85 __in HANDLE hPipe, 86 __in HANDLE hPipe,
@@ -422,8 +423,9 @@ extern "C" HRESULT ApplyRegister(
422 HRESULT hr = S_OK; 423 HRESULT hr = S_OK;
423 LPWSTR sczEngineWorkingPath = NULL; 424 LPWSTR sczEngineWorkingPath = NULL;
424 BOOTSTRAPPER_REGISTRATION_TYPE registrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS; 425 BOOTSTRAPPER_REGISTRATION_TYPE registrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS;
426 DWORD64 qwEstimatedSize = 0;
425 427
426 CalculateKeepRegistration(pEngineState, FALSE, &registrationType); 428 CalculateKeepRegistration(pEngineState, FALSE, &registrationType, &qwEstimatedSize);
427 429
428 hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience, &registrationType); 430 hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience, &registrationType);
429 ExitOnRootFailure(hr, "BA aborted register begin."); 431 ExitOnRootFailure(hr, "BA aborted register begin.");
@@ -451,12 +453,12 @@ extern "C" HRESULT ApplyRegister(
451 // begin new session 453 // begin new session
452 if (pEngineState->registration.fPerMachine) 454 if (pEngineState->registration.fPerMachine)
453 { 455 {
454 hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->registration.fDetectedForeignProviderKeyBundleId, pEngineState->plan.qwEstimatedSize, registrationType); 456 hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->registration.fDetectedForeignProviderKeyBundleId, qwEstimatedSize, registrationType);
455 ExitOnFailure(hr, "Failed to begin registration session in per-machine process."); 457 ExitOnFailure(hr, "Failed to begin registration session in per-machine process.");
456 } 458 }
457 else 459 else
458 { 460 {
459 hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->cache, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.qwEstimatedSize, registrationType); 461 hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->cache, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, qwEstimatedSize, registrationType);
460 ExitOnFailure(hr, "Failed to begin registration session."); 462 ExitOnFailure(hr, "Failed to begin registration session.");
461 } 463 }
462 } 464 }
@@ -491,6 +493,7 @@ extern "C" HRESULT ApplyUnregister(
491 BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE; 493 BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE;
492 BOOTSTRAPPER_REGISTRATION_TYPE defaultRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE; 494 BOOTSTRAPPER_REGISTRATION_TYPE defaultRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE;
493 BOOTSTRAPPER_REGISTRATION_TYPE registrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE; 495 BOOTSTRAPPER_REGISTRATION_TYPE registrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE;
496 DWORD64 qwEstimatedSize = 0;
494 497
495 // Calculate special cases for the resume mode. If a restart has been initiated, that trumps all other 498 // Calculate special cases for the resume mode. If a restart has been initiated, that trumps all other
496 // modes. If the user chose to suspend the install then we'll use that as the resume mode. 499 // modes. If the user chose to suspend the install then we'll use that as the resume mode.
@@ -513,7 +516,7 @@ extern "C" HRESULT ApplyUnregister(
513 defaultRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS; 516 defaultRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS;
514 } 517 }
515 518
516 CalculateKeepRegistration(pEngineState, TRUE, &defaultRegistrationType); 519 CalculateKeepRegistration(pEngineState, TRUE, &defaultRegistrationType, &qwEstimatedSize);
517 520
518 registrationType = defaultRegistrationType; 521 registrationType = defaultRegistrationType;
519 522
@@ -547,12 +550,12 @@ extern "C" HRESULT ApplyUnregister(
547 550
548 if (pEngineState->registration.fPerMachine) 551 if (pEngineState->registration.fPerMachine)
549 { 552 {
550 hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->registration.fDetectedForeignProviderKeyBundleId, registrationType); 553 hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->registration.fDetectedForeignProviderKeyBundleId, qwEstimatedSize, registrationType);
551 ExitOnFailure(hr, "Failed to end session in per-machine process."); 554 ExitOnFailure(hr, "Failed to end session in per-machine process.");
552 } 555 }
553 else 556 else
554 { 557 {
555 hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->cache, &pEngineState->variables, &pEngineState->packages, resumeMode, restart, registrationType); 558 hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->cache, &pEngineState->variables, &pEngineState->packages, resumeMode, restart, qwEstimatedSize, registrationType);
556 ExitOnFailure(hr, "Failed to end session in per-user process."); 559 ExitOnFailure(hr, "Failed to end session in per-user process.");
557 } 560 }
558 561
@@ -880,9 +883,12 @@ extern "C" void ApplyClean(
880static void CalculateKeepRegistration( 883static void CalculateKeepRegistration(
881 __in BURN_ENGINE_STATE* pEngineState, 884 __in BURN_ENGINE_STATE* pEngineState,
882 __in BOOL fLog, 885 __in BOOL fLog,
883 __inout BOOTSTRAPPER_REGISTRATION_TYPE* pRegistrationType 886 __inout BOOTSTRAPPER_REGISTRATION_TYPE* pRegistrationType,
887 __inout DWORD64* pqwEstimatedSize
884 ) 888 )
885{ 889{
890 DWORD64 qwEstimatedSize = pEngineState->section.qwBundleSize;
891
886 if (fLog) 892 if (fLog)
887 { 893 {
888 LogId(REPORT_STANDARD, MSG_POST_APPLY_CALCULATE_REGISTRATION); 894 LogId(REPORT_STANDARD, MSG_POST_APPLY_CALCULATE_REGISTRATION);
@@ -911,16 +917,29 @@ static void CalculateKeepRegistration(
911 { 917 {
912 *pRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_FULL; 918 *pRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_FULL;
913 919
914 if (!fLog) 920 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
915 { 921 {
916 break; 922 qwEstimatedSize += pPackage->qwSize;
917 } 923 }
924
925 qwEstimatedSize += pPackage->qwInstallSize;
918 } 926 }
919 else if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState && BOOTSTRAPPER_REGISTRATION_TYPE_NONE == *pRegistrationType) 927
928 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState)
920 { 929 {
921 *pRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS; 930 if (BOOTSTRAPPER_REGISTRATION_TYPE_NONE == *pRegistrationType)
931 {
932 *pRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS;
933 }
934
935 qwEstimatedSize += pPackage->qwSize;
922 } 936 }
923 } 937 }
938
939 if (pqwEstimatedSize)
940 {
941 *pqwEstimatedSize = qwEstimatedSize;
942 }
924} 943}
925 944
926static HRESULT ExecuteDependentRegistrationActions( 945static HRESULT ExecuteDependentRegistrationActions(
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp
index 56a62955..3305cf62 100644
--- a/src/burn/engine/elevation.cpp
+++ b/src/burn/engine/elevation.cpp
@@ -636,6 +636,7 @@ extern "C" HRESULT ElevationSessionEnd(
636 __in BURN_RESUME_MODE resumeMode, 636 __in BURN_RESUME_MODE resumeMode,
637 __in BOOTSTRAPPER_APPLY_RESTART restart, 637 __in BOOTSTRAPPER_APPLY_RESTART restart,
638 __in BOOL fDetectedForeignProviderKeyBundleId, 638 __in BOOL fDetectedForeignProviderKeyBundleId,
639 __in DWORD64 qwEstimatedSize,
639 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType 640 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType
640 ) 641 )
641{ 642{
@@ -654,6 +655,9 @@ extern "C" HRESULT ElevationSessionEnd(
654 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fDetectedForeignProviderKeyBundleId); 655 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fDetectedForeignProviderKeyBundleId);
655 ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); 656 ExitOnFailure(hr, "Failed to write dependency registration action to message buffer.");
656 657
658 hr = BuffWriteNumber64(&pbData, &cbData, qwEstimatedSize);
659 ExitOnFailure(hr, "Failed to write estimated size to message buffer.");
660
657 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)registrationType); 661 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)registrationType);
658 ExitOnFailure(hr, "Failed to write registration type to message buffer."); 662 ExitOnFailure(hr, "Failed to write registration type to message buffer.");
659 663
@@ -2580,6 +2584,7 @@ static HRESULT OnSessionEnd(
2580 SIZE_T iData = 0; 2584 SIZE_T iData = 0;
2581 DWORD dwResumeMode = 0; 2585 DWORD dwResumeMode = 0;
2582 DWORD dwRestart = 0; 2586 DWORD dwRestart = 0;
2587 DWORD64 qwEstimatedSize = 0;
2583 DWORD dwRegistrationType = 0; 2588 DWORD dwRegistrationType = 0;
2584 2589
2585 // Deserialize message data. 2590 // Deserialize message data.
@@ -2592,11 +2597,14 @@ static HRESULT OnSessionEnd(
2592 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDetectedForeignProviderKeyBundleId); 2597 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDetectedForeignProviderKeyBundleId);
2593 ExitOnFailure(hr, "Failed to read dependency registration action."); 2598 ExitOnFailure(hr, "Failed to read dependency registration action.");
2594 2599
2600 hr = BuffReadNumber64(pbData, cbData, &iData, &qwEstimatedSize);
2601 ExitOnFailure(hr, "Failed to read estimated size.");
2602
2595 hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationType); 2603 hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationType);
2596 ExitOnFailure(hr, "Failed to read dependency registration action."); 2604 ExitOnFailure(hr, "Failed to read dependency registration action.");
2597 2605
2598 // suspend session in per-machine process 2606 // suspend session in per-machine process
2599 hr = RegistrationSessionEnd(pRegistration, pCache, pVariables, pPackages, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BOOTSTRAPPER_REGISTRATION_TYPE)dwRegistrationType); 2607 hr = RegistrationSessionEnd(pRegistration, pCache, pVariables, pPackages, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, qwEstimatedSize, (BOOTSTRAPPER_REGISTRATION_TYPE)dwRegistrationType);
2600 ExitOnFailure(hr, "Failed to suspend registration session."); 2608 ExitOnFailure(hr, "Failed to suspend registration session.");
2601 2609
2602LExit: 2610LExit:
diff --git a/src/burn/engine/elevation.h b/src/burn/engine/elevation.h
index 3484057e..a74b6027 100644
--- a/src/burn/engine/elevation.h
+++ b/src/burn/engine/elevation.h
@@ -44,6 +44,7 @@ HRESULT ElevationSessionEnd(
44 __in BURN_RESUME_MODE resumeMode, 44 __in BURN_RESUME_MODE resumeMode,
45 __in BOOTSTRAPPER_APPLY_RESTART restart, 45 __in BOOTSTRAPPER_APPLY_RESTART restart,
46 __in BOOL fDetectedForeignProviderKeyBundleId, 46 __in BOOL fDetectedForeignProviderKeyBundleId,
47 __in DWORD64 qwEstimatedSize,
47 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType 48 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType
48 ); 49 );
49HRESULT ElevationSaveState( 50HRESULT ElevationSaveState(
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp
index 47da22c0..68c1d2ba 100644
--- a/src/burn/engine/plan.cpp
+++ b/src/burn/engine/plan.cpp
@@ -1178,31 +1178,6 @@ extern "C" HRESULT PlanExecutePackage(
1178 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; 1178 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1179 } 1179 }
1180 1180
1181 // Add the cache and install size to estimated size if it will be on the machine at the end of the install
1182 if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested ||
1183 fRequestedCache ||
1184 (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested)
1185 )
1186 {
1187 // If the package will remain in the cache, add the package size to the estimated size
1188 if (BOOTSTRAPPER_CACHE_TYPE_REMOVE < pPackage->cacheType)
1189 {
1190 pPlan->qwEstimatedSize += pPackage->qwSize;
1191 }
1192
1193 // If the package will end up installed on the machine, add the install size to the estimated size.
1194 if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested)
1195 {
1196 // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well
1197 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1198 {
1199 pPlan->qwEstimatedSize += pPackage->qwSize;
1200 }
1201
1202 pPlan->qwEstimatedSize += pPackage->qwInstallSize;
1203 }
1204 }
1205
1206 // Add execute actions. 1181 // Add execute actions.
1207 switch (pPackage->type) 1182 switch (pPackage->type)
1208 { 1183 {
@@ -3079,7 +3054,6 @@ extern "C" void PlanDump(
3079 LogStringLine(PlanDumpLevel, " disallow-removal: %hs", LoggingTrueFalseToString(pPlan->fDisallowRemoval)); 3054 LogStringLine(PlanDumpLevel, " disallow-removal: %hs", LoggingTrueFalseToString(pPlan->fDisallowRemoval));
3080 LogStringLine(PlanDumpLevel, " downgrade: %hs", LoggingTrueFalseToString(pPlan->fDowngrade)); 3055 LogStringLine(PlanDumpLevel, " downgrade: %hs", LoggingTrueFalseToString(pPlan->fDowngrade));
3081 LogStringLine(PlanDumpLevel, " registration options: %hs", LoggingRegistrationOptionsToString(pPlan->dwRegistrationOperations)); 3056 LogStringLine(PlanDumpLevel, " registration options: %hs", LoggingRegistrationOptionsToString(pPlan->dwRegistrationOperations));
3082 LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize);
3083 if (pPlan->sczLayoutDirectory) 3057 if (pPlan->sczLayoutDirectory)
3084 { 3058 {
3085 LogStringLine(PlanDumpLevel, " layout directory: %ls", pPlan->sczLayoutDirectory); 3059 LogStringLine(PlanDumpLevel, " layout directory: %ls", pPlan->sczLayoutDirectory);
diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h
index fbb7a5a1..2b04e097 100644
--- a/src/burn/engine/plan.h
+++ b/src/burn/engine/plan.h
@@ -267,8 +267,6 @@ typedef struct _BURN_PLAN
267 267
268 DWORD64 qwCacheSizeTotal; 268 DWORD64 qwCacheSizeTotal;
269 269
270 DWORD64 qwEstimatedSize;
271
272 DWORD cExecutePackagesTotal; 270 DWORD cExecutePackagesTotal;
273 DWORD cOverallProgressTicksTotal; 271 DWORD cOverallProgressTicksTotal;
274 272
diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp
index 0ffbc89e..9953be83 100644
--- a/src/burn/engine/registration.cpp
+++ b/src/burn/engine/registration.cpp
@@ -102,6 +102,10 @@ static HRESULT UpdateBundleNameRegistration(
102 __in HKEY hkRegistration, 102 __in HKEY hkRegistration,
103 __in BOOL fInProgressRegistration 103 __in BOOL fInProgressRegistration
104 ); 104 );
105static HRESULT UpdateEstimatedSize(
106 __in HKEY hkRegistration,
107 __in DWORD64 qwEstimatedSize
108 );
105static BOOL IsWuRebootPending(); 109static BOOL IsWuRebootPending();
106static BOOL IsRegistryRebootPending(); 110static BOOL IsRegistryRebootPending();
107 111
@@ -600,8 +604,8 @@ extern "C" HRESULT RegistrationSessionBegin(
600 ) 604 )
601{ 605{
602 HRESULT hr = S_OK; 606 HRESULT hr = S_OK;
603 DWORD dwSize = 0;
604 HKEY hkRegistration = NULL; 607 HKEY hkRegistration = NULL;
608 BOOL fCreated = FALSE;
605 LPWSTR sczPublisher = NULL; 609 LPWSTR sczPublisher = NULL;
606 610
607 AssertSz(BOOTSTRAPPER_REGISTRATION_TYPE_NONE != registrationType, "Registration type can't be NONE"); 611 AssertSz(BOOTSTRAPPER_REGISTRATION_TYPE_NONE != registrationType, "Registration type can't be NONE");
@@ -620,7 +624,7 @@ extern "C" HRESULT RegistrationSessionBegin(
620 } 624 }
621 625
622 // create registration key 626 // create registration key
623 hr = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); 627 hr = RegCreateEx(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, REG_KEY_DEFAULT, FALSE, NULL, &hkRegistration, &fCreated);
624 ExitOnFailure(hr, "Failed to create registration key."); 628 ExitOnFailure(hr, "Failed to create registration key.");
625 629
626 // Write any ARP values and software tags. 630 // Write any ARP values and software tags.
@@ -797,22 +801,12 @@ extern "C" HRESULT RegistrationSessionBegin(
797 ExitOnFailure(hr, "Failed to write update registration."); 801 ExitOnFailure(hr, "Failed to write update registration.");
798 } 802 }
799 803
800 // Update estimated size. 804 // Only set estimated size here for the first time.
801 qwEstimatedSize /= 1024; // Convert bytes to KB 805 // It will always get updated at the end of the session.
802 if (0 < qwEstimatedSize) 806 if (fCreated)
803 { 807 {
804 if (DWORD_MAX < qwEstimatedSize) 808 hr = UpdateEstimatedSize(hkRegistration, qwEstimatedSize);
805 { 809 ExitOnFailure(hr, "Failed to update estimated size.");
806 // ARP doesn't support QWORDs here
807 dwSize = DWORD_MAX;
808 }
809 else
810 {
811 dwSize = static_cast<DWORD>(qwEstimatedSize);
812 }
813
814 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize);
815 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE);
816 } 810 }
817 811
818 // Register the bundle dependency key. 812 // Register the bundle dependency key.
@@ -879,6 +873,7 @@ extern "C" HRESULT RegistrationSessionEnd(
879 __in BURN_PACKAGES* pPackages, 873 __in BURN_PACKAGES* pPackages,
880 __in BURN_RESUME_MODE resumeMode, 874 __in BURN_RESUME_MODE resumeMode,
881 __in BOOTSTRAPPER_APPLY_RESTART restart, 875 __in BOOTSTRAPPER_APPLY_RESTART restart,
876 __in DWORD64 qwEstimatedSize,
882 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType 877 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType
883 ) 878 )
884{ 879{
@@ -921,6 +916,9 @@ extern "C" HRESULT RegistrationSessionEnd(
921 // update display name 916 // update display name
922 hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType); 917 hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType);
923 ExitOnFailure(hr, "Failed to update name and publisher."); 918 ExitOnFailure(hr, "Failed to update name and publisher.");
919
920 hr = UpdateEstimatedSize(hkRegistration, qwEstimatedSize);
921 ExitOnFailure(hr, "Failed to update estimated size.");
924 } 922 }
925 923
926 // Update resume mode. 924 // Update resume mode.
@@ -1604,6 +1602,35 @@ LExit:
1604 return hr; 1602 return hr;
1605} 1603}
1606 1604
1605static HRESULT UpdateEstimatedSize(
1606 __in HKEY hkRegistration,
1607 __in DWORD64 qwEstimatedSize
1608 )
1609{
1610 HRESULT hr = S_OK;
1611 DWORD dwSize = 0;
1612
1613 qwEstimatedSize /= 1024; // Convert bytes to KB
1614 if (0 < qwEstimatedSize)
1615 {
1616 if (DWORD_MAX < qwEstimatedSize)
1617 {
1618 // ARP doesn't support QWORDs here
1619 dwSize = DWORD_MAX;
1620 }
1621 else
1622 {
1623 dwSize = static_cast<DWORD>(qwEstimatedSize);
1624 }
1625
1626 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize);
1627 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE);
1628 }
1629
1630LExit:
1631 return hr;
1632}
1633
1607static BOOL IsWuRebootPending() 1634static BOOL IsWuRebootPending()
1608{ 1635{
1609 HRESULT hr = S_OK; 1636 HRESULT hr = S_OK;
diff --git a/src/burn/engine/registration.h b/src/burn/engine/registration.h
index c07be962..cb5349a3 100644
--- a/src/burn/engine/registration.h
+++ b/src/burn/engine/registration.h
@@ -213,6 +213,7 @@ HRESULT RegistrationSessionEnd(
213 __in BURN_PACKAGES* pPackages, 213 __in BURN_PACKAGES* pPackages,
214 __in BURN_RESUME_MODE resumeMode, 214 __in BURN_RESUME_MODE resumeMode,
215 __in BOOTSTRAPPER_APPLY_RESTART restart, 215 __in BOOTSTRAPPER_APPLY_RESTART restart,
216 __in DWORD64 qwEstimatedSize,
216 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType 217 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType
217 ); 218 );
218HRESULT RegistrationSaveState( 219HRESULT RegistrationSaveState(
diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp
index f69606a0..be078c5c 100644
--- a/src/burn/test/BurnUnitTest/PlanTest.cpp
+++ b/src/burn/test/BurnUnitTest/PlanTest.cpp
@@ -106,7 +106,6 @@ namespace Bootstrapper
106 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); 106 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14);
107 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 107 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
108 108
109 Assert::Equal(107082ull, pPlan->qwEstimatedSize);
110 Assert::Equal(522548ull, pPlan->qwCacheSizeTotal); 109 Assert::Equal(522548ull, pPlan->qwCacheSizeTotal);
111 110
112 fRollback = FALSE; 111 fRollback = FALSE;
@@ -252,7 +251,6 @@ namespace Bootstrapper
252 dwIndex = 0; 251 dwIndex = 0;
253 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 252 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
254 253
255 Assert::Equal(0ull, pPlan->qwEstimatedSize);
256 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 254 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
257 255
258 fRollback = FALSE; 256 fRollback = FALSE;
@@ -392,7 +390,6 @@ namespace Bootstrapper
392 dwIndex = 0; 390 dwIndex = 0;
393 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 391 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
394 392
395 Assert::Equal(18575450ull, pPlan->qwEstimatedSize);
396 Assert::Equal(78462280ull, pPlan->qwCacheSizeTotal); 393 Assert::Equal(78462280ull, pPlan->qwCacheSizeTotal);
397 394
398 fRollback = FALSE; 395 fRollback = FALSE;
@@ -503,7 +500,6 @@ namespace Bootstrapper
503 dwIndex = 0; 500 dwIndex = 0;
504 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 501 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
505 502
506 Assert::Equal(18575450ull, pPlan->qwEstimatedSize);
507 Assert::Equal(52254105ull, pPlan->qwCacheSizeTotal); 503 Assert::Equal(52254105ull, pPlan->qwCacheSizeTotal);
508 504
509 fRollback = FALSE; 505 fRollback = FALSE;
@@ -595,7 +591,6 @@ namespace Bootstrapper
595 dwIndex = 0; 591 dwIndex = 0;
596 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 592 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
597 593
598 Assert::Equal(0ull, pPlan->qwEstimatedSize);
599 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 594 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
600 595
601 fRollback = FALSE; 596 fRollback = FALSE;
@@ -703,7 +698,6 @@ namespace Bootstrapper
703 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); 698 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
704 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 699 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
705 700
706 Assert::Equal(35694ull, pPlan->qwEstimatedSize);
707 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); 701 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal);
708 702
709 fRollback = FALSE; 703 fRollback = FALSE;
@@ -823,7 +817,6 @@ namespace Bootstrapper
823 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); 817 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
824 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 818 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
825 819
826 Assert::Equal(35694ull, pPlan->qwEstimatedSize);
827 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); 820 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal);
828 821
829 fRollback = FALSE; 822 fRollback = FALSE;
@@ -924,7 +917,6 @@ namespace Bootstrapper
924 dwIndex = 0; 917 dwIndex = 0;
925 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 918 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
926 919
927 Assert::Equal(1463267ull, pPlan->qwEstimatedSize);
928 Assert::Equal(119695ull, pPlan->qwCacheSizeTotal); 920 Assert::Equal(119695ull, pPlan->qwCacheSizeTotal);
929 921
930 fRollback = FALSE; 922 fRollback = FALSE;
@@ -1021,7 +1013,6 @@ namespace Bootstrapper
1021 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); 1013 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
1022 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1014 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1023 1015
1024 Assert::Equal(33743ull, pPlan->qwEstimatedSize);
1025 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); 1016 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal);
1026 1017
1027 fRollback = FALSE; 1018 fRollback = FALSE;
@@ -1104,7 +1095,6 @@ namespace Bootstrapper
1104 dwIndex = 0; 1095 dwIndex = 0;
1105 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1096 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1106 1097
1107 Assert::Equal(0ull, pPlan->qwEstimatedSize);
1108 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 1098 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
1109 1099
1110 fRollback = FALSE; 1100 fRollback = FALSE;
@@ -1180,7 +1170,6 @@ namespace Bootstrapper
1180 dwIndex = 0; 1170 dwIndex = 0;
1181 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1171 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1182 1172
1183 Assert::Equal(0ull, pPlan->qwEstimatedSize);
1184 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 1173 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
1185 1174
1186 fRollback = FALSE; 1175 fRollback = FALSE;
@@ -1273,7 +1262,6 @@ namespace Bootstrapper
1273 dwIndex = 0; 1262 dwIndex = 0;
1274 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1263 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1275 1264
1276 Assert::Equal(35694ull, pPlan->qwEstimatedSize);
1277 Assert::Equal(175674ull, pPlan->qwCacheSizeTotal); 1265 Assert::Equal(175674ull, pPlan->qwCacheSizeTotal);
1278 1266
1279 fRollback = FALSE; 1267 fRollback = FALSE;
@@ -1371,7 +1359,6 @@ namespace Bootstrapper
1371 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); 1359 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
1372 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1360 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1373 1361
1374 Assert::Equal(35694ull, pPlan->qwEstimatedSize);
1375 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); 1362 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal);
1376 1363
1377 fRollback = FALSE; 1364 fRollback = FALSE;
@@ -1473,7 +1460,6 @@ namespace Bootstrapper
1473 dwIndex = 0; 1460 dwIndex = 0;
1474 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1461 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1475 1462
1476 Assert::Equal(0ull, pPlan->qwEstimatedSize);
1477 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 1463 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
1478 1464
1479 fRollback = FALSE; 1465 fRollback = FALSE;
@@ -1553,7 +1539,6 @@ namespace Bootstrapper
1553 dwIndex = 0; 1539 dwIndex = 0;
1554 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1540 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1555 1541
1556 Assert::Equal(0ull, pPlan->qwEstimatedSize);
1557 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 1542 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
1558 1543
1559 fRollback = FALSE; 1544 fRollback = FALSE;
@@ -1649,7 +1634,6 @@ namespace Bootstrapper
1649 dwIndex = 0; 1634 dwIndex = 0;
1650 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1635 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1651 1636
1652 Assert::Equal(0ull, pPlan->qwEstimatedSize);
1653 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 1637 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
1654 1638
1655 fRollback = FALSE; 1639 fRollback = FALSE;
@@ -1720,7 +1704,6 @@ namespace Bootstrapper
1720 dwIndex = 0; 1704 dwIndex = 0;
1721 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1705 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1722 1706
1723 Assert::Equal(0ull, pPlan->qwEstimatedSize);
1724 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 1707 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
1725 1708
1726 fRollback = FALSE; 1709 fRollback = FALSE;
@@ -1805,7 +1788,6 @@ namespace Bootstrapper
1805 dwIndex = 0; 1788 dwIndex = 0;
1806 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1789 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1807 1790
1808 Assert::Equal(0ull, pPlan->qwEstimatedSize);
1809 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 1791 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
1810 1792
1811 fRollback = FALSE; 1793 fRollback = FALSE;
@@ -1906,7 +1888,6 @@ namespace Bootstrapper
1906 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); 1888 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
1907 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1889 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1908 1890
1909 Assert::Equal(56ull, pPlan->qwEstimatedSize);
1910 Assert::Equal(140ull, pPlan->qwCacheSizeTotal); 1891 Assert::Equal(140ull, pPlan->qwCacheSizeTotal);
1911 1892
1912 fRollback = FALSE; 1893 fRollback = FALSE;
@@ -2005,7 +1986,6 @@ namespace Bootstrapper
2005 dwIndex = 0; 1986 dwIndex = 0;
2006 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 1987 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
2007 1988
2008 Assert::Equal(3055111ull, pPlan->qwEstimatedSize);
2009 Assert::Equal(6130592ull, pPlan->qwCacheSizeTotal); 1989 Assert::Equal(6130592ull, pPlan->qwCacheSizeTotal);
2010 1990
2011 fRollback = FALSE; 1991 fRollback = FALSE;
@@ -2128,7 +2108,6 @@ namespace Bootstrapper
2128 dwIndex = 0; 2108 dwIndex = 0;
2129 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 2109 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
2130 2110
2131 Assert::Equal(0ull, pPlan->qwEstimatedSize);
2132 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 2111 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
2133 2112
2134 fRollback = FALSE; 2113 fRollback = FALSE;
@@ -2245,7 +2224,6 @@ namespace Bootstrapper
2245 dwIndex = 0; 2224 dwIndex = 0;
2246 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 2225 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
2247 2226
2248 Assert::Equal(0ull, pPlan->qwEstimatedSize);
2249 Assert::Equal(0ull, pPlan->qwCacheSizeTotal); 2227 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
2250 2228
2251 fRollback = FALSE; 2229 fRollback = FALSE;
@@ -2351,7 +2329,6 @@ namespace Bootstrapper
2351 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 4); 2329 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 4);
2352 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); 2330 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
2353 2331
2354 Assert::Equal(2993671ull, pPlan->qwEstimatedSize);
2355 Assert::Equal(6048672ull, pPlan->qwCacheSizeTotal); 2332 Assert::Equal(6048672ull, pPlan->qwCacheSizeTotal);
2356 2333
2357 fRollback = FALSE; 2334 fRollback = FALSE;
@@ -2451,6 +2428,8 @@ namespace Bootstrapper
2451 ReleaseStr(sczFilePath); 2428 ReleaseStr(sczFilePath);
2452 } 2429 }
2453 2430
2431 pEngineState->section.qwBundleSize = 1234;
2432
2454 hr = CoreInitializeConstants(pEngineState); 2433 hr = CoreInitializeConstants(pEngineState);
2455 NativeAssert::Succeeded(hr, "Failed to initialize core constants"); 2434 NativeAssert::Succeeded(hr, "Failed to initialize core constants");
2456 2435
diff --git a/src/burn/test/BurnUnitTest/RegistrationTest.cpp b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
index f01d92a4..0075e937 100644
--- a/src/burn/test/BurnUnitTest/RegistrationTest.cpp
+++ b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
@@ -60,6 +60,7 @@ namespace Bootstrapper
60 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID)); 60 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
61 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe")); 61 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe"));
62 DWORD dwRegistrationOptions = BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; 62 DWORD dwRegistrationOptions = BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
63 DWORD64 qwEstimatedSize = 1024;
63 64
64 try 65 try
65 { 66 {
@@ -103,7 +104,7 @@ namespace Bootstrapper
103 TestThrowOnFailure(hr, L"Failed to get current process path."); 104 TestThrowOnFailure(hr, L"Failed to get current process path.");
104 105
105 // write registration 106 // write registration
106 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 107 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
107 TestThrowOnFailure(hr, L"Failed to register bundle."); 108 TestThrowOnFailure(hr, L"Failed to register bundle.");
108 109
109 // verify that registration was created 110 // verify that registration was created
@@ -114,7 +115,7 @@ namespace Bootstrapper
114 this->ValidateRunOnceKeyEntry(cacheExePath); 115 this->ValidateRunOnceKeyEntry(cacheExePath);
115 116
116 // end session 117 // end session
117 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE); 118 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
118 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 119 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
119 120
120 // verify that registration was removed 121 // verify that registration was removed
@@ -158,6 +159,7 @@ namespace Bootstrapper
158 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID)); 159 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
159 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe")); 160 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe"));
160 DWORD dwRegistrationOptions = 0; 161 DWORD dwRegistrationOptions = 0;
162 DWORD64 qwEstimatedSize = 1024;
161 try 163 try
162 { 164 {
163 this->testRegistry->SetUp(); 165 this->testRegistry->SetUp();
@@ -204,7 +206,7 @@ namespace Bootstrapper
204 // 206 //
205 207
206 // write registration 208 // write registration
207 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 209 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
208 TestThrowOnFailure(hr, L"Failed to register bundle."); 210 TestThrowOnFailure(hr, L"Failed to register bundle.");
209 211
210 // verify that registration was created 212 // verify that registration was created
@@ -213,7 +215,7 @@ namespace Bootstrapper
213 this->ValidateRunOnceKeyEntry(cacheExePath); 215 this->ValidateRunOnceKeyEntry(cacheExePath);
214 216
215 // complete registration 217 // complete registration
216 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 218 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
217 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 219 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
218 220
219 // verify that registration was updated 221 // verify that registration was updated
@@ -226,7 +228,7 @@ namespace Bootstrapper
226 // 228 //
227 229
228 // write registration 230 // write registration
229 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 231 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
230 TestThrowOnFailure(hr, L"Failed to register bundle."); 232 TestThrowOnFailure(hr, L"Failed to register bundle.");
231 233
232 // verify that registration was updated 234 // verify that registration was updated
@@ -235,7 +237,7 @@ namespace Bootstrapper
235 this->ValidateRunOnceKeyEntry(cacheExePath); 237 this->ValidateRunOnceKeyEntry(cacheExePath);
236 238
237 // delete registration 239 // delete registration
238 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE); 240 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
239 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 241 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
240 242
241 // verify that registration was removed 243 // verify that registration was removed
@@ -278,6 +280,7 @@ namespace Bootstrapper
278 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID)); 280 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
279 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe")); 281 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe"));
280 DWORD dwRegistrationOptions = 0; 282 DWORD dwRegistrationOptions = 0;
283 DWORD64 qwEstimatedSize = 1024;
281 try 284 try
282 { 285 {
283 this->testRegistry->SetUp(); 286 this->testRegistry->SetUp();
@@ -327,7 +330,7 @@ namespace Bootstrapper
327 // 330 //
328 331
329 // write registration 332 // write registration
330 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 333 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
331 TestThrowOnFailure(hr, L"Failed to register bundle."); 334 TestThrowOnFailure(hr, L"Failed to register bundle.");
332 335
333 // verify that registration was created 336 // verify that registration was created
@@ -335,7 +338,7 @@ namespace Bootstrapper
335 this->ValidateRunOnceKeyEntry(cacheExePath); 338 this->ValidateRunOnceKeyEntry(cacheExePath);
336 339
337 // complete registration 340 // complete registration
338 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BOOTSTRAPPER_REGISTRATION_TYPE_FULL); 341 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_FULL);
339 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 342 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
340 343
341 // verify that registration variables were updated 344 // verify that registration variables were updated
@@ -355,7 +358,7 @@ namespace Bootstrapper
355 // 358 //
356 359
357 // delete registration 360 // delete registration
358 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE); 361 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
359 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 362 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
360 363
361 // verify that registration was removed 364 // verify that registration was removed
@@ -398,6 +401,7 @@ namespace Bootstrapper
398 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID)); 401 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
399 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe")); 402 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe"));
400 DWORD dwRegistrationOptions = 0; 403 DWORD dwRegistrationOptions = 0;
404 DWORD64 qwEstimatedSize = 1024;
401 try 405 try
402 { 406 {
403 this->testRegistry->SetUp(); 407 this->testRegistry->SetUp();
@@ -446,7 +450,7 @@ namespace Bootstrapper
446 // 450 //
447 451
448 // write registration 452 // write registration
449 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 453 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
450 TestThrowOnFailure(hr, L"Failed to register bundle."); 454 TestThrowOnFailure(hr, L"Failed to register bundle.");
451 455
452 // verify that registration was created 456 // verify that registration was created
@@ -454,7 +458,7 @@ namespace Bootstrapper
454 this->ValidateRunOnceKeyEntry(cacheExePath); 458 this->ValidateRunOnceKeyEntry(cacheExePath);
455 459
456 // finish registration 460 // finish registration
457 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_FULL); 461 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_FULL);
458 TestThrowOnFailure(hr, L"Failed to register bundle."); 462 TestThrowOnFailure(hr, L"Failed to register bundle.");
459 463
460 // verify that registration was updated 464 // verify that registration was updated
@@ -479,7 +483,7 @@ namespace Bootstrapper
479 // 483 //
480 484
481 // write registration 485 // write registration
482 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 486 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
483 TestThrowOnFailure(hr, L"Failed to register bundle."); 487 TestThrowOnFailure(hr, L"Failed to register bundle.");
484 488
485 // verify that registration was updated 489 // verify that registration was updated
@@ -487,7 +491,7 @@ namespace Bootstrapper
487 this->ValidateRunOnceKeyEntry(cacheExePath); 491 this->ValidateRunOnceKeyEntry(cacheExePath);
488 492
489 // delete registration 493 // delete registration
490 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE); 494 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
491 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 495 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
492 496
493 // verify that registration was removed 497 // verify that registration was removed
@@ -533,6 +537,7 @@ namespace Bootstrapper
533 BYTE* pbBuffer = NULL; 537 BYTE* pbBuffer = NULL;
534 SIZE_T cbBuffer = 0; 538 SIZE_T cbBuffer = 0;
535 DWORD dwRegistrationOptions = 0; 539 DWORD dwRegistrationOptions = 0;
540 DWORD64 qwEstimatedSize = 1024;
536 541
537 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID)); 542 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
538 try 543 try
@@ -586,7 +591,7 @@ namespace Bootstrapper
586 TestThrowOnFailure(hr, L"Failed to get current process path."); 591 TestThrowOnFailure(hr, L"Failed to get current process path.");
587 592
588 // begin session 593 // begin session
589 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 594 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
590 TestThrowOnFailure(hr, L"Failed to register bundle."); 595 TestThrowOnFailure(hr, L"Failed to register bundle.");
591 596
592 VariableSetNumericHelper(&variables, L"MyBurnVariable1", 42); 597 VariableSetNumericHelper(&variables, L"MyBurnVariable1", 42);
@@ -629,7 +634,7 @@ namespace Bootstrapper
629 NativeAssert::StringEqual(L"42", sczValue); 634 NativeAssert::StringEqual(L"42", sczValue);
630 635
631 // end session 636 // end session
632 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE); 637 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
633 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 638 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
634 } 639 }
635 finally 640 finally
@@ -673,6 +678,7 @@ namespace Bootstrapper
673 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID)); 678 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
674 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe")); 679 String^ cacheExePath = Path::Combine(cacheDirectory, gcnew String(L"setup.exe"));
675 DWORD dwRegistrationOptions = 0; 680 DWORD dwRegistrationOptions = 0;
681 DWORD64 qwEstimatedSize = 1024;
676 try 682 try
677 { 683 {
678 this->testRegistry->SetUp(); 684 this->testRegistry->SetUp();
@@ -728,7 +734,7 @@ namespace Bootstrapper
728 Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); 734 Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType);
729 735
730 // begin session 736 // begin session
731 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 737 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &cache, &variables, dwRegistrationOptions, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
732 TestThrowOnFailure(hr, L"Failed to register bundle."); 738 TestThrowOnFailure(hr, L"Failed to register bundle.");
733 739
734 VariableSetNumericHelper(&variables, L"MyBurnVariable1", 42); 740 VariableSetNumericHelper(&variables, L"MyBurnVariable1", 42);
@@ -767,7 +773,7 @@ namespace Bootstrapper
767 Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType); 773 Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType);
768 774
769 // suspend session 775 // suspend session
770 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 776 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
771 TestThrowOnFailure(hr, L"Failed to suspend session."); 777 TestThrowOnFailure(hr, L"Failed to suspend session.");
772 778
773 // verify that run key was removed 779 // verify that run key was removed
@@ -794,7 +800,7 @@ namespace Bootstrapper
794 this->ValidateRunOnceKeyEntry(cacheExePath); 800 this->ValidateRunOnceKeyEntry(cacheExePath);
795 801
796 // end session 802 // end session
797 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE); 803 hr = RegistrationSessionEnd(&registration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, qwEstimatedSize, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
798 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 804 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
799 805
800 // read resume type after session 806 // read resume type after session
diff --git a/src/libs/dutil/WixToolset.DUtil/regutil.cpp b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
index 219a6c11..78507ade 100644
--- a/src/libs/dutil/WixToolset.DUtil/regutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
@@ -107,10 +107,9 @@ DAPI_(HRESULT) RegCreate(
107 ) 107 )
108{ 108{
109 HRESULT hr = S_OK; 109 HRESULT hr = S_OK;
110 DWORD er = ERROR_SUCCESS;
111 110
112 er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL); 111 hr = RegCreateEx(hkRoot, wzSubKey, dwAccess, REG_KEY_DEFAULT, FALSE, NULL, phk, NULL);
113 RegExitOnWin32Error(er, hr, "Failed to create registry key."); 112 RegExitOnFailure(hr, "Failed to create registry key.");
114 113
115LExit: 114LExit:
116 return hr; 115 return hr;
diff --git a/src/test/burn/TestBA/TestBA.cs b/src/test/burn/TestBA/TestBA.cs
index fdbcc6d4..b4d74341 100644
--- a/src/test/burn/TestBA/TestBA.cs
+++ b/src/test/burn/TestBA/TestBA.cs
@@ -27,6 +27,7 @@ namespace WixToolset.Test.BA
27 27
28 private string updateBundlePath; 28 private string updateBundlePath;
29 29
30 private bool forceKeepRegistration;
30 private bool immediatelyQuit; 31 private bool immediatelyQuit;
31 private bool quitAfterDetect; 32 private bool quitAfterDetect;
32 private bool explicitlyElevateAndPlanFromOnElevateBegin; 33 private bool explicitlyElevateAndPlanFromOnElevateBegin;
@@ -131,6 +132,12 @@ namespace WixToolset.Test.BA
131 this.explicitlyElevateAndPlanFromOnElevateBegin = false; 132 this.explicitlyElevateAndPlanFromOnElevateBegin = false;
132 } 133 }
133 134
135 string forceKeepRegistration = this.ReadPackageAction(null, "ForceKeepRegistration");
136 if (String.IsNullOrEmpty(forceKeepRegistration) || !Boolean.TryParse(forceKeepRegistration, out this.forceKeepRegistration))
137 {
138 this.forceKeepRegistration = false;
139 }
140
134 string quitAfterDetect = this.ReadPackageAction(null, "QuitAfterDetect"); 141 string quitAfterDetect = this.ReadPackageAction(null, "QuitAfterDetect");
135 if (String.IsNullOrEmpty(quitAfterDetect) || !Boolean.TryParse(quitAfterDetect, out this.quitAfterDetect)) 142 if (String.IsNullOrEmpty(quitAfterDetect) || !Boolean.TryParse(quitAfterDetect, out this.quitAfterDetect))
136 { 143 {
@@ -533,6 +540,16 @@ namespace WixToolset.Test.BA
533 this.ShutdownUiThread(); 540 this.ShutdownUiThread();
534 } 541 }
535 542
543 protected override void OnUnregisterBegin(UnregisterBeginEventArgs args)
544 {
545 if (this.forceKeepRegistration && args.RegistrationType == RegistrationType.None)
546 {
547 args.RegistrationType = RegistrationType.InProgress;
548 }
549
550 this.Log("OnUnregisterBegin, default: {0}, requested: {1}", args.RecommendedRegistrationType, args.RegistrationType);
551 }
552
536 private void TestVariables() 553 private void TestVariables()
537 { 554 {
538 // First make sure we can check and get standard variables of each type. 555 // First make sure we can check and get standard variables of each type.
diff --git a/src/test/burn/WixTestTools/BundleVerifier.cs b/src/test/burn/WixTestTools/BundleVerifier.cs
index 56044c5f..594b19aa 100644
--- a/src/test/burn/WixTestTools/BundleVerifier.cs
+++ b/src/test/burn/WixTestTools/BundleVerifier.cs
@@ -87,7 +87,7 @@ namespace WixTestTools
87 } 87 }
88 } 88 }
89 89
90 public string VerifyRegisteredAndInPackageCache(int? expectedSystemComponent = null) 90 public BundleRegistration VerifyRegisteredAndInPackageCache(int? expectedSystemComponent = null)
91 { 91 {
92 Assert.True(this.TryGetRegistration(out var registration)); 92 Assert.True(this.TryGetRegistration(out var registration));
93 93
@@ -99,7 +99,7 @@ namespace WixTestTools
99 var expectedCachePath = this.GetExpectedCachedBundlePath(); 99 var expectedCachePath = this.GetExpectedCachedBundlePath();
100 WixAssert.StringEqual(expectedCachePath, registration.CachePath, true); 100 WixAssert.StringEqual(expectedCachePath, registration.CachePath, true);
101 101
102 return registration.CachePath; 102 return registration;
103 } 103 }
104 104
105 public void VerifyUnregisteredAndRemovedFromPackageCache() 105 public void VerifyUnregisteredAndRemovedFromPackageCache()
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/BasicFunctionalityTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/BasicFunctionalityTests.cs
index 89c5be9b..efe4f05b 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/BasicFunctionalityTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/BasicFunctionalityTests.cs
@@ -82,7 +82,8 @@ namespace WixToolsetTest.BurnE2E
82 82
83 bundle.Install(); 83 bundle.Install();
84 84
85 var cachedBundlePath = bundle.VerifyRegisteredAndInPackageCache(); 85 var registration = bundle.VerifyRegisteredAndInPackageCache();
86 var cachedBundlePath = registration.CachePath;
86 87
87 // Source file should be installed 88 // Source file should be installed
88 Assert.True(File.Exists(packageSourceCodeInstalled), $"Should have found {packageName} payload installed at: {packageSourceCodeInstalled}"); 89 Assert.True(File.Exists(packageSourceCodeInstalled), $"Should have found {packageName} payload installed at: {packageSourceCodeInstalled}");
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs
index 943939e3..4d5fb811 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/CacheTests.cs
@@ -162,14 +162,14 @@ namespace WixToolsetTest.BurnE2E
162 bundleA.Install(bundleACopiedPath); 162 bundleA.Install(bundleACopiedPath);
163 } 163 }
164 164
165 var bundlePackageCachePath = bundleA.VerifyRegisteredAndInPackageCache(); 165 var bundlePackageRegistration = bundleA.VerifyRegisteredAndInPackageCache();
166 166
167 packageA.VerifyInstalled(true); 167 packageA.VerifyInstalled(true);
168 packageB.VerifyInstalled(false); 168 packageB.VerifyInstalled(false);
169 169
170 testBAController.SetPackageRequestedState("PackageB", RequestState.Present); 170 testBAController.SetPackageRequestedState("PackageB", RequestState.Present);
171 171
172 var modifyLogPath = bundleA.Modify(bundlePackageCachePath); 172 var modifyLogPath = bundleA.Modify(bundlePackageRegistration.CachePath);
173 bundleA.VerifyRegisteredAndInPackageCache(); 173 bundleA.VerifyRegisteredAndInPackageCache();
174 174
175 packageA.VerifyInstalled(true); 175 packageA.VerifyInstalled(true);
@@ -204,14 +204,14 @@ namespace WixToolsetTest.BurnE2E
204 204
205 bundleB.Install(bundleBCopiedPath); 205 bundleB.Install(bundleBCopiedPath);
206 206
207 var bundlePackageCachePath = bundleB.VerifyRegisteredAndInPackageCache(); 207 var bundlePackageRegistration = bundleB.VerifyRegisteredAndInPackageCache();
208 208
209 packageA.VerifyInstalled(true); 209 packageA.VerifyInstalled(true);
210 packageB.VerifyInstalled(false); 210 packageB.VerifyInstalled(false);
211 211
212 testBAController.SetPackageRequestedState("PackageB", RequestState.Present); 212 testBAController.SetPackageRequestedState("PackageB", RequestState.Present);
213 213
214 bundleB.Modify(bundlePackageCachePath); 214 bundleB.Modify(bundlePackageRegistration.CachePath);
215 bundleB.VerifyRegisteredAndInPackageCache(); 215 bundleB.VerifyRegisteredAndInPackageCache();
216 216
217 packageA.VerifyInstalled(true); 217 packageA.VerifyInstalled(true);
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/MsiTransactionTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/MsiTransactionTests.cs
index 3d9748bb..cbfee806 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/MsiTransactionTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/MsiTransactionTests.cs
@@ -42,7 +42,7 @@ namespace WixToolsetTest.BurnE2E
42 42
43 bundleAv1.Install(); 43 bundleAv1.Install();
44 44
45 var bundleAv1CachedPath = bundleAv1.VerifyRegisteredAndInPackageCache(); 45 var bundleAv1Registration = bundleAv1.VerifyRegisteredAndInPackageCache();
46 46
47 // Source file should be installed 47 // Source file should be installed
48 Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled)); 48 Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
@@ -51,7 +51,7 @@ namespace WixToolsetTest.BurnE2E
51 51
52 bundleAv2.Install(); 52 bundleAv2.Install();
53 53
54 var bundleAv2CachedPath = bundleAv2.VerifyRegisteredAndInPackageCache(); 54 var bundleAv2Registration = bundleAv2.VerifyRegisteredAndInPackageCache();
55 55
56 // Source file should be upgraded 56 // Source file should be upgraded
57 Assert.True(File.Exists(packageDSourceCodeInstalled), String.Concat("Should have found Package D payload installed at: ", packageDSourceCodeInstalled)); 57 Assert.True(File.Exists(packageDSourceCodeInstalled), String.Concat("Should have found Package D payload installed at: ", packageDSourceCodeInstalled));
@@ -61,7 +61,7 @@ namespace WixToolsetTest.BurnE2E
61 Assert.False(File.Exists(packageBv1SourceCodeInstalled), String.Concat("Package Bv1 payload should have been removed by upgrade uninstall from: ", packageBv1SourceCodeInstalled)); 61 Assert.False(File.Exists(packageBv1SourceCodeInstalled), String.Concat("Package Bv1 payload should have been removed by upgrade uninstall from: ", packageBv1SourceCodeInstalled));
62 Assert.False(File.Exists(packageASourceCodeInstalled), String.Concat("Package A payload should have been removed by upgrade uninstall from: ", packageASourceCodeInstalled)); 62 Assert.False(File.Exists(packageASourceCodeInstalled), String.Concat("Package A payload should have been removed by upgrade uninstall from: ", packageASourceCodeInstalled));
63 63
64 bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache(bundleAv1CachedPath); 64 bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache(bundleAv1Registration.CachePath);
65 65
66 // Uninstall everything. 66 // Uninstall everything.
67 bundleAv2.Uninstall(); 67 bundleAv2.Uninstall();
@@ -71,7 +71,7 @@ namespace WixToolsetTest.BurnE2E
71 Assert.False(File.Exists(packageBv2SourceCodeInstalled), String.Concat("Package Bv2 payload should have been removed by uninstall from: ", packageBv2SourceCodeInstalled)); 71 Assert.False(File.Exists(packageBv2SourceCodeInstalled), String.Concat("Package Bv2 payload should have been removed by uninstall from: ", packageBv2SourceCodeInstalled));
72 Assert.False(File.Exists(packageCv2SourceCodeInstalled), String.Concat("Package Cv2 payload should have been removed by uninstall from: ", packageCv2SourceCodeInstalled)); 72 Assert.False(File.Exists(packageCv2SourceCodeInstalled), String.Concat("Package Cv2 payload should have been removed by uninstall from: ", packageCv2SourceCodeInstalled));
73 73
74 bundleAv2.VerifyUnregisteredAndRemovedFromPackageCache(bundleAv2CachedPath); 74 bundleAv2.VerifyUnregisteredAndRemovedFromPackageCache(bundleAv2Registration.CachePath);
75 } 75 }
76 76
77 /// <summary> 77 /// <summary>
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/RegistrationTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/RegistrationTests.cs
index 51122c28..01ffa942 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/RegistrationTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/RegistrationTests.cs
@@ -3,6 +3,7 @@
3namespace WixToolsetTest.BurnE2E 3namespace WixToolsetTest.BurnE2E
4{ 4{
5 using System; 5 using System;
6 using WixToolset.Mba.Core;
6 using Xunit; 7 using Xunit;
7 using Xunit.Abstractions; 8 using Xunit.Abstractions;
8 9
@@ -11,8 +12,35 @@ namespace WixToolsetTest.BurnE2E
11 public RegistrationTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } 12 public RegistrationTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
12 13
13 [Fact] 14 [Fact]
15 public void AllowsBAToKeepRegistration()
16 {
17 this.CreatePackageInstaller("PackageA");
18 var bundleA = this.CreateBundleInstaller("BundleA");
19 var testBAController = this.CreateTestBAController();
20
21 testBAController.SetPackageRequestedState("PackageA", RequestState.Absent);
22 testBAController.SetForceKeepRegistration();
23
24 bundleA.Install();
25 var initialRegistration = bundleA.VerifyRegisteredAndInPackageCache();
26
27 Assert.NotNull(initialRegistration.EstimatedSize);
28
29 testBAController.SetForceKeepRegistration(null);
30 testBAController.ResetPackageStates("PackageA");
31
32 bundleA.Install();
33 var finalRegistration = bundleA.VerifyRegisteredAndInPackageCache();
34
35 // Verifies https://github.com/wixtoolset/issues/issues/4039
36 Assert.NotNull(finalRegistration.EstimatedSize);
37 Assert.InRange(finalRegistration.EstimatedSize.Value, initialRegistration.EstimatedSize.Value + 1, Int32.MaxValue);
38 }
39
40 [Fact]
14 public void AutomaticallyUncachesBundleWhenNotInstalled() 41 public void AutomaticallyUncachesBundleWhenNotInstalled()
15 { 42 {
43 this.CreatePackageInstaller("PackageA");
16 var bundleA = this.CreateBundleInstaller("BundleA"); 44 var bundleA = this.CreateBundleInstaller("BundleA");
17 var testBAController = this.CreateTestBAController(); 45 var testBAController = this.CreateTestBAController();
18 46
@@ -40,6 +68,7 @@ namespace WixToolsetTest.BurnE2E
40 [Fact] 68 [Fact]
41 public void RegistersInARPIfPrecached() 69 public void RegistersInARPIfPrecached()
42 { 70 {
71 this.CreatePackageInstaller("PackageA");
43 var bundleA = this.CreateBundleInstaller("BundleA"); 72 var bundleA = this.CreateBundleInstaller("BundleA");
44 73
45 bundleA.ManuallyCache(); 74 bundleA.ManuallyCache();
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs b/src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs
index fa553919..8e6611a2 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs
@@ -47,6 +47,11 @@ namespace WixToolsetTest.BurnE2E
47 this.SetBurnTestValue("ExplicitlyElevateAndPlanFromOnElevateBegin", value); 47 this.SetBurnTestValue("ExplicitlyElevateAndPlanFromOnElevateBegin", value);
48 } 48 }
49 49
50 public void SetForceKeepRegistration(string value = "true")
51 {
52 this.SetBurnTestValue("ForceKeepRegistration", value);
53 }
54
50 public void SetImmediatelyQuit(string value = "true") 55 public void SetImmediatelyQuit(string value = "true")
51 { 56 {
52 this.SetBurnTestValue("ImmediatelyQuit", value); 57 this.SetBurnTestValue("ImmediatelyQuit", value);