@joebigelow / wix / commits / 9e2cda60

FilesInUse messages are too different to unify in the Burn engine.

Fixes #6348

Sean Hall committed Dec 15, 2021 at 10:48 UTC 9e2cda60e3852660f235beb5e0af1c746d0045e6
29 files changed +277 -84
src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+8
@@ -38,6 +38,13 @@ enum BOOTSTRAPPER_ERROR_TYPE
38 BOOTSTRAPPER_ERROR_TYPE_APPLY, // error occurred during apply.
39 };
40
41 +enum 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 +
48 enum BOOTSTRAPPER_RELATED_OPERATION
49 {
50 BOOTSTRAPPER_RELATED_OPERATION_NONE,
@@ -874,6 +881,7 @@ struct BA_ONEXECUTEFILESINUSE_ARGS
881 DWORD cFiles;
882 LPCWSTR* rgwzFiles;
883 int nRecommendation;
884 + BOOTSTRAPPER_FILES_IN_USE_TYPE source;
885 };
886
887 struct BA_ONEXECUTEFILESINUSE_RESULTS
src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+2 -2
@@ -1712,9 +1712,9 @@ namespace WixToolset.Mba.Core
1712 return args.HResult;
1713 }
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 {
1717 - ExecuteFilesInUseEventArgs args = new ExecuteFilesInUseEventArgs(wzPackageId, rgwzFiles, nRecommendation, pResult);
1717 + ExecuteFilesInUseEventArgs args = new ExecuteFilesInUseEventArgs(wzPackageId, rgwzFiles, nRecommendation, source, pResult);
1718 this.OnExecuteFilesInUse(args);
1719
1720 pResult = args.Result;
src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+7 -1
@@ -1613,11 +1613,12 @@ namespace WixToolset.Mba.Core
1613 public class ExecuteFilesInUseEventArgs : ResultEventArgs
1614 {
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)
1618 {
1619 this.PackageId = packageId;
1620 this.Files = new ReadOnlyCollection<string>(files ?? new string[] { });
1621 + this.Source = source;
1622 }
1623
1624 /// <summary>
@@ -1629,6 +1630,11 @@ namespace WixToolset.Mba.Core
1630 /// Gets the list of files in use.
1631 /// </summary>
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; }
1638 }
1639
1640 /// <summary>
src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+20 -6
@@ -777,12 +777,6 @@ namespace WixToolset.Mba.Core
777 /// <summary>
778 /// See <see cref="IDefaultBootstrapperApplication.ExecuteFilesInUse"/>.
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>
780 [PreserveSig]
781 [return: MarshalAs(UnmanagedType.I4)]
782 int OnExecuteFilesInUse(
@@ -790,6 +784,7 @@ namespace WixToolset.Mba.Core
784 [MarshalAs(UnmanagedType.U4)] int cFiles,
785 [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.LPWStr), In] string[] rgwzFiles,
786 [MarshalAs(UnmanagedType.I4)] Result nRecommendation,
787 + [MarshalAs(UnmanagedType.I4)] FilesInUseType source,
788 [MarshalAs(UnmanagedType.I4)] ref Result pResult
789 );
790
@@ -1445,6 +1440,25 @@ namespace WixToolset.Mba.Core
1440 Apply,
1441 };
1442
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>
1463 /// The calculated operation for the related bundle.
1464 /// </summary>
src/api/burn/balutil/inc/BalBaseBAFunctions.h
+1
@@ -583,6 +583,7 @@ public: // IBootstrapperApplication
583 __in DWORD /*cFiles*/,
584 __in_ecount_z(cFiles) LPCWSTR* /*rgwzFiles*/,
585 __in int /*nRecommendation*/,
586 + __in BOOTSTRAPPER_FILES_IN_USE_TYPE /*source*/,
587 __inout int* /*pResult*/
588 )
589 {
src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+1
@@ -738,6 +738,7 @@ public: // IBootstrapperApplication
738 __in DWORD /*cFiles*/,
739 __in_ecount_z(cFiles) LPCWSTR* /*rgwzFiles*/,
740 __in int /*nRecommendation*/,
741 + __in BOOTSTRAPPER_FILES_IN_USE_TYPE /*source*/,
742 __inout int* pResult
743 )
744 {
src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+1 -1
@@ -438,7 +438,7 @@ static HRESULT BalBaseBAProcOnExecuteFilesInUse(
438 __inout BA_ONEXECUTEFILESINUSE_RESULTS* pResults
439 )
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 }
443
444 static HRESULT BalBaseBAProcOnExecutePackageComplete(
src/api/burn/balutil/inc/IBootstrapperApplication.h
+7 -15
@@ -488,27 +488,19 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
488 __inout int* pResult
489 ) = 0;
490
491 - // OnExecuteFilesInUse - called when the engine encounters files in use while
492 - // executing a package.
491 + // OnExecuteFilesInUse - called when the engine receives a files in use message
492 + // while executing a package.
493 //
494 - // Return:
495 - // IDOK instructs the engine to let the Restart Manager attempt to close the
496 - // applications to avoid a restart.
497 - //
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.
494 + // Return value depends on the source:
495 + // BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI: https://docs.microsoft.com/en-us/windows/win32/msi/installvalidate-action
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 + // BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX: https://docs.microsoft.com/en-us/dotnet/framework/deployment/how-to-get-progress-from-the-dotnet-installer
498 STDMETHOD(OnExecuteFilesInUse)(
499 __in_z LPCWSTR wzPackageId,
500 __in DWORD cFiles,
501 __in_ecount_z(cFiles) LPCWSTR* rgwzFiles,
502 __in int nRecommendation,
503 + __in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
504 __inout int* pResult
505 ) = 0;
506
src/burn/engine/apply.cpp
+26 -18
@@ -1931,8 +1931,8 @@ static HRESULT WINAPI AuthenticationRequired(
1931
1932 APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData);
1933
1934 - UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value;
1935 - nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
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, BURN_MB_RETRYTRYAGAIN, nResult);
1936 if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply)
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);
@@ -2495,10 +2495,10 @@ static HRESULT ExecuteExePackage(
2495 ExitOnRootFailure(hr, "BA aborted execute EXE package begin.");
2496
2497 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2498 - message.dwAllowedResults = MB_OKCANCEL;
2498 + message.dwUIHint = MB_OKCANCEL;
2499 message.progress.dwPercentage = fRollback ? 100 : 0;
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.");
2503
2504 fExecuted = TRUE;
@@ -2516,10 +2516,10 @@ static HRESULT ExecuteExePackage(
2516 }
2517
2518 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2519 - message.dwAllowedResults = MB_OKCANCEL;
2519 + message.dwUIHint = MB_OKCANCEL;
2520 message.progress.dwPercentage = fRollback ? 0 : 100;
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.");
2524
2525 pContext->cExecutedPackages += fRollback ? -1 : 1;
@@ -2712,10 +2712,10 @@ static HRESULT ExecuteMsuPackage(
2712 ExitOnRootFailure(hr, "BA aborted execute MSU package begin.");
2713
2714 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2715 - message.dwAllowedResults = MB_OKCANCEL;
2715 + message.dwUIHint = MB_OKCANCEL;
2716 message.progress.dwPercentage = fRollback ? 100 : 0;
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.");
2720
2721 fExecuted = TRUE;
@@ -2733,10 +2733,10 @@ static HRESULT ExecuteMsuPackage(
2733 }
2734
2735 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2736 - message.dwAllowedResults = MB_OKCANCEL;
2736 + message.dwUIHint = MB_OKCANCEL;
2737 message.progress.dwPercentage = fRollback ? 0 : 100;
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.");
2741
2742 pContext->cExecutedPackages += fRollback ? -1 : 1;
@@ -3047,6 +3047,7 @@ static int GenericExecuteMessageHandler(
3047 )
3048 {
3049 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
3050 + DWORD dwAllowedResults = pMessage->dwUIHint & MB_TYPEMASK;
3051 int nResult = IDNOACTION;
3052
3053 switch (pMessage->type)
@@ -3059,15 +3060,16 @@ static int GenericExecuteMessageHandler(
3060 break;
3061
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.
3064 break;
3065
3065 - case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE:
3066 - UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value.
3066 + case GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE:
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;
3069 break;
3070 }
3071
3070 - nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult);
3072 + nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, dwAllowedResults, nResult);
3073 return nResult;
3074 }
3075
@@ -3077,7 +3079,9 @@ static int MsiExecuteMessageHandler(
3079 )
3080 {
3081 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
3082 + DWORD dwAllowedResults = pMessage->dwUIHint & MB_TYPEMASK;
3083 int nResult = IDNOACTION;
3084 + BOOL fRestartManager = FALSE;
3085
3086 switch (pMessage->type)
3087 {
@@ -3090,20 +3094,24 @@ static int MsiExecuteMessageHandler(
3094
3095 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
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.
3098 break;
3099
3100 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
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.
3103 break;
3104
3105 + case WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE:
3106 + fRestartManager = TRUE;
3107 + __fallthrough;
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;
3111 break;
3112 }
3113
3106 - nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult);
3114 + nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, dwAllowedResults, nResult);
3115 return nResult;
3116 }
3117
src/burn/engine/apply.h
+2 -2
@@ -12,7 +12,7 @@ enum GENERIC_EXECUTE_MESSAGE_TYPE
12 GENERIC_EXECUTE_MESSAGE_NONE,
13 GENERIC_EXECUTE_MESSAGE_ERROR,
14 GENERIC_EXECUTE_MESSAGE_PROGRESS,
15 - GENERIC_EXECUTE_MESSAGE_FILES_IN_USE,
15 + GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE,
16 };
17
18 typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
@@ -25,7 +25,7 @@ typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
25 typedef struct _GENERIC_EXECUTE_MESSAGE
26 {
27 GENERIC_EXECUTE_MESSAGE_TYPE type;
28 - DWORD dwAllowedResults;
28 + DWORD dwUIHint;
29
30 union
31 {
src/burn/engine/detect.cpp
+2 -2
@@ -306,8 +306,8 @@ static HRESULT WINAPI AuthenticationRequired(
306 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
307 ExitOnFailure(hr, "Failed to allocation error string.");
308
309 - UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value.
310 - nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
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, BURN_MB_RETRYTRYAGAIN, nResult);
311 if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect)
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);
src/burn/engine/elevation.cpp
+23 -13
@@ -42,7 +42,8 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE
42 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS,
43 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR,
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,
47 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID,
48 BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE,
49 } BURN_ELEVATION_MESSAGE_TYPE;
@@ -1618,7 +1619,7 @@ static HRESULT ProcessGenericExecuteMessages(
1619 LPWSTR* rgwzFiles = NULL;
1620 GENERIC_EXECUTE_MESSAGE message = { };
1621
1621 - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults);
1622 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwUIHint);
1623 ExitOnFailure(hr, "Failed to allowed results.");
1624
1625 // Process the message.
@@ -1645,8 +1646,8 @@ static HRESULT ProcessGenericExecuteMessages(
1646 message.error.wzMessage = sczMessage;
1647 break;
1648
1648 - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE:
1649 - message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE;
1649 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_NETFX_FILES_IN_USE:
1650 + message.type = GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE;
1651
1652 // read message parameters
1653 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles);
@@ -1701,6 +1702,7 @@ static HRESULT ProcessMsiPackageMessages(
1702 LPWSTR* rgwzMsiData = NULL;
1703 BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_MSI_MESSAGE_CONTEXT*>(pvContext);
1704 LPWSTR sczMessage = NULL;
1705 + BOOL fRestartManager = FALSE;
1706
1707 // Read MSI extended message data.
1708 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData);
@@ -1721,7 +1723,7 @@ static HRESULT ProcessMsiPackageMessages(
1723 message.rgwzData = (LPCWSTR*)rgwzMsiData;
1724 }
1725
1724 - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults);
1726 + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwUIHint);
1727 ExitOnFailure(hr, "Failed to read UI flags.");
1728
1729 // Process the rest of the message.
@@ -1759,8 +1761,11 @@ static HRESULT ProcessMsiPackageMessages(
1761 message.msiMessage.wzMessage = sczMessage;
1762 break;
1763
1762 - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE:
1763 - message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1764 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_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;
1769 message.msiFilesInUse.cFiles = cMsiData;
1770 message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData;
1771 break;
@@ -3006,7 +3011,7 @@ static int GenericExecuteMessageHandler(
3011 SIZE_T cbData = 0;
3012 DWORD dwMessage = 0;
3013
3009 - hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults);
3014 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwUIHint);
3015 ExitOnFailure(hr, "Failed to write UI flags.");
3016
3017 switch(pMessage->type)
@@ -3030,7 +3035,7 @@ static int GenericExecuteMessageHandler(
3035 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR;
3036 break;
3037
3033 - case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE:
3038 + case GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE:
3039 hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles);
3040 ExitOnFailure(hr, "Failed to count of files in use to message buffer.");
3041
@@ -3040,7 +3045,7 @@ static int GenericExecuteMessageHandler(
3045 ExitOnFailure(hr, "Failed to write file in use to message buffer.");
3046 }
3047
3043 - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE;
3048 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_NETFX_FILES_IN_USE;
3049 break;
3050 }
3051
@@ -3065,6 +3070,7 @@ static int MsiExecuteMessageHandler(
3070 BYTE* pbData = NULL;
3071 SIZE_T cbData = 0;
3072 DWORD dwMessage = 0;
3073 + BOOL fRestartManager = FALSE;
3074
3075 // Always send any extra data via the struct first.
3076 hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData);
@@ -3076,7 +3082,7 @@ static int MsiExecuteMessageHandler(
3082 ExitOnFailure(hr, "Failed to write MSI data to message buffer.");
3083 }
3084
3079 - hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults);
3085 + hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwUIHint);
3086 ExitOnFailure(hr, "Failed to write UI flags.");
3087
3088 switch (pMessage->type)
@@ -3114,11 +3120,15 @@ static int MsiExecuteMessageHandler(
3120 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE;
3121 break;
3122
3123 + case WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE:
3124 + fRestartManager = TRUE;
3125 + __fallthrough;
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.");
3129
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;
3132 break;
3133
3134 default:
src/burn/engine/embedded.cpp
+2 -2
@@ -161,7 +161,7 @@ static HRESULT OnEmbeddedErrorMessage(
161
162 message.error.wzMessage = sczMessage;
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.");
166
167 *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext);
@@ -185,7 +185,7 @@ static HRESULT OnEmbeddedProgress(
185 GENERIC_EXECUTE_MESSAGE message = { };
186
187 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
188 - message.dwAllowedResults = MB_OKCANCEL;
188 + message.dwUIHint = MB_OKCANCEL;
189
190 hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage);
191 ExitOnFailure(hr, "Failed to read progress from buffer.");
src/burn/engine/exeengine.cpp
+1 -1
@@ -533,7 +533,7 @@ extern "C" HRESULT ExeEngineExecutePackage(
533 do
534 {
535 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
536 - message.dwAllowedResults = MB_OKCANCEL;
536 + message.dwUIHint = MB_OKCANCEL;
537 message.progress.dwPercentage = 50;
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);
src/burn/engine/msuengine.cpp
+1 -1
@@ -339,7 +339,7 @@ extern "C" HRESULT MsuEngineExecutePackage(
339 do
340 {
341 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
342 - message.dwAllowedResults = MB_OKCANCEL;
342 + message.dwUIHint = MB_OKCANCEL;
343 message.progress.dwPercentage = 50;
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);
src/burn/engine/netfxchainer.cpp
+4 -4
@@ -233,8 +233,8 @@ static HRESULT OnNetFxFilesInUse(
233 }
234
235 // send message
236 - message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE;
237 - message.dwAllowedResults = MB_ABORTRETRYIGNORE;
236 + message.type = GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE;
237 + message.dwUIHint = MB_ABORTRETRYIGNORE;
238 message.filesInUse.cFiles = cFiles;
239 message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles;
240 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
@@ -259,7 +259,7 @@ static HRESULT OnNetFxProgress(
259
260 // send message
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;
264 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
265
@@ -283,7 +283,7 @@ static HRESULT OnNetFxError(
283
284 // send message
285 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
286 - message.dwAllowedResults = MB_OK;
286 + message.dwUIHint = MB_OK;
287 message.error.dwErrorCode = hrError;
288 message.error.wzMessage = NULL;
289 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
src/burn/engine/userexperience.cpp
+51 -5
@@ -1494,6 +1494,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse(
1494 __in_z LPCWSTR wzPackageId,
1495 __in DWORD cFiles,
1496 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
1497 + __in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
1498 __inout int* pnResult
1499 )
1500 {
@@ -1506,6 +1507,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse(
1507 args.cFiles = cFiles;
1508 args.rgwzFiles = rgwzFiles;
1509 args.nRecommendation = *pnResult;
1510 + args.source = source;
1511
1512 results.cbSize = sizeof(results);
1513 results.nResult = *pnResult;
@@ -2492,13 +2494,12 @@ static int FilterResult(
2494 __in int nResult
2495 )
2496 {
2495 - DWORD dwFilteredAllowedResults = dwAllowedResults & MB_TYPEMASK;
2497 if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through.
2498 {
2499 }
2500 else
2501 {
2501 - switch (dwFilteredAllowedResults)
2502 + switch (dwAllowedResults)
2503 {
2504 case MB_OK:
2505 nResult = IDOK;
@@ -2606,7 +2607,28 @@ static int FilterResult(
2607 }
2608 break;
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-
2632 if (IDOK == nResult || IDYES == nResult)
2633 {
2634 nResult = IDOK;
@@ -2615,11 +2637,15 @@ static int FilterResult(
2637 {
2638 nResult = IDIGNORE;
2639 }
2640 + else if (IDNO == nResult)
2641 + {
2642 + nResult = IDNO;
2643 + }
2644 else if (IDCANCEL == nResult || IDABORT == nResult)
2645 {
2646 nResult = IDCANCEL;
2647 }
2622 - else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult)
2648 + else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2649 {
2650 nResult = IDRETRY;
2651 }
@@ -2629,13 +2655,33 @@ static int FilterResult(
2655 }
2656 break;
2657
2632 - case MB_RETRYTRYAGAIN: // custom return code.
2658 + case BURN_MB_RETRYTRYAGAIN: // custom return code.
2659 if (IDRETRY != nResult && IDTRYAGAIN != nResult)
2660 {
2661 nResult = IDNOACTION;
2662 }
2663 break;
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 +
2685 default:
2686 AssertSz(FALSE, "Unknown allowed results.");
2687 break;
src/burn/engine/userexperience.h
+5 -1
@@ -10,7 +10,10 @@ extern "C" {
10
11 // constants
12
13 -const DWORD MB_RETRYTRYAGAIN = 0xF;
13 +const DWORD BURN_MB_RETRYTRYAGAIN = 0x10;
14 +const DWORD BURN_MB_MSI_FILES_IN_USE = 0x11;
15 +const DWORD BURN_MB_MSI_RM_FILES_IN_USE = 0x12;
16 +const DWORD BURN_MB_NETFX_FILES_IN_USE = 0x13;
17
18
19 // structs
@@ -356,6 +359,7 @@ BAAPI UserExperienceOnExecuteFilesInUse(
359 __in_z LPCWSTR wzPackageId,
360 __in DWORD cFiles,
361 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
362 + __in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
363 __inout int* pnResult
364 );
365 BAAPI UserExperienceOnExecuteMsiMessage(
src/internal/SetBuildNumber/Directory.Packages.props.pp
+1
@@ -29,6 +29,7 @@
29 <PackageVersion Include="WixToolset.Bal.wixext" Version="{packageversion}" />
30 <PackageVersion Include="WixToolset.Dependency.wixext" Version="{packageversion}" />
31 <PackageVersion Include="WixToolset.NetFx.wixext" Version="{packageversion}" />
32 + <PackageVersion Include="WixToolset.UI.wixext" Version="{packageversion}" />
33 <PackageVersion Include="WixToolset.Util.wixext" Version="{packageversion}" />
34 </ItemGroup>
35
src/libs/dutil/WixToolset.DUtil/inc/wiutil.h
+2 -2
@@ -9,7 +9,6 @@ extern "C" {
9 // constants
10
11 #define IDNOACTION 0
12 -#define WIU_MB_OKIGNORECANCELRETRY 0xE
12
13 #define MAX_DARWIN_KEY 73
14 #define MAX_DARWIN_COLUMN 255
@@ -37,6 +36,7 @@ typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE
36 WIU_MSI_EXECUTE_MESSAGE_ERROR,
37 WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE,
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;
41
42
@@ -45,7 +45,7 @@ typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE
45 typedef struct _WIU_MSI_EXECUTE_MESSAGE
46 {
47 WIU_MSI_EXECUTE_MESSAGE_TYPE type;
48 - DWORD dwAllowedResults;
48 + DWORD dwUIHint;
49
50 DWORD cData;
51 LPCWSTR* rgwzData;
src/libs/dutil/WixToolset.DUtil/wiutil.cpp
+9 -7
@@ -113,6 +113,7 @@ static INT SendErrorMessage(
113 );
114 static INT SendFilesInUseMessage(
115 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
116 + __in UINT uiFlags,
117 __in_opt MSIHANDLE hRecord,
118 __in BOOL fRestartManagerRequest
119 );
@@ -1161,7 +1162,7 @@ Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex,
1162
1163 case INSTALLMESSAGE_FILESINUSE:
1164 case INSTALLMESSAGE_RMFILESINUSE:
1164 - nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt);
1165 + nResult = SendFilesInUseMessage(pContext, uiFlags, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt);
1166 break;
1167
1168 /*
@@ -1401,7 +1402,7 @@ static INT SendMsiMessage(
1402 InitializeMessageData(hRecord, &rgsczData, &cData);
1403
1404 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE;
1404 - message.dwAllowedResults = uiFlags;
1405 + message.dwUIHint = uiFlags;
1406 message.cData = cData;
1407 message.rgwzData = (LPCWSTR*)rgsczData;
1408 message.msiMessage.mt = mt;
@@ -1445,7 +1446,7 @@ static INT SendErrorMessage(
1446 InitializeMessageData(hRecord, &rgsczData, &cData);
1447
1448 message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR;
1448 - message.dwAllowedResults = uiFlags;
1449 + message.dwUIHint = uiFlags;
1450 message.nResultRecommendation = nResult;
1451 message.cData = cData;
1452 message.rgwzData = (LPCWSTR*)rgsczData;
@@ -1459,8 +1460,9 @@ static INT SendErrorMessage(
1460
1461 static INT SendFilesInUseMessage(
1462 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1463 + __in UINT uiFlags,
1464 __in_opt MSIHANDLE hRecord,
1463 - __in BOOL /*fRestartManagerRequest*/
1465 + __in BOOL fRestartManagerRequest
1466 )
1467 {
1468 INT nResult = IDNOACTION;
@@ -1470,8 +1472,8 @@ static INT SendFilesInUseMessage(
1472
1473 InitializeMessageData(hRecord, &rgsczData, &cData);
1474
1473 - message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1474 - message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY;
1475 + message.type = fRestartManagerRequest ? WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE : WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1476 + message.dwUIHint = uiFlags;
1477 message.cData = cData;
1478 message.rgwzData = (LPCWSTR*)rgsczData;
1479 message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information.
@@ -1527,7 +1529,7 @@ static INT SendProgressUpdate(
1529 #endif
1530
1531 message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS;
1530 - message.dwAllowedResults = MB_OKCANCEL;
1532 + message.dwUIHint = MB_OKCANCEL;
1533 message.progress.dwPercentage = dwPercentage;
1534 nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1535
src/test/burn/TestBA/TestBA.cs
+1 -1
@@ -399,7 +399,7 @@ namespace WixToolset.Test.BA
399
400 protected override void OnExecuteFilesInUse(ExecuteFilesInUseEventArgs args)
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
404 if (this.retryExecuteFilesInUse > 0)
405 {
src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wixproj new
+18
@@ -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
src/test/burn/TestData/FilesInUseTests/BundleA/BundleA.wxs new
+10
@@ -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>
src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wixproj new
+13
@@ -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
src/test/burn/TestData/FilesInUseTests/PackageA/PackageA.wxs new
+10
@@ -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>
src/test/burn/TestData/FilesInUseTests/PackageA/license.txt new
+1
@@ -0,0 +1 @@
1 +a
\ No newline at end of file
src/test/burn/WixToolsetTest.BurnE2E/FilesInUseTests.cs new
+38
@@ -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 +
3 +namespace 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 +}
src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs
+10
@@ -117,6 +117,16 @@ namespace WixToolsetTest.BurnE2E
117 this.SetPackageState(packageId, "CancelOnProgressAtProgress", cancelPoint.HasValue ? cancelPoint.ToString() : null);
118 }
119
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>
131 /// Sets the requested state for a package that the TestBA will return to the engine during plan.
132 /// </summary>