aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h8
-rw-r--r--src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs4
-rw-r--r--src/api/burn/WixToolset.Mba.Core/EventArgs.cs8
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs26
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctions.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h2
-rw-r--r--src/api/burn/balutil/inc/IBootstrapperApplication.h22
-rw-r--r--src/burn/engine/apply.cpp44
-rw-r--r--src/burn/engine/apply.h4
-rw-r--r--src/burn/engine/detect.cpp4
-rw-r--r--src/burn/engine/elevation.cpp36
-rw-r--r--src/burn/engine/embedded.cpp4
-rw-r--r--src/burn/engine/exeengine.cpp2
-rw-r--r--src/burn/engine/msuengine.cpp2
-rw-r--r--src/burn/engine/netfxchainer.cpp8
-rw-r--r--src/burn/engine/userexperience.cpp56
-rw-r--r--src/burn/engine/userexperience.h6
-rw-r--r--src/internal/SetBuildNumber/Directory.Packages.props.pp1
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/wiutil.h4
-rw-r--r--src/libs/dutil/WixToolset.DUtil/wiutil.cpp16
-rw-r--r--src/test/burn/TestBA/TestBA.cs2
-rw-r--r--src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wixproj18
-rw-r--r--src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wxs10
-rw-r--r--src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wixproj13
-rw-r--r--src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wxs10
-rw-r--r--src/test/burn/TestData/FilesInUseTests/PackageA/license.txt1
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/FilesInUseTests.cs38
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs10
29 files changed, 277 insertions, 84 deletions
diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
index e5f924c8..c2641b5f 100644
--- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
@@ -38,6 +38,13 @@ enum BOOTSTRAPPER_ERROR_TYPE
38 BOOTSTRAPPER_ERROR_TYPE_APPLY, // error occurred during apply. 38 BOOTSTRAPPER_ERROR_TYPE_APPLY, // error occurred during apply.
39}; 39};
40 40
41enum BOOTSTRAPPER_FILES_IN_USE_TYPE
42{
43 BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI, // INSTALLMESSAGE_FILESINUSE
44 BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM, // INSTALLMESSAGE_RMFILESINUSE
45 BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX, // MMIO_CLOSE_APPS
46};
47
41enum BOOTSTRAPPER_RELATED_OPERATION 48enum BOOTSTRAPPER_RELATED_OPERATION
42{ 49{
43 BOOTSTRAPPER_RELATED_OPERATION_NONE, 50 BOOTSTRAPPER_RELATED_OPERATION_NONE,
@@ -874,6 +881,7 @@ struct BA_ONEXECUTEFILESINUSE_ARGS
874 DWORD cFiles; 881 DWORD cFiles;
875 LPCWSTR* rgwzFiles; 882 LPCWSTR* rgwzFiles;
876 int nRecommendation; 883 int nRecommendation;
884 BOOTSTRAPPER_FILES_IN_USE_TYPE source;
877}; 885};
878 886
879struct BA_ONEXECUTEFILESINUSE_RESULTS 887struct BA_ONEXECUTEFILESINUSE_RESULTS
diff --git a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
index a78bf43f..b1fcaea4 100644
--- a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
@@ -1712,9 +1712,9 @@ namespace WixToolset.Mba.Core
1712 return args.HResult; 1712 return args.HResult;
1713 } 1713 }
1714 1714
1715 int IBootstrapperApplication.OnExecuteFilesInUse(string wzPackageId, int cFiles, string[] rgwzFiles, Result nRecommendation, ref Result pResult) 1715 int IBootstrapperApplication.OnExecuteFilesInUse(string wzPackageId, int cFiles, string[] rgwzFiles, Result nRecommendation, FilesInUseType source, ref Result pResult)
1716 { 1716 {
1717 ExecuteFilesInUseEventArgs args = new ExecuteFilesInUseEventArgs(wzPackageId, rgwzFiles, nRecommendation, pResult); 1717 ExecuteFilesInUseEventArgs args = new ExecuteFilesInUseEventArgs(wzPackageId, rgwzFiles, nRecommendation, source, pResult);
1718 this.OnExecuteFilesInUse(args); 1718 this.OnExecuteFilesInUse(args);
1719 1719
1720 pResult = args.Result; 1720 pResult = args.Result;
diff --git a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
index 55c9e74c..65169b25 100644
--- a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+++ b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
@@ -1613,11 +1613,12 @@ namespace WixToolset.Mba.Core
1613 public class ExecuteFilesInUseEventArgs : ResultEventArgs 1613 public class ExecuteFilesInUseEventArgs : ResultEventArgs
1614 { 1614 {
1615 /// <summary /> 1615 /// <summary />
1616 public ExecuteFilesInUseEventArgs(string packageId, string[] files, Result recommendation, Result result) 1616 public ExecuteFilesInUseEventArgs(string packageId, string[] files, Result recommendation, FilesInUseType source, Result result)
1617 : base(recommendation, result) 1617 : base(recommendation, result)
1618 { 1618 {
1619 this.PackageId = packageId; 1619 this.PackageId = packageId;
1620 this.Files = new ReadOnlyCollection<string>(files ?? new string[] { }); 1620 this.Files = new ReadOnlyCollection<string>(files ?? new string[] { });
1621 this.Source = source;
1621 } 1622 }
1622 1623
1623 /// <summary> 1624 /// <summary>
@@ -1629,6 +1630,11 @@ namespace WixToolset.Mba.Core
1629 /// Gets the list of files in use. 1630 /// Gets the list of files in use.
1630 /// </summary> 1631 /// </summary>
1631 public IList<string> Files { get; private set; } 1632 public IList<string> Files { get; private set; }
1633
1634 /// <summary>
1635 /// Gets the source of the message.
1636 /// </summary>
1637 public FilesInUseType Source { get; private set; }
1632 } 1638 }
1633 1639
1634 /// <summary> 1640 /// <summary>
diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
index c6a8fc14..3df54bde 100644
--- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
@@ -777,12 +777,6 @@ namespace WixToolset.Mba.Core
777 /// <summary> 777 /// <summary>
778 /// See <see cref="IDefaultBootstrapperApplication.ExecuteFilesInUse"/>. 778 /// See <see cref="IDefaultBootstrapperApplication.ExecuteFilesInUse"/>.
779 /// </summary> 779 /// </summary>
780 /// <param name="wzPackageId"></param>
781 /// <param name="cFiles"></param>
782 /// <param name="rgwzFiles"></param>
783 /// <param name="nRecommendation"></param>
784 /// <param name="pResult"></param>
785 /// <returns></returns>
786 [PreserveSig] 780 [PreserveSig]
787 [return: MarshalAs(UnmanagedType.I4)] 781 [return: MarshalAs(UnmanagedType.I4)]
788 int OnExecuteFilesInUse( 782 int OnExecuteFilesInUse(
@@ -790,6 +784,7 @@ namespace WixToolset.Mba.Core
790 [MarshalAs(UnmanagedType.U4)] int cFiles, 784 [MarshalAs(UnmanagedType.U4)] int cFiles,
791 [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.LPWStr), In] string[] rgwzFiles, 785 [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.LPWStr), In] string[] rgwzFiles,
792 [MarshalAs(UnmanagedType.I4)] Result nRecommendation, 786 [MarshalAs(UnmanagedType.I4)] Result nRecommendation,
787 [MarshalAs(UnmanagedType.I4)] FilesInUseType source,
793 [MarshalAs(UnmanagedType.I4)] ref Result pResult 788 [MarshalAs(UnmanagedType.I4)] ref Result pResult
794 ); 789 );
795 790
@@ -1446,6 +1441,25 @@ namespace WixToolset.Mba.Core
1446 }; 1441 };
1447 1442
1448 /// <summary> 1443 /// <summary>
1444 /// Indicates the source of the FilesInUse message.
1445 /// </summary>
1446 public enum FilesInUseType
1447 {
1448 /// <summary>
1449 /// Generated from INSTALLMESSAGE_FILESINUSE.
1450 /// </summary>
1451 Msi,
1452 /// <summary>
1453 /// Generated from INSTALLMESSAGE_RMFILESINUSE.
1454 /// </summary>
1455 MsiRm,
1456 /// <summary>
1457 /// Generated from MMIO_CLOSE_APPS.
1458 /// </summary>
1459 Netfx,
1460 }
1461
1462 /// <summary>
1449 /// The calculated operation for the related bundle. 1463 /// The calculated operation for the related bundle.
1450 /// </summary> 1464 /// </summary>
1451 public enum RelatedOperation 1465 public enum RelatedOperation
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
index 8d9bddca..7f52f76e 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
@@ -583,6 +583,7 @@ public: // IBootstrapperApplication
583 __in DWORD /*cFiles*/, 583 __in DWORD /*cFiles*/,
584 __in_ecount_z(cFiles) LPCWSTR* /*rgwzFiles*/, 584 __in_ecount_z(cFiles) LPCWSTR* /*rgwzFiles*/,
585 __in int /*nRecommendation*/, 585 __in int /*nRecommendation*/,
586 __in BOOTSTRAPPER_FILES_IN_USE_TYPE /*source*/,
586 __inout int* /*pResult*/ 587 __inout int* /*pResult*/
587 ) 588 )
588 { 589 {
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
index 4d043dfe..5665fee3 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
@@ -738,6 +738,7 @@ public: // IBootstrapperApplication
738 __in DWORD /*cFiles*/, 738 __in DWORD /*cFiles*/,
739 __in_ecount_z(cFiles) LPCWSTR* /*rgwzFiles*/, 739 __in_ecount_z(cFiles) LPCWSTR* /*rgwzFiles*/,
740 __in int /*nRecommendation*/, 740 __in int /*nRecommendation*/,
741 __in BOOTSTRAPPER_FILES_IN_USE_TYPE /*source*/,
741 __inout int* pResult 742 __inout int* pResult
742 ) 743 )
743 { 744 {
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
index 59bfc1f8..2292cd64 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
@@ -438,7 +438,7 @@ static HRESULT BalBaseBAProcOnExecuteFilesInUse(
438 __inout BA_ONEXECUTEFILESINUSE_RESULTS* pResults 438 __inout BA_ONEXECUTEFILESINUSE_RESULTS* pResults
439 ) 439 )
440{ 440{
441 return pBA->OnExecuteFilesInUse(pArgs->wzPackageId, pArgs->cFiles, pArgs->rgwzFiles, pArgs->nRecommendation, &pResults->nResult); 441 return pBA->OnExecuteFilesInUse(pArgs->wzPackageId, pArgs->cFiles, pArgs->rgwzFiles, pArgs->nRecommendation, pArgs->source, &pResults->nResult);
442} 442}
443 443
444static HRESULT BalBaseBAProcOnExecutePackageComplete( 444static HRESULT BalBaseBAProcOnExecutePackageComplete(
diff --git a/src/api/burn/balutil/inc/IBootstrapperApplication.h b/src/api/burn/balutil/inc/IBootstrapperApplication.h
index be9b7b6e..2ba1f503 100644
--- a/src/api/burn/balutil/inc/IBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/IBootstrapperApplication.h
@@ -488,27 +488,19 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
488 __inout int* pResult 488 __inout int* pResult
489 ) = 0; 489 ) = 0;
490 490
491 // OnExecuteFilesInUse - called when the engine encounters files in use while 491 // OnExecuteFilesInUse - called when the engine receives a files in use message
492 // executing a package. 492 // while executing a package.
493 // 493 //
494 // Return: 494 // Return value depends on the source:
495 // IDOK instructs the engine to let the Restart Manager attempt to close the 495 // BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI: https://docs.microsoft.com/en-us/windows/win32/msi/installvalidate-action
496 // applications to avoid a restart. 496 // BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM: https://docs.microsoft.com/en-us/windows/win32/msi/using-restart-manager-with-an-external-ui-
497 // 497 // BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX: https://docs.microsoft.com/en-us/dotnet/framework/deployment/how-to-get-progress-from-the-dotnet-installer
498 // IDCANCEL instructs the engine to abort the execution and start rollback.
499 //
500 // IDIGNORE instructs the engine to ignore the running applications. A restart will be
501 // required.
502 //
503 // IDRETRY instructs the engine to check if the applications are still running again.
504 //
505 // IDNOACTION is equivalent to ignoring the running applications. A restart will be
506 // required.
507 STDMETHOD(OnExecuteFilesInUse)( 498 STDMETHOD(OnExecuteFilesInUse)(
508 __in_z LPCWSTR wzPackageId, 499 __in_z LPCWSTR wzPackageId,
509 __in DWORD cFiles, 500 __in DWORD cFiles,
510 __in_ecount_z(cFiles) LPCWSTR* rgwzFiles, 501 __in_ecount_z(cFiles) LPCWSTR* rgwzFiles,
511 __in int nRecommendation, 502 __in int nRecommendation,
503 __in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
512 __inout int* pResult 504 __inout int* pResult
513 ) = 0; 505 ) = 0;
514 506
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
index dfaba3f2..9e552ee0 100644
--- a/src/burn/engine/apply.cpp
+++ b/src/burn/engine/apply.cpp
@@ -1931,8 +1931,8 @@ static HRESULT WINAPI AuthenticationRequired(
1931 1931
1932 APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData); 1932 APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData);
1933 1933
1934 UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value; 1934 UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYCANCEL, 0, NULL, &nResult); // ignore return value;
1935 nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); 1935 nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, BURN_MB_RETRYTRYAGAIN, nResult);
1936 if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply) 1936 if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply)
1937 { 1937 {
1938 er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); 1938 er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
@@ -2495,10 +2495,10 @@ static HRESULT ExecuteExePackage(
2495 ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); 2495 ExitOnRootFailure(hr, "BA aborted execute EXE package begin.");
2496 2496
2497 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; 2497 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2498 message.dwAllowedResults = MB_OKCANCEL; 2498 message.dwUIHint = MB_OKCANCEL;
2499 message.progress.dwPercentage = fRollback ? 100 : 0; 2499 message.progress.dwPercentage = fRollback ? 100 : 0;
2500 nResult = GenericExecuteMessageHandler(&message, pContext); 2500 nResult = GenericExecuteMessageHandler(&message, pContext);
2501 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); 2501 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2502 ExitOnRootFailure(hr, "BA aborted EXE progress."); 2502 ExitOnRootFailure(hr, "BA aborted EXE progress.");
2503 2503
2504 fExecuted = TRUE; 2504 fExecuted = TRUE;
@@ -2516,10 +2516,10 @@ static HRESULT ExecuteExePackage(
2516 } 2516 }
2517 2517
2518 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; 2518 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2519 message.dwAllowedResults = MB_OKCANCEL; 2519 message.dwUIHint = MB_OKCANCEL;
2520 message.progress.dwPercentage = fRollback ? 0 : 100; 2520 message.progress.dwPercentage = fRollback ? 0 : 100;
2521 nResult = GenericExecuteMessageHandler(&message, pContext); 2521 nResult = GenericExecuteMessageHandler(&message, pContext);
2522 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); 2522 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2523 ExitOnRootFailure(hr, "BA aborted EXE progress."); 2523 ExitOnRootFailure(hr, "BA aborted EXE progress.");
2524 2524
2525 pContext->cExecutedPackages += fRollback ? -1 : 1; 2525 pContext->cExecutedPackages += fRollback ? -1 : 1;
@@ -2712,10 +2712,10 @@ static HRESULT ExecuteMsuPackage(
2712 ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); 2712 ExitOnRootFailure(hr, "BA aborted execute MSU package begin.");
2713 2713
2714 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; 2714 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2715 message.dwAllowedResults = MB_OKCANCEL; 2715 message.dwUIHint = MB_OKCANCEL;
2716 message.progress.dwPercentage = fRollback ? 100 : 0; 2716 message.progress.dwPercentage = fRollback ? 100 : 0;
2717 nResult = GenericExecuteMessageHandler(&message, pContext); 2717 nResult = GenericExecuteMessageHandler(&message, pContext);
2718 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); 2718 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2719 ExitOnRootFailure(hr, "BA aborted MSU progress."); 2719 ExitOnRootFailure(hr, "BA aborted MSU progress.");
2720 2720
2721 fExecuted = TRUE; 2721 fExecuted = TRUE;
@@ -2733,10 +2733,10 @@ static HRESULT ExecuteMsuPackage(
2733 } 2733 }
2734 2734
2735 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; 2735 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2736 message.dwAllowedResults = MB_OKCANCEL; 2736 message.dwUIHint = MB_OKCANCEL;
2737 message.progress.dwPercentage = fRollback ? 0 : 100; 2737 message.progress.dwPercentage = fRollback ? 0 : 100;
2738 nResult = GenericExecuteMessageHandler(&message, pContext); 2738 nResult = GenericExecuteMessageHandler(&message, pContext);
2739 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); 2739 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult);
2740 ExitOnRootFailure(hr, "BA aborted MSU progress."); 2740 ExitOnRootFailure(hr, "BA aborted MSU progress.");
2741 2741
2742 pContext->cExecutedPackages += fRollback ? -1 : 1; 2742 pContext->cExecutedPackages += fRollback ? -1 : 1;
@@ -3047,6 +3047,7 @@ static int GenericExecuteMessageHandler(
3047 ) 3047 )
3048{ 3048{
3049 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; 3049 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
3050 DWORD dwAllowedResults = pMessage->dwUIHint & MB_TYPEMASK;
3050 int nResult = IDNOACTION; 3051 int nResult = IDNOACTION;
3051 3052
3052 switch (pMessage->type) 3053 switch (pMessage->type)
@@ -3059,15 +3060,16 @@ static int GenericExecuteMessageHandler(
3059 break; 3060 break;
3060 3061
3061 case GENERIC_EXECUTE_MESSAGE_ERROR: 3062 case GENERIC_EXECUTE_MESSAGE_ERROR:
3062 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value. 3063 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, 0, NULL, &nResult); // ignore return value.
3063 break; 3064 break;
3064 3065
3065 case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: 3066 case GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE:
3066 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value. 3067 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX, &nResult); // ignore return value.
3068 dwAllowedResults = BURN_MB_NETFX_FILES_IN_USE;
3067 break; 3069 break;
3068 } 3070 }
3069 3071
3070 nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); 3072 nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, dwAllowedResults, nResult);
3071 return nResult; 3073 return nResult;
3072} 3074}
3073 3075
@@ -3077,7 +3079,9 @@ static int MsiExecuteMessageHandler(
3077 ) 3079 )
3078{ 3080{
3079 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; 3081 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
3082 DWORD dwAllowedResults = pMessage->dwUIHint & MB_TYPEMASK;
3080 int nResult = IDNOACTION; 3083 int nResult = IDNOACTION;
3084 BOOL fRestartManager = FALSE;
3081 3085
3082 switch (pMessage->type) 3086 switch (pMessage->type)
3083 { 3087 {
@@ -3090,20 +3094,24 @@ static int MsiExecuteMessageHandler(
3090 3094
3091 case WIU_MSI_EXECUTE_MESSAGE_ERROR: 3095 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
3092 nResult = pMessage->nResultRecommendation; 3096 nResult = pMessage->nResultRecommendation;
3093 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. 3097 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
3094 break; 3098 break;
3095 3099
3096 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: 3100 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
3097 nResult = pMessage->nResultRecommendation; 3101 nResult = pMessage->nResultRecommendation;
3098 UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. 3102 UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwUIHint, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
3099 break; 3103 break;
3100 3104
3105 case WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE:
3106 fRestartManager = TRUE;
3107 __fallthrough;
3101 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: 3108 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
3102 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value. 3109 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, fRestartManager ? BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM : BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI, &nResult); // ignore return value.
3110 dwAllowedResults = fRestartManager ? BURN_MB_MSI_RM_FILES_IN_USE : BURN_MB_MSI_FILES_IN_USE;
3103 break; 3111 break;
3104 } 3112 }
3105 3113
3106 nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); 3114 nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, dwAllowedResults, nResult);
3107 return nResult; 3115 return nResult;
3108} 3116}
3109 3117
diff --git a/src/burn/engine/apply.h b/src/burn/engine/apply.h
index 45270f92..1717a71a 100644
--- a/src/burn/engine/apply.h
+++ b/src/burn/engine/apply.h
@@ -12,7 +12,7 @@ enum GENERIC_EXECUTE_MESSAGE_TYPE
12 GENERIC_EXECUTE_MESSAGE_NONE, 12 GENERIC_EXECUTE_MESSAGE_NONE,
13 GENERIC_EXECUTE_MESSAGE_ERROR, 13 GENERIC_EXECUTE_MESSAGE_ERROR,
14 GENERIC_EXECUTE_MESSAGE_PROGRESS, 14 GENERIC_EXECUTE_MESSAGE_PROGRESS,
15 GENERIC_EXECUTE_MESSAGE_FILES_IN_USE, 15 GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE,
16}; 16};
17 17
18typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA 18typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
@@ -25,7 +25,7 @@ typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
25typedef struct _GENERIC_EXECUTE_MESSAGE 25typedef struct _GENERIC_EXECUTE_MESSAGE
26{ 26{
27 GENERIC_EXECUTE_MESSAGE_TYPE type; 27 GENERIC_EXECUTE_MESSAGE_TYPE type;
28 DWORD dwAllowedResults; 28 DWORD dwUIHint;
29 29
30 union 30 union
31 { 31 {
diff --git a/src/burn/engine/detect.cpp b/src/burn/engine/detect.cpp
index 5f68a240..4eda240e 100644
--- a/src/burn/engine/detect.cpp
+++ b/src/burn/engine/detect.cpp
@@ -306,8 +306,8 @@ static HRESULT WINAPI AuthenticationRequired(
306 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); 306 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
307 ExitOnFailure(hr, "Failed to allocation error string."); 307 ExitOnFailure(hr, "Failed to allocation error string.");
308 308
309 UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value. 309 UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYCANCEL, 0, NULL, &nResult); // ignore return value.
310 nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); 310 nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, BURN_MB_RETRYTRYAGAIN, nResult);
311 if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect) 311 if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect)
312 { 312 {
313 er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); 313 er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp
index 12c9f296..6c4a775f 100644
--- a/src/burn/engine/elevation.cpp
+++ b/src/burn/engine/elevation.cpp
@@ -42,7 +42,8 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE
42 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, 42 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS,
43 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR, 43 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR,
44 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE, 44 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE,
45 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE, 45 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_FILES_IN_USE,
46 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_NETFX_FILES_IN_USE,
46 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, 47 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID,
47 BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE, 48 BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE,
48} BURN_ELEVATION_MESSAGE_TYPE; 49} BURN_ELEVATION_MESSAGE_TYPE;
@@ -1618,7 +1619,7 @@ static HRESULT ProcessGenericExecuteMessages(
1618 LPWSTR* rgwzFiles = NULL; 1619 LPWSTR* rgwzFiles = NULL;
1619 GENERIC_EXECUTE_MESSAGE message = { }; 1620 GENERIC_EXECUTE_MESSAGE message = { };
1620 1621
1621 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); 1622 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwUIHint);
1622 ExitOnFailure(hr, "Failed to allowed results."); 1623 ExitOnFailure(hr, "Failed to allowed results.");
1623 1624
1624 // Process the message. 1625 // Process the message.
@@ -1645,8 +1646,8 @@ static HRESULT ProcessGenericExecuteMessages(
1645 message.error.wzMessage = sczMessage; 1646 message.error.wzMessage = sczMessage;
1646 break; 1647 break;
1647 1648
1648 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: 1649 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_NETFX_FILES_IN_USE:
1649 message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; 1650 message.type = GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE;
1650 1651
1651 // read message parameters 1652 // read message parameters
1652 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles); 1653 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles);
@@ -1701,6 +1702,7 @@ static HRESULT ProcessMsiPackageMessages(
1701 LPWSTR* rgwzMsiData = NULL; 1702 LPWSTR* rgwzMsiData = NULL;
1702 BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_MSI_MESSAGE_CONTEXT*>(pvContext); 1703 BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_MSI_MESSAGE_CONTEXT*>(pvContext);
1703 LPWSTR sczMessage = NULL; 1704 LPWSTR sczMessage = NULL;
1705 BOOL fRestartManager = FALSE;
1704 1706
1705 // Read MSI extended message data. 1707 // Read MSI extended message data.
1706 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData); 1708 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData);
@@ -1721,7 +1723,7 @@ static HRESULT ProcessMsiPackageMessages(
1721 message.rgwzData = (LPCWSTR*)rgwzMsiData; 1723 message.rgwzData = (LPCWSTR*)rgwzMsiData;
1722 } 1724 }
1723 1725
1724 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); 1726 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwUIHint);
1725 ExitOnFailure(hr, "Failed to read UI flags."); 1727 ExitOnFailure(hr, "Failed to read UI flags.");
1726 1728
1727 // Process the rest of the message. 1729 // Process the rest of the message.
@@ -1759,8 +1761,11 @@ static HRESULT ProcessMsiPackageMessages(
1759 message.msiMessage.wzMessage = sczMessage; 1761 message.msiMessage.wzMessage = sczMessage;
1760 break; 1762 break;
1761 1763
1762 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: 1764 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_FILES_IN_USE:
1763 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; 1765 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&fRestartManager);
1766 ExitOnFailure(hr, "Failed to read fRestartManager.");
1767
1768 message.type = fRestartManager ? WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE : WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1764 message.msiFilesInUse.cFiles = cMsiData; 1769 message.msiFilesInUse.cFiles = cMsiData;
1765 message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData; 1770 message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData;
1766 break; 1771 break;
@@ -3006,7 +3011,7 @@ static int GenericExecuteMessageHandler(
3006 SIZE_T cbData = 0; 3011 SIZE_T cbData = 0;
3007 DWORD dwMessage = 0; 3012 DWORD dwMessage = 0;
3008 3013
3009 hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); 3014 hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwUIHint);
3010 ExitOnFailure(hr, "Failed to write UI flags."); 3015 ExitOnFailure(hr, "Failed to write UI flags.");
3011 3016
3012 switch(pMessage->type) 3017 switch(pMessage->type)
@@ -3030,7 +3035,7 @@ static int GenericExecuteMessageHandler(
3030 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; 3035 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR;
3031 break; 3036 break;
3032 3037
3033 case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: 3038 case GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE:
3034 hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles); 3039 hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles);
3035 ExitOnFailure(hr, "Failed to count of files in use to message buffer."); 3040 ExitOnFailure(hr, "Failed to count of files in use to message buffer.");
3036 3041
@@ -3040,7 +3045,7 @@ static int GenericExecuteMessageHandler(
3040 ExitOnFailure(hr, "Failed to write file in use to message buffer."); 3045 ExitOnFailure(hr, "Failed to write file in use to message buffer.");
3041 } 3046 }
3042 3047
3043 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; 3048 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_NETFX_FILES_IN_USE;
3044 break; 3049 break;
3045 } 3050 }
3046 3051
@@ -3065,6 +3070,7 @@ static int MsiExecuteMessageHandler(
3065 BYTE* pbData = NULL; 3070 BYTE* pbData = NULL;
3066 SIZE_T cbData = 0; 3071 SIZE_T cbData = 0;
3067 DWORD dwMessage = 0; 3072 DWORD dwMessage = 0;
3073 BOOL fRestartManager = FALSE;
3068 3074
3069 // Always send any extra data via the struct first. 3075 // Always send any extra data via the struct first.
3070 hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData); 3076 hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData);
@@ -3076,7 +3082,7 @@ static int MsiExecuteMessageHandler(
3076 ExitOnFailure(hr, "Failed to write MSI data to message buffer."); 3082 ExitOnFailure(hr, "Failed to write MSI data to message buffer.");
3077 } 3083 }
3078 3084
3079 hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); 3085 hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwUIHint);
3080 ExitOnFailure(hr, "Failed to write UI flags."); 3086 ExitOnFailure(hr, "Failed to write UI flags.");
3081 3087
3082 switch (pMessage->type) 3088 switch (pMessage->type)
@@ -3114,11 +3120,15 @@ static int MsiExecuteMessageHandler(
3114 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE; 3120 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE;
3115 break; 3121 break;
3116 3122
3123 case WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE:
3124 fRestartManager = TRUE;
3125 __fallthrough;
3117 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: 3126 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
3118 // NOTE: we do not serialize other message data here because all the "files in use" are in the data above. 3127 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRestartManager);
3128 ExitOnFailure(hr, "Failed to write fRestartManager to message buffer.");
3119 3129
3120 // set message id 3130 // set message id
3121 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; 3131 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_FILES_IN_USE;
3122 break; 3132 break;
3123 3133
3124 default: 3134 default:
diff --git a/src/burn/engine/embedded.cpp b/src/burn/engine/embedded.cpp
index 03898ebd..1c295d59 100644
--- a/src/burn/engine/embedded.cpp
+++ b/src/burn/engine/embedded.cpp
@@ -161,7 +161,7 @@ static HRESULT OnEmbeddedErrorMessage(
161 161
162 message.error.wzMessage = sczMessage; 162 message.error.wzMessage = sczMessage;
163 163
164 hr = BuffReadNumber(pbData, cbData, &iData, &message.dwAllowedResults); 164 hr = BuffReadNumber(pbData, cbData, &iData, &message.dwUIHint);
165 ExitOnFailure(hr, "Failed to read UI hint from buffer."); 165 ExitOnFailure(hr, "Failed to read UI hint from buffer.");
166 166
167 *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); 167 *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext);
@@ -185,7 +185,7 @@ static HRESULT OnEmbeddedProgress(
185 GENERIC_EXECUTE_MESSAGE message = { }; 185 GENERIC_EXECUTE_MESSAGE message = { };
186 186
187 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; 187 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
188 message.dwAllowedResults = MB_OKCANCEL; 188 message.dwUIHint = MB_OKCANCEL;
189 189
190 hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage); 190 hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage);
191 ExitOnFailure(hr, "Failed to read progress from buffer."); 191 ExitOnFailure(hr, "Failed to read progress from buffer.");
diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp
index 67da3bdd..b728f599 100644
--- a/src/burn/engine/exeengine.cpp
+++ b/src/burn/engine/exeengine.cpp
@@ -533,7 +533,7 @@ extern "C" HRESULT ExeEngineExecutePackage(
533 do 533 do
534 { 534 {
535 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; 535 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
536 message.dwAllowedResults = MB_OKCANCEL; 536 message.dwUIHint = MB_OKCANCEL;
537 message.progress.dwPercentage = 50; 537 message.progress.dwPercentage = 50;
538 nResult = pfnGenericMessageHandler(&message, pvContext); 538 nResult = pfnGenericMessageHandler(&message, pvContext);
539 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); 539 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
diff --git a/src/burn/engine/msuengine.cpp b/src/burn/engine/msuengine.cpp
index 1ce2dd11..693bb64b 100644
--- a/src/burn/engine/msuengine.cpp
+++ b/src/burn/engine/msuengine.cpp
@@ -339,7 +339,7 @@ extern "C" HRESULT MsuEngineExecutePackage(
339 do 339 do
340 { 340 {
341 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; 341 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
342 message.dwAllowedResults = MB_OKCANCEL; 342 message.dwUIHint = MB_OKCANCEL;
343 message.progress.dwPercentage = 50; 343 message.progress.dwPercentage = 50;
344 nResult = pfnGenericMessageHandler(&message, pvContext); 344 nResult = pfnGenericMessageHandler(&message, pvContext);
345 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); 345 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
diff --git a/src/burn/engine/netfxchainer.cpp b/src/burn/engine/netfxchainer.cpp
index 4e7a7720..af4f03f5 100644
--- a/src/burn/engine/netfxchainer.cpp
+++ b/src/burn/engine/netfxchainer.cpp
@@ -233,8 +233,8 @@ static HRESULT OnNetFxFilesInUse(
233 } 233 }
234 234
235 // send message 235 // send message
236 message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; 236 message.type = GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE;
237 message.dwAllowedResults = MB_ABORTRETRYIGNORE; 237 message.dwUIHint = MB_ABORTRETRYIGNORE;
238 message.filesInUse.cFiles = cFiles; 238 message.filesInUse.cFiles = cFiles;
239 message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; 239 message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles;
240 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); 240 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
@@ -259,7 +259,7 @@ static HRESULT OnNetFxProgress(
259 259
260 // send message 260 // send message
261 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; 261 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
262 message.dwAllowedResults = MB_OKCANCEL; 262 message.dwUIHint = MB_OKCANCEL;
263 message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX; 263 message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX;
264 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); 264 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
265 265
@@ -283,7 +283,7 @@ static HRESULT OnNetFxError(
283 283
284 // send message 284 // send message
285 message.type = GENERIC_EXECUTE_MESSAGE_ERROR; 285 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
286 message.dwAllowedResults = MB_OK; 286 message.dwUIHint = MB_OK;
287 message.error.dwErrorCode = hrError; 287 message.error.dwErrorCode = hrError;
288 message.error.wzMessage = NULL; 288 message.error.wzMessage = NULL;
289 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); 289 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp
index a6d670ea..7cc6f049 100644
--- a/src/burn/engine/userexperience.cpp
+++ b/src/burn/engine/userexperience.cpp
@@ -1494,6 +1494,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse(
1494 __in_z LPCWSTR wzPackageId, 1494 __in_z LPCWSTR wzPackageId,
1495 __in DWORD cFiles, 1495 __in DWORD cFiles,
1496 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, 1496 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
1497 __in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
1497 __inout int* pnResult 1498 __inout int* pnResult
1498 ) 1499 )
1499{ 1500{
@@ -1506,6 +1507,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse(
1506 args.cFiles = cFiles; 1507 args.cFiles = cFiles;
1507 args.rgwzFiles = rgwzFiles; 1508 args.rgwzFiles = rgwzFiles;
1508 args.nRecommendation = *pnResult; 1509 args.nRecommendation = *pnResult;
1510 args.source = source;
1509 1511
1510 results.cbSize = sizeof(results); 1512 results.cbSize = sizeof(results);
1511 results.nResult = *pnResult; 1513 results.nResult = *pnResult;
@@ -2492,13 +2494,12 @@ static int FilterResult(
2492 __in int nResult 2494 __in int nResult
2493 ) 2495 )
2494{ 2496{
2495 DWORD dwFilteredAllowedResults = dwAllowedResults & MB_TYPEMASK;
2496 if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through. 2497 if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through.
2497 { 2498 {
2498 } 2499 }
2499 else 2500 else
2500 { 2501 {
2501 switch (dwFilteredAllowedResults) 2502 switch (dwAllowedResults)
2502 { 2503 {
2503 case MB_OK: 2504 case MB_OK:
2504 nResult = IDOK; 2505 nResult = IDOK;
@@ -2606,7 +2607,28 @@ static int FilterResult(
2606 } 2607 }
2607 break; 2608 break;
2608 2609
2609 case WIU_MB_OKIGNORECANCELRETRY: // custom Windows Installer utility return code. 2610 case BURN_MB_MSI_FILES_IN_USE:
2611 // https://docs.microsoft.com/en-us/windows/win32/msi/installvalidate-action
2612 if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2613 {
2614 nResult = IDRETRY;
2615 }
2616 else if (IDCANCEL == nResult || IDABORT == nResult)
2617 {
2618 nResult = IDCANCEL;
2619 }
2620 else if (IDCONTINUE == nResult || IDIGNORE == nResult)
2621 {
2622 nResult = IDIGNORE;
2623 }
2624 else
2625 {
2626 nResult = IDNOACTION;
2627 }
2628 break;
2629
2630 case BURN_MB_MSI_RM_FILES_IN_USE:
2631 // https://docs.microsoft.com/en-us/windows/win32/msi/using-restart-manager-with-an-external-ui-
2610 if (IDOK == nResult || IDYES == nResult) 2632 if (IDOK == nResult || IDYES == nResult)
2611 { 2633 {
2612 nResult = IDOK; 2634 nResult = IDOK;
@@ -2615,11 +2637,15 @@ static int FilterResult(
2615 { 2637 {
2616 nResult = IDIGNORE; 2638 nResult = IDIGNORE;
2617 } 2639 }
2640 else if (IDNO == nResult)
2641 {
2642 nResult = IDNO;
2643 }
2618 else if (IDCANCEL == nResult || IDABORT == nResult) 2644 else if (IDCANCEL == nResult || IDABORT == nResult)
2619 { 2645 {
2620 nResult = IDCANCEL; 2646 nResult = IDCANCEL;
2621 } 2647 }
2622 else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult) 2648 else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2623 { 2649 {
2624 nResult = IDRETRY; 2650 nResult = IDRETRY;
2625 } 2651 }
@@ -2629,13 +2655,33 @@ static int FilterResult(
2629 } 2655 }
2630 break; 2656 break;
2631 2657
2632 case MB_RETRYTRYAGAIN: // custom return code. 2658 case BURN_MB_RETRYTRYAGAIN: // custom return code.
2633 if (IDRETRY != nResult && IDTRYAGAIN != nResult) 2659 if (IDRETRY != nResult && IDTRYAGAIN != nResult)
2634 { 2660 {
2635 nResult = IDNOACTION; 2661 nResult = IDNOACTION;
2636 } 2662 }
2637 break; 2663 break;
2638 2664
2665 case BURN_MB_NETFX_FILES_IN_USE:
2666 // https://docs.microsoft.com/en-us/dotnet/framework/deployment/how-to-get-progress-from-the-dotnet-installer
2667 if (IDOK == nResult || IDYES == nResult)
2668 {
2669 nResult = IDYES;
2670 }
2671 else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2672 {
2673 nResult = IDRETRY;
2674 }
2675 else if (IDCANCEL == nResult || IDABORT == nResult)
2676 {
2677 nResult = IDCANCEL;
2678 }
2679 else
2680 {
2681 nResult = IDNO;
2682 }
2683 break;
2684
2639 default: 2685 default:
2640 AssertSz(FALSE, "Unknown allowed results."); 2686 AssertSz(FALSE, "Unknown allowed results.");
2641 break; 2687 break;
diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h
index f7ac962c..75723afa 100644
--- a/src/burn/engine/userexperience.h
+++ b/src/burn/engine/userexperience.h
@@ -10,7 +10,10 @@ extern "C" {
10 10
11// constants 11// constants
12 12
13const DWORD MB_RETRYTRYAGAIN = 0xF; 13const DWORD BURN_MB_RETRYTRYAGAIN = 0x10;
14const DWORD BURN_MB_MSI_FILES_IN_USE = 0x11;
15const DWORD BURN_MB_MSI_RM_FILES_IN_USE = 0x12;
16const DWORD BURN_MB_NETFX_FILES_IN_USE = 0x13;
14 17
15 18
16// structs 19// structs
@@ -356,6 +359,7 @@ BAAPI UserExperienceOnExecuteFilesInUse(
356 __in_z LPCWSTR wzPackageId, 359 __in_z LPCWSTR wzPackageId,
357 __in DWORD cFiles, 360 __in DWORD cFiles,
358 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, 361 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
362 __in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
359 __inout int* pnResult 363 __inout int* pnResult
360 ); 364 );
361BAAPI UserExperienceOnExecuteMsiMessage( 365BAAPI UserExperienceOnExecuteMsiMessage(
diff --git a/src/internal/SetBuildNumber/Directory.Packages.props.pp b/src/internal/SetBuildNumber/Directory.Packages.props.pp
index 4743fb2e..86f9d8cb 100644
--- a/src/internal/SetBuildNumber/Directory.Packages.props.pp
+++ b/src/internal/SetBuildNumber/Directory.Packages.props.pp
@@ -29,6 +29,7 @@
29 <PackageVersion Include="WixToolset.Bal.wixext" Version="{packageversion}" /> 29 <PackageVersion Include="WixToolset.Bal.wixext" Version="{packageversion}" />
30 <PackageVersion Include="WixToolset.Dependency.wixext" Version="{packageversion}" /> 30 <PackageVersion Include="WixToolset.Dependency.wixext" Version="{packageversion}" />
31 <PackageVersion Include="WixToolset.NetFx.wixext" Version="{packageversion}" /> 31 <PackageVersion Include="WixToolset.NetFx.wixext" Version="{packageversion}" />
32 <PackageVersion Include="WixToolset.UI.wixext" Version="{packageversion}" />
32 <PackageVersion Include="WixToolset.Util.wixext" Version="{packageversion}" /> 33 <PackageVersion Include="WixToolset.Util.wixext" Version="{packageversion}" />
33 </ItemGroup> 34 </ItemGroup>
34 35
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h b/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h
index 9c2de209..0ddcd893 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h
@@ -9,7 +9,6 @@ extern "C" {
9// constants 9// constants
10 10
11#define IDNOACTION 0 11#define IDNOACTION 0
12#define WIU_MB_OKIGNORECANCELRETRY 0xE
13 12
14#define MAX_DARWIN_KEY 73 13#define MAX_DARWIN_KEY 73
15#define MAX_DARWIN_COLUMN 255 14#define MAX_DARWIN_COLUMN 255
@@ -37,6 +36,7 @@ typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE
37 WIU_MSI_EXECUTE_MESSAGE_ERROR, 36 WIU_MSI_EXECUTE_MESSAGE_ERROR,
38 WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE, 37 WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE,
39 WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE, 38 WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE,
39 WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE,
40} WIU_MSI_EXECUTE_MESSAGE_TYPE; 40} WIU_MSI_EXECUTE_MESSAGE_TYPE;
41 41
42 42
@@ -45,7 +45,7 @@ typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE
45typedef struct _WIU_MSI_EXECUTE_MESSAGE 45typedef struct _WIU_MSI_EXECUTE_MESSAGE
46{ 46{
47 WIU_MSI_EXECUTE_MESSAGE_TYPE type; 47 WIU_MSI_EXECUTE_MESSAGE_TYPE type;
48 DWORD dwAllowedResults; 48 DWORD dwUIHint;
49 49
50 DWORD cData; 50 DWORD cData;
51 LPCWSTR* rgwzData; 51 LPCWSTR* rgwzData;
diff --git a/src/libs/dutil/WixToolset.DUtil/wiutil.cpp b/src/libs/dutil/WixToolset.DUtil/wiutil.cpp
index 7414ac42..da7cffe7 100644
--- a/src/libs/dutil/WixToolset.DUtil/wiutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/wiutil.cpp
@@ -113,6 +113,7 @@ static INT SendErrorMessage(
113 ); 113 );
114static INT SendFilesInUseMessage( 114static INT SendFilesInUseMessage(
115 __in WIU_MSI_EXECUTE_CONTEXT* pContext, 115 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
116 __in UINT uiFlags,
116 __in_opt MSIHANDLE hRecord, 117 __in_opt MSIHANDLE hRecord,
117 __in BOOL fRestartManagerRequest 118 __in BOOL fRestartManagerRequest
118 ); 119 );
@@ -1161,7 +1162,7 @@ Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex,
1161 1162
1162 case INSTALLMESSAGE_FILESINUSE: 1163 case INSTALLMESSAGE_FILESINUSE:
1163 case INSTALLMESSAGE_RMFILESINUSE: 1164 case INSTALLMESSAGE_RMFILESINUSE:
1164 nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt); 1165 nResult = SendFilesInUseMessage(pContext, uiFlags, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt);
1165 break; 1166 break;
1166 1167
1167/* 1168/*
@@ -1401,7 +1402,7 @@ static INT SendMsiMessage(
1401 InitializeMessageData(hRecord, &rgsczData, &cData); 1402 InitializeMessageData(hRecord, &rgsczData, &cData);
1402 1403
1403 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; 1404 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE;
1404 message.dwAllowedResults = uiFlags; 1405 message.dwUIHint = uiFlags;
1405 message.cData = cData; 1406 message.cData = cData;
1406 message.rgwzData = (LPCWSTR*)rgsczData; 1407 message.rgwzData = (LPCWSTR*)rgsczData;
1407 message.msiMessage.mt = mt; 1408 message.msiMessage.mt = mt;
@@ -1445,7 +1446,7 @@ static INT SendErrorMessage(
1445 InitializeMessageData(hRecord, &rgsczData, &cData); 1446 InitializeMessageData(hRecord, &rgsczData, &cData);
1446 1447
1447 message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; 1448 message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR;
1448 message.dwAllowedResults = uiFlags; 1449 message.dwUIHint = uiFlags;
1449 message.nResultRecommendation = nResult; 1450 message.nResultRecommendation = nResult;
1450 message.cData = cData; 1451 message.cData = cData;
1451 message.rgwzData = (LPCWSTR*)rgsczData; 1452 message.rgwzData = (LPCWSTR*)rgsczData;
@@ -1459,8 +1460,9 @@ static INT SendErrorMessage(
1459 1460
1460static INT SendFilesInUseMessage( 1461static INT SendFilesInUseMessage(
1461 __in WIU_MSI_EXECUTE_CONTEXT* pContext, 1462 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1463 __in UINT uiFlags,
1462 __in_opt MSIHANDLE hRecord, 1464 __in_opt MSIHANDLE hRecord,
1463 __in BOOL /*fRestartManagerRequest*/ 1465 __in BOOL fRestartManagerRequest
1464 ) 1466 )
1465{ 1467{
1466 INT nResult = IDNOACTION; 1468 INT nResult = IDNOACTION;
@@ -1470,8 +1472,8 @@ static INT SendFilesInUseMessage(
1470 1472
1471 InitializeMessageData(hRecord, &rgsczData, &cData); 1473 InitializeMessageData(hRecord, &rgsczData, &cData);
1472 1474
1473 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; 1475 message.type = fRestartManagerRequest ? WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE : WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1474 message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY; 1476 message.dwUIHint = uiFlags;
1475 message.cData = cData; 1477 message.cData = cData;
1476 message.rgwzData = (LPCWSTR*)rgsczData; 1478 message.rgwzData = (LPCWSTR*)rgsczData;
1477 message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information. 1479 message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information.
@@ -1527,7 +1529,7 @@ static INT SendProgressUpdate(
1527#endif 1529#endif
1528 1530
1529 message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; 1531 message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS;
1530 message.dwAllowedResults = MB_OKCANCEL; 1532 message.dwUIHint = MB_OKCANCEL;
1531 message.progress.dwPercentage = dwPercentage; 1533 message.progress.dwPercentage = dwPercentage;
1532 nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); 1534 nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1533 1535
diff --git a/src/test/burn/TestBA/TestBA.cs b/src/test/burn/TestBA/TestBA.cs
index 5c70253d..5ef26253 100644
--- a/src/test/burn/TestBA/TestBA.cs
+++ b/src/test/burn/TestBA/TestBA.cs
@@ -399,7 +399,7 @@ namespace WixToolset.Test.BA
399 399
400 protected override void OnExecuteFilesInUse(ExecuteFilesInUseEventArgs args) 400 protected override void OnExecuteFilesInUse(ExecuteFilesInUseEventArgs args)
401 { 401 {
402 this.Log("OnExecuteFilesInUse() - package: {0}, retries remaining: {1}, data: {2}", args.PackageId, this.retryExecuteFilesInUse, String.Join(", ", args.Files.ToArray())); 402 this.Log("OnExecuteFilesInUse() - package: {0}, source: {1}, retries remaining: {2}, data: {3}", args.PackageId, args.Source, this.retryExecuteFilesInUse, String.Join(", ", args.Files.ToArray()));
403 403
404 if (this.retryExecuteFilesInUse > 0) 404 if (this.retryExecuteFilesInUse > 0)
405 { 405 {
diff --git a/src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wixproj b/src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wixproj
new file mode 100644
index 00000000..c71410c2
--- /dev/null
+++ b/src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wixproj
@@ -0,0 +1,18 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <OutputType>Bundle</OutputType>
5 <UpgradeCode>{6A348108-8ACE-4D13-A352-D8F76785BFE4}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <ProjectReference Include="..\PackageA\PackageA.wixproj" />
12 <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
13 </ItemGroup>
14 <ItemGroup>
15 <PackageReference Include="WixToolset.Bal.wixext" />
16 <PackageReference Include="WixToolset.NetFx.wixext" />
17 </ItemGroup>
18</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wxs b/src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wxs
new file mode 100644
index 00000000..bd164a29
--- /dev/null
+++ b/src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.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="PackageA" SourceFile="$(var.PackageA.TargetPath)" />
8 </PackageGroup>
9 </Fragment>
10</Wix>
diff --git a/src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wixproj b/src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wixproj
new file mode 100644
index 00000000..57825f7b
--- /dev/null
+++ b/src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wixproj
@@ -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<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <ProductComponentsRef>true</ProductComponentsRef>
5 <UpgradeCode>{C94C8FC9-1347-44CE-B1FB-0A6196928921}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <PackageReference Include="WixToolset.UI.wixext" />
12 </ItemGroup>
13</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wxs b/src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wxs
new file mode 100644
index 00000000..a96c2a11
--- /dev/null
+++ b/src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
4 <Fragment>
5 <ComponentGroup Id="ProductComponents" />
6
7 <UIRef Id="WixUI_Minimal" />
8 <WixVariable Id="WixUILicenseRtf" Value="license.txt" />
9 </Fragment>
10</Wix>
diff --git a/src/test/burn/TestData/FilesInUseTests/PackageA/license.txt b/src/test/burn/TestData/FilesInUseTests/PackageA/license.txt
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/src/test/burn/TestData/FilesInUseTests/PackageA/license.txt
@@ -0,0 +1 @@
a \ No newline at end of file
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/FilesInUseTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/FilesInUseTests.cs
new file mode 100644
index 00000000..6ad68d22
--- /dev/null
+++ b/src/test/burn/WixToolsetTest.BurnE2E/FilesInUseTests.cs
@@ -0,0 +1,38 @@
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 System.IO;
6 using WixTestTools;
7 using Xunit;
8 using Xunit.Abstractions;
9
10 public class FilesInUseTests : BurnE2ETests
11 {
12 public FilesInUseTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
13
14 [Fact]
15 public void CanCancelInstallAfterRetryingLockedFile()
16 {
17 var packageA = this.CreatePackageInstaller("PackageA");
18 var bundleA = this.CreateBundleInstaller("BundleA");
19 var testBAController = this.CreateTestBAController();
20
21 testBAController.SetPackageRetryExecuteFilesInUse("PackageA", 1);
22
23 packageA.VerifyInstalled(false);
24
25 // Lock the file that will be installed.
26 string targetInstallFile = packageA.GetInstalledFilePath("Package.wxs");
27 Directory.CreateDirectory(Path.GetDirectoryName(targetInstallFile));
28 using (FileStream lockTargetFile = new FileStream(targetInstallFile, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose))
29 {
30 bundleA.Install(expectedExitCode: (int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT);
31 }
32
33 bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
34
35 packageA.VerifyInstalled(false);
36 }
37 }
38}
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs b/src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs
index 6e4fe6c6..d2e8a1ca 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs
@@ -118,6 +118,16 @@ namespace WixToolsetTest.BurnE2E
118 } 118 }
119 119
120 /// <summary> 120 /// <summary>
121 /// Retries the files in use one or more times before canceling.
122 /// </summary>
123 /// <param name="packageId">Package identity.</param>
124 /// <param name="cancelPoint">Sets or removes the retry count on a package's file in use message.</param>
125 public void SetPackageRetryExecuteFilesInUse(string packageId, int? retryCount)
126 {
127 this.SetPackageState(packageId, "RetryExecuteFilesInUse", retryCount.HasValue ? retryCount.ToString() : null);
128 }
129
130 /// <summary>
121 /// Sets the requested state for a package that the TestBA will return to the engine during plan. 131 /// Sets the requested state for a package that the TestBA will return to the engine during plan.
122 /// </summary> 132 /// </summary>
123 /// <param name="packageId">Package identity.</param> 133 /// <param name="packageId">Package identity.</param>