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>