Give BA process id and option to wait for cancelled process to exit.
Sean Hall committed
Mar 18, 2022 at 20:15 UTC
fb54576f1d05e82ba47cd718c4c4f8b3bad624c9
31 files changed
+716
-124
src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+27
@@ -224,6 +224,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
224
BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
225
BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
226
BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
227
+ BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL,
228
};
229
230
enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -282,6 +283,18 @@ enum BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION
283
BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND,
284
};
285
286
+enum BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION
287
+{
288
+ // Instructs the engine to stop waiting for the process to exit.
289
+ // The package is immediately considered to have failed with ERROR_INSTALL_USEREXIT.
290
+ // The engine will never rollback the package.
291
+ BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION_ABANDON,
292
+ // Instructs the engine to wait for the process to exit.
293
+ // Once the process has exited, the package is considered to have failed with ERROR_INSTALL_USEREXIT.
294
+ // This allows the engine to rollback the package if necessary.
295
+ BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION_WAIT,
296
+};
297
+
298
enum BOOTSTRAPPER_SHUTDOWN_ACTION
299
{
300
BOOTSTRAPPER_SHUTDOWN_ACTION_NONE,
@@ -997,6 +1010,20 @@ struct BA_ONEXECUTEPATCHTARGET_RESULTS
1010
BOOL fCancel;
1011
};
1012
1013
+struct BA_ONEXECUTEPROCESSCANCEL_ARGS
1014
+{
1015
+ DWORD cbSize;
1016
+ LPCWSTR wzPackageId;
1017
+ DWORD dwProcessId;
1018
+ BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation;
1019
+};
1020
+
1021
+struct BA_ONEXECUTEPROCESSCANCEL_RESULTS
1022
+{
1023
+ DWORD cbSize;
1024
+ BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION action;
1025
+};
1026
+
1027
struct BA_ONEXECUTEPROGRESS_ARGS
1028
{
1029
DWORD cbSize;
src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+25
@@ -280,6 +280,9 @@ namespace WixToolset.Mba.Core
280
/// <inheritdoc/>
281
public event EventHandler<PlanRestoreRelatedBundleEventArgs> PlanRestoreRelatedBundle;
282
283
+ /// <inheritdoc/>
284
+ public event EventHandler<ExecuteProcessCancelEventArgs> ExecuteProcessCancel;
285
+
286
/// <summary>
287
/// Entry point that is called when the bootstrapper application is ready to run.
288
/// </summary>
@@ -1369,6 +1372,19 @@ namespace WixToolset.Mba.Core
1372
}
1373
}
1374
1375
+ /// <summary>
1376
+ /// Called by the engine, raises the <see cref="ExecuteProcessCancel"/> event.
1377
+ /// </summary>
1378
+ /// <param name="args">Additional arguments for this event.</param>
1379
+ protected virtual void OnExecuteProcessCancel(ExecuteProcessCancelEventArgs args)
1380
+ {
1381
+ EventHandler<ExecuteProcessCancelEventArgs> handler = this.ExecuteProcessCancel;
1382
+ if (null != handler)
1383
+ {
1384
+ handler(this, args);
1385
+ }
1386
+ }
1387
+
1388
#region IBootstrapperApplication Members
1389
1390
int IBootstrapperApplication.BAProc(int message, IntPtr pvArgs, IntPtr pvResults, IntPtr pvContext)
@@ -2119,6 +2135,15 @@ namespace WixToolset.Mba.Core
2135
return args.HResult;
2136
}
2137
2138
+ int IBootstrapperApplication.OnExecuteProcessCancel(string wzPackageId, int processId, BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation, ref BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION pAction)
2139
+ {
2140
+ ExecuteProcessCancelEventArgs args = new ExecuteProcessCancelEventArgs(wzPackageId, processId, recommendation, pAction);
2141
+ this.OnExecuteProcessCancel(args);
2142
+
2143
+ pAction = args.Action;
2144
+ return args.HResult;
2145
+ }
2146
+
2147
#endregion
2148
}
2149
}
src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+36
@@ -2488,4 +2488,40 @@ namespace WixToolset.Mba.Core
2488
/// </summary>
2489
public RequestState State { get; set; }
2490
}
2491
+
2492
+ /// <summary>
2493
+ /// Event arguments for <see cref="IDefaultBootstrapperApplication.ExecuteProcessCancel"/>
2494
+ /// </summary>
2495
+ [Serializable]
2496
+ public class ExecuteProcessCancelEventArgs : HResultEventArgs
2497
+ {
2498
+ /// <summary />
2499
+ public ExecuteProcessCancelEventArgs(string packageId, int processId, BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation, BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION action)
2500
+ {
2501
+ this.PackageId = packageId;
2502
+ this.ProcessId = processId;
2503
+ this.Recommendation = recommendation;
2504
+ this.Action = action;
2505
+ }
2506
+
2507
+ /// <summary>
2508
+ /// Gets the identity of the package.
2509
+ /// </summary>
2510
+ public string PackageId { get; private set; }
2511
+
2512
+ /// <summary>
2513
+ /// Gets the process id.
2514
+ /// </summary>
2515
+ public int ProcessId { get; private set; }
2516
+
2517
+ /// <summary>
2518
+ /// Gets the recommended action from the engine.
2519
+ /// </summary>
2520
+ public BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION Recommendation { get; private set; }
2521
+
2522
+ /// <summary>
2523
+ /// Gets or sets the action to be performed. This is passed back to the engine.
2524
+ /// </summary>
2525
+ public BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION Action { get; set; }
2526
+ }
2527
}
src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+32
@@ -1170,6 +1170,18 @@ namespace WixToolset.Mba.Core
1170
[MarshalAs(UnmanagedType.I4)] int hrRecommended,
1171
[MarshalAs(UnmanagedType.I4)] ref int hrStatus
1172
);
1173
+
1174
+ /// <summary>
1175
+ /// See <see cref="IDefaultBootstrapperApplication.ExecuteProcessCancel"/>.
1176
+ /// </summary>
1177
+ [PreserveSig]
1178
+ [return: MarshalAs(UnmanagedType.I4)]
1179
+ int OnExecuteProcessCancel(
1180
+ [MarshalAs(UnmanagedType.LPWStr)] string wzPackageId,
1181
+ int processId,
1182
+ [MarshalAs(UnmanagedType.I4)] BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation,
1183
+ [MarshalAs(UnmanagedType.I4)] ref BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION pAction
1184
+ );
1185
}
1186
1187
/// <summary>
@@ -1906,6 +1918,26 @@ namespace WixToolset.Mba.Core
1918
Suspend,
1919
}
1920
1921
+ /// <summary>
1922
+ /// The available actions for <see cref="IDefaultBootstrapperApplication.ExecuteProcessCancel"/>.
1923
+ /// </summary>
1924
+ public enum BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION
1925
+ {
1926
+ /// <summary>
1927
+ /// Instructs the engine to stop waiting for the process to exit.
1928
+ /// The package is immediately considered to have failed with ERROR_INSTALL_USEREXIT.
1929
+ /// The engine will never rollback the package.
1930
+ /// </summary>
1931
+ Abandon,
1932
+
1933
+ /// <summary>
1934
+ /// Instructs the engine to wait for the process to exit.
1935
+ /// Once the process has exited, the package is considered to have failed with ERROR_INSTALL_USEREXIT.
1936
+ /// This allows the engine to rollback the package if necessary.
1937
+ /// </summary>
1938
+ Wait,
1939
+ }
1940
+
1941
/// <summary>
1942
/// The result of evaluating a condition from a package.
1943
/// </summary>
src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
+5
@@ -243,6 +243,11 @@ namespace WixToolset.Mba.Core
243
/// </summary>
244
event EventHandler<ExecutePackageCompleteEventArgs> ExecutePackageComplete;
245
246
+ /// <summary>
247
+ /// Fired when a package that spawned a process is cancelled.
248
+ /// </summary>
249
+ event EventHandler<ExecuteProcessCancelEventArgs> ExecuteProcessCancel;
250
+
251
/// <summary>
252
/// Fired when the engine executes one or more patches targeting a product.
253
/// </summary>
src/api/burn/balutil/inc/BAFunctions.h
+1
@@ -91,6 +91,7 @@ enum BA_FUNCTIONS_MESSAGE
91
BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
92
BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
93
BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
94
+ BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL = BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL,
95
96
BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
97
BA_FUNCTIONS_MESSAGE_WNDPROC,
src/api/burn/balutil/inc/BalBaseBAFunctions.h
+10
@@ -877,6 +877,16 @@ public: // IBootstrapperApplication
877
return S_OK;
878
}
879
880
+ virtual STDMETHODIMP OnExecuteProcessCancel(
881
+ __in_z LPCWSTR /*wzPackageId*/,
882
+ __in DWORD /*dwProcessId*/,
883
+ __in BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION /*recommendation*/,
884
+ __inout BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION* /*pAction*/
885
+ )
886
+ {
887
+ return S_OK;
888
+ }
889
+
890
public: // IBAFunctions
891
virtual STDMETHODIMP OnPlan(
892
)
src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+1
@@ -162,6 +162,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
162
case BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE:
163
case BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE:
164
case BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE:
165
+ case BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL:
166
hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
167
break;
168
case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+10
@@ -1077,6 +1077,16 @@ public: // IBootstrapperApplication
1077
return S_OK;
1078
}
1079
1080
+ virtual STDMETHODIMP OnExecuteProcessCancel(
1081
+ __in_z LPCWSTR /*wzPackageId*/,
1082
+ __in DWORD /*dwProcessId*/,
1083
+ __in BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION /*recommendation*/,
1084
+ __inout BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION* /*pAction*/
1085
+ )
1086
+ {
1087
+ return S_OK;
1088
+ }
1089
+
1090
public: //CBalBaseBootstrapperApplication
1091
virtual STDMETHODIMP Initialize(
1092
__in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs
src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+12
@@ -486,6 +486,15 @@ static HRESULT BalBaseBAProcOnExecutePackageComplete(
486
return pBA->OnExecutePackageComplete(pArgs->wzPackageId, pArgs->hrStatus, pArgs->restart, pArgs->recommendation, &pResults->action);
487
}
488
489
+static HRESULT BalBaseBAProcOnExecuteProcessCancel(
490
+ __in IBootstrapperApplication* pBA,
491
+ __in BA_ONEXECUTEPROCESSCANCEL_ARGS* pArgs,
492
+ __inout BA_ONEXECUTEPROCESSCANCEL_RESULTS* pResults
493
+ )
494
+{
495
+ return pBA->OnExecuteProcessCancel(pArgs->wzPackageId, pArgs->dwProcessId, pArgs->recommendation, &pResults->action);
496
+}
497
+
498
static HRESULT BalBaseBAProcOnExecuteComplete(
499
__in IBootstrapperApplication* pBA,
500
__in BA_ONEXECUTECOMPLETE_ARGS* pArgs,
@@ -1012,6 +1021,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
1021
case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE:
1022
hr = BalBaseBAProcOnApplyDowngrade(pBA, reinterpret_cast<BA_ONAPPLYDOWNGRADE_ARGS*>(pvArgs), reinterpret_cast<BA_ONAPPLYDOWNGRADE_RESULTS*>(pvResults));
1023
break;
1024
+ case BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL:
1025
+ hr = BalBaseBAProcOnExecuteProcessCancel(pBA, reinterpret_cast<BA_ONEXECUTEPROCESSCANCEL_ARGS*>(pvArgs), reinterpret_cast<BA_ONEXECUTEPROCESSCANCEL_RESULTS*>(pvResults));
1026
+ break;
1027
}
1028
}
1029
src/api/burn/balutil/inc/IBootstrapperApplication.h
+9
@@ -714,4 +714,13 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
714
__in HRESULT hrRecommended,
715
__inout HRESULT* phrStatus
716
) = 0;
717
+
718
+ // OnExecuteProcessCancel - called when a package that spawned a process is cancelled.
719
+ //
720
+ STDMETHOD(OnExecuteProcessCancel)(
721
+ __in_z LPCWSTR wzPackageId,
722
+ __in DWORD dwProcessId,
723
+ __in BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation,
724
+ __inout BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION* pAction
725
+ ) = 0;
726
};
src/burn/engine/apply.cpp
+8
@@ -3326,6 +3326,14 @@ static int GenericExecuteMessageHandler(
3326
}
3327
break;
3328
3329
+ case GENERIC_EXECUTE_MESSAGE_PROCESS_CANCEL:
3330
+ {
3331
+ BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION action = BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION_ABANDON;
3332
+ UserExperienceOnExecuteProcessCancel(pContext->pUX, pContext->wzExecutingPackageId, pMessage->processCancel.dwProcessId, &action); // ignore return value.
3333
+ nResult = BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION_WAIT == action ? IDRETRY : IDIGNORE;
3334
+ }
3335
+ break;
3336
+
3337
case GENERIC_EXECUTE_MESSAGE_ERROR:
3338
UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->wzExecutingPackageId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, 0, NULL, &nResult); // ignore return value.
3339
break;
src/burn/engine/apply.h
+5
@@ -13,6 +13,7 @@ enum GENERIC_EXECUTE_MESSAGE_TYPE
13
GENERIC_EXECUTE_MESSAGE_ERROR,
14
GENERIC_EXECUTE_MESSAGE_PROGRESS,
15
GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE,
16
+ GENERIC_EXECUTE_MESSAGE_PROCESS_CANCEL,
17
};
18
19
typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
@@ -43,6 +44,10 @@ typedef struct _GENERIC_EXECUTE_MESSAGE
44
DWORD cFiles;
45
LPCWSTR* rgwzFiles;
46
} filesInUse;
47
+ struct
48
+ {
49
+ DWORD dwProcessId;
50
+ } processCancel;
51
};
52
} GENERIC_EXECUTE_MESSAGE;
53
src/burn/engine/bundlepackageengine.cpp
+3
-25
@@ -251,7 +251,6 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
251
)
252
{
253
HRESULT hr = S_OK;
254
- int nResult = IDNOACTION;
254
LPCWSTR wzArguments = NULL;
255
LPWSTR sczArguments = NULL;
256
LPWSTR sczArgumentsFormatted = NULL;
@@ -420,31 +419,10 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
419
hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
420
ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath);
421
}
423
- else // create and wait for the executable process while sending fake progress to allow cancel.
422
+ else
423
{
425
- // Make the cache location of the executable the current directory to help those executables
426
- // that expect stuff to be relative to them.
427
- si.cb = sizeof(si);
428
- if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, sczCachedDirectory, &si, &pi))
429
- {
430
- ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath);
431
- }
432
-
433
- do
434
- {
435
- message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
436
- message.dwUIHint = MB_OKCANCEL;
437
- message.progress.dwPercentage = 50;
438
- nResult = pfnGenericMessageHandler(&message, pvContext);
439
- hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
440
- ExitOnRootFailure(hr, "Bootstrapper application aborted during BUNDLE progress.");
441
-
442
- hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
443
- if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
444
- {
445
- ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath);
446
- }
447
- } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
424
+ hr = ExeEngineRunProcess(pfnGenericMessageHandler, pvContext, pPackage, sczExecutablePath, sczCommand, sczCachedDirectory, &dwExitCode);
425
+ ExitOnFailure(hr, "Failed to run BUNDLE process");
426
}
427
428
hr = ExeEngineHandleExitCode(pPackage->Bundle.rgExitCodes, pPackage->Bundle.cExitCodes, dwExitCode, pRestart);
src/burn/engine/elevation.cpp
+16
-1
@@ -43,6 +43,7 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE
43
BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE,
44
BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS,
45
BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS,
46
+ BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_CANCEL,
47
BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR,
48
BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE,
49
BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_FILES_IN_USE,
@@ -1812,7 +1813,14 @@ static HRESULT ProcessGenericExecuteMessages(
1813
1814
// read message parameters
1815
hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage);
1815
- ExitOnFailure(hr, "Failed to progress.");
1816
+ ExitOnFailure(hr, "Failed to read progress.");
1817
+ break;
1818
+
1819
+ case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_CANCEL:
1820
+ message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_CANCEL;
1821
+
1822
+ hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.processCancel.dwProcessId);
1823
+ ExitOnFailure(hr, "Failed to read processId.");
1824
break;
1825
1826
case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR:
@@ -3450,6 +3458,13 @@ static int GenericExecuteMessageHandler(
3458
dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS;
3459
break;
3460
3461
+ case GENERIC_EXECUTE_MESSAGE_PROCESS_CANCEL:
3462
+ hr = BuffWriteNumber(&pbData, &cbData, pMessage->processCancel.dwProcessId);
3463
+ ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
3464
+
3465
+ dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_CANCEL;
3466
+ break;
3467
+
3468
case GENERIC_EXECUTE_MESSAGE_ERROR:
3469
// serialize message data
3470
hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode);
src/burn/engine/engine.mc
+7
@@ -933,6 +933,13 @@ Language=English
933
Could not create system restore point, error: 0x%1!x!. Continuing...
934
.
935
936
+MessageId=364
937
+Severity=Success
938
+SymbolicName=MSG_EXECUTE_PROCESS_DELAYED_CANCEL_REQUESTED
939
+Language=English
940
+Bootstrapper application requested delayed cancel during package process progress, id: %1!ls!. Waiting...
941
+.
942
+
943
MessageId=370
944
Severity=Success
945
SymbolicName=MSG_SESSION_BEGIN
src/burn/engine/exeengine.cpp
+96
-36
@@ -317,7 +317,6 @@ extern "C" HRESULT ExeEngineExecutePackage(
317
)
318
{
319
HRESULT hr = S_OK;
320
- int nResult = IDNOACTION;
320
LPCWSTR wzArguments = NULL;
321
LPWSTR sczArguments = NULL;
322
LPWSTR sczArgumentsFormatted = NULL;
@@ -327,10 +326,7 @@ extern "C" HRESULT ExeEngineExecutePackage(
326
LPWSTR sczCommand = NULL;
327
LPWSTR sczCommandObfuscated = NULL;
328
HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
330
- STARTUPINFOW si = { };
331
- PROCESS_INFORMATION pi = { };
329
DWORD dwExitCode = 0;
333
- GENERIC_EXECUTE_MESSAGE message = { };
330
BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage;
331
BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
332
@@ -442,37 +438,10 @@ extern "C" HRESULT ExeEngineExecutePackage(
438
hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
439
ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath);
440
}
445
- else // create and wait for the executable process while sending fake progress to allow cancel.
441
+ else
442
{
447
- // Make the cache location of the executable the current directory to help those executables
448
- // that expect stuff to be relative to them.
449
- si.cb = sizeof(si);
450
- if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, sczCachedDirectory, &si, &pi))
451
- {
452
- ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath);
453
- }
454
-
455
- if (pPackage->Exe.fFireAndForget)
456
- {
457
- ::WaitForInputIdle(pi.hProcess, 5000);
458
- ExitFunction();
459
- }
460
-
461
- do
462
- {
463
- message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
464
- message.dwUIHint = MB_OKCANCEL;
465
- message.progress.dwPercentage = 50;
466
- nResult = pfnGenericMessageHandler(&message, pvContext);
467
- hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
468
- ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress.");
469
-
470
- hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
471
- if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
472
- {
473
- ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath);
474
- }
475
- } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
443
+ hr = ExeEngineRunProcess(pfnGenericMessageHandler, pvContext, pPackage, sczExecutablePath, sczCommand, sczCachedDirectory, &dwExitCode);
444
+ ExitOnFailure(hr, "Failed to run EXE process");
445
}
446
447
hr = ExeEngineHandleExitCode(pPackage->Exe.rgExitCodes, pPackage->Exe.cExitCodes, dwExitCode, pRestart);
@@ -487,8 +456,6 @@ LExit:
456
StrSecureZeroFreeString(sczCommand);
457
ReleaseStr(sczCommandObfuscated);
458
490
- ReleaseHandle(pi.hThread);
491
- ReleaseHandle(pi.hProcess);
459
ReleaseFileHandle(hExecutableFile);
460
461
// Best effort to clear the execute package cache folder and action variables.
@@ -498,6 +465,99 @@ LExit:
465
return hr;
466
}
467
468
+extern "C" HRESULT ExeEngineRunProcess(
469
+ __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
470
+ __in LPVOID pvContext,
471
+ __in BURN_PACKAGE* pPackage,
472
+ __in_z LPCWSTR wzExecutablePath,
473
+ __in_z LPWSTR wzCommand,
474
+ __in_z_opt LPCWSTR wzCachedDirectory,
475
+ __inout DWORD* pdwExitCode
476
+ )
477
+{
478
+ HRESULT hr = S_OK;
479
+ STARTUPINFOW si = { };
480
+ PROCESS_INFORMATION pi = { };
481
+ GENERIC_EXECUTE_MESSAGE message = { };
482
+ int nResult = IDNOACTION;
483
+ DWORD dwProcessId = 0;
484
+ BOOL fDelayedCancel = FALSE;
485
+ BOOL fFireAndForget = BURN_PACKAGE_TYPE_EXE == pPackage->type && pPackage->Exe.fFireAndForget;
486
+ BOOL fInheritHandles = BURN_PACKAGE_TYPE_BUNDLE == pPackage->type;
487
+
488
+ // Make the cache location of the executable the current directory to help those executables
489
+ // that expect stuff to be relative to them.
490
+ si.cb = sizeof(si);
491
+ if (!::CreateProcessW(wzExecutablePath, wzCommand, NULL, NULL, fInheritHandles, CREATE_NO_WINDOW, NULL, wzCachedDirectory, &si, &pi))
492
+ {
493
+ ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
494
+ }
495
+
496
+ if (fFireAndForget)
497
+ {
498
+ ::WaitForInputIdle(pi.hProcess, 5000);
499
+ ExitFunction();
500
+ }
501
+
502
+ dwProcessId = ::GetProcessId(pi.hProcess);
503
+
504
+ // Wait for the executable process while sending fake progress to allow cancel.
505
+ do
506
+ {
507
+ message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
508
+ message.dwUIHint = MB_OKCANCEL;
509
+ message.progress.dwPercentage = 50;
510
+ nResult = pfnGenericMessageHandler(&message, pvContext);
511
+
512
+ if (IDCANCEL == nResult)
513
+ {
514
+ memset(&message, 0, sizeof(message));
515
+ message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_CANCEL;
516
+ message.dwUIHint = MB_ABORTRETRYIGNORE;
517
+ message.processCancel.dwProcessId = dwProcessId;
518
+ nResult = pfnGenericMessageHandler(&message, pvContext);
519
+
520
+ if (IDIGNORE == nResult) // abandon
521
+ {
522
+ nResult = IDCANCEL;
523
+ fDelayedCancel = FALSE;
524
+ }
525
+ //else if (IDABORT == nResult) // kill
526
+ else // wait
527
+ {
528
+ if (!fDelayedCancel)
529
+ {
530
+ fDelayedCancel = TRUE;
531
+
532
+ LogId(REPORT_STANDARD, MSG_EXECUTE_PROCESS_DELAYED_CANCEL_REQUESTED, pPackage->sczId);
533
+ }
534
+
535
+ nResult = IDNOACTION;
536
+ }
537
+ }
538
+
539
+ hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
540
+ ExitOnRootFailure(hr, "Bootstrapper application aborted during package process progress.");
541
+
542
+ hr = ProcWaitForCompletion(pi.hProcess, 500, pdwExitCode);
543
+ if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
544
+ {
545
+ ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", wzExecutablePath);
546
+ }
547
+ } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
548
+
549
+ if (fDelayedCancel)
550
+ {
551
+ ExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT), "Bootstrapper application cancelled during package process progress, exit code: 0x%x", *pdwExitCode);
552
+ }
553
+
554
+LExit:
555
+ ReleaseHandle(pi.hThread);
556
+ ReleaseHandle(pi.hProcess);
557
+
558
+ return hr;
559
+}
560
+
561
extern "C" void ExeEngineUpdateInstallRegistrationState(
562
__in BURN_EXECUTE_ACTION* pAction,
563
__in HRESULT hrExecute
src/burn/engine/exeengine.h
+9
@@ -42,6 +42,15 @@ HRESULT ExeEngineExecutePackage(
42
__in LPVOID pvContext,
43
__out BOOTSTRAPPER_APPLY_RESTART* pRestart
44
);
45
+HRESULT ExeEngineRunProcess(
46
+ __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
47
+ __in LPVOID pvContext,
48
+ __in BURN_PACKAGE* pPackage,
49
+ __in_z LPCWSTR wzExecutablePath,
50
+ __in_z LPWSTR wzCommand,
51
+ __in_z_opt LPCWSTR wzCachedDirectory,
52
+ __inout DWORD* pdwExitCode
53
+ );
54
void ExeEngineUpdateInstallRegistrationState(
55
__in BURN_EXECUTE_ACTION* pAction,
56
__in HRESULT hrExecute
src/burn/engine/msuengine.cpp
+2
-30
@@ -264,7 +264,6 @@ extern "C" HRESULT MsuEngineExecutePackage(
264
)
265
{
266
HRESULT hr = S_OK;
267
- int nResult = IDNOACTION;
267
LPWSTR sczCachedDirectory = NULL;
268
LPWSTR sczMsuPath = NULL;
269
LPWSTR sczWindowsPath = NULL;
@@ -350,35 +349,8 @@ extern "C" HRESULT MsuEngineExecutePackage(
349
hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled);
350
ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package.");
351
353
- // create process
354
- si.cb = sizeof(si);
355
- if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
356
- {
357
- ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath);
358
- }
359
-
360
- do
361
- {
362
- message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
363
- message.dwUIHint = MB_OKCANCEL;
364
- message.progress.dwPercentage = 50;
365
- nResult = pfnGenericMessageHandler(&message, pvContext);
366
- hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
367
- ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress.");
368
-
369
- // wait for process to terminate
370
- hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
371
- if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
372
- {
373
- ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath);
374
- }
375
- } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
376
-
377
- // get process exit code
378
- if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode))
379
- {
380
- ExitWithLastError(hr, "Failed to get process exit code.");
381
- }
352
+ hr = ExeEngineRunProcess(pfnGenericMessageHandler, pvContext, pPackage, sczWusaPath, sczCommand, NULL, &dwExitCode);
353
+ ExitOnFailure(hr, "Failed to run MSU process");
354
355
// We'll normalize the restart required error code from wusa.exe just in case. Most likely
356
// that on reboot we'll actually get WU_S_REBOOT_REQUIRED.
src/burn/engine/userexperience.cpp
+29
-1
@@ -104,7 +104,7 @@ extern "C" HRESULT UserExperienceLoad(
104
args.pCommand = pCommand;
105
args.pfnBootstrapperEngineProc = EngineForApplicationProc;
106
args.pvBootstrapperEngineProcContext = pEngineContext;
107
- args.qwEngineAPIVersion = MAKEQWORDVERSION(2022, 3, 14, 0);
107
+ args.qwEngineAPIVersion = MAKEQWORDVERSION(2022, 3, 17, 0);
108
109
results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
110
@@ -1701,6 +1701,34 @@ LExit:
1701
return hr;
1702
}
1703
1704
+BAAPI UserExperienceOnExecuteProcessCancel(
1705
+ __in BURN_USER_EXPERIENCE* pUserExperience,
1706
+ __in_z LPCWSTR wzPackageId,
1707
+ __in DWORD dwProcessId,
1708
+ __inout BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION* pAction
1709
+ )
1710
+{
1711
+ HRESULT hr = S_OK;
1712
+ BA_ONEXECUTEPROCESSCANCEL_ARGS args = { };
1713
+ BA_ONEXECUTEPROCESSCANCEL_RESULTS results = { };
1714
+
1715
+ args.cbSize = sizeof(args);
1716
+ args.wzPackageId = wzPackageId;
1717
+ args.dwProcessId = dwProcessId;
1718
+ args.recommendation = *pAction;
1719
+
1720
+ results.cbSize = sizeof(results);
1721
+ results.action = *pAction;
1722
+
1723
+ hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL, &args, &results);
1724
+ ExitOnFailure(hr, "BA OnExecuteProcessCancel failed.");
1725
+
1726
+ *pAction = results.action;
1727
+
1728
+LExit:
1729
+ return hr;
1730
+}
1731
+
1732
EXTERN_C BAAPI UserExperienceOnExecuteProgress(
1733
__in BURN_USER_EXPERIENCE* pUserExperience,
1734
__in_z LPCWSTR wzPackageId,
src/burn/engine/userexperience.h
+6
@@ -398,6 +398,12 @@ BAAPI UserExperienceOnExecutePatchTarget(
398
__in_z LPCWSTR wzPackageId,
399
__in_z LPCWSTR wzTargetProductCode
400
);
401
+BAAPI UserExperienceOnExecuteProcessCancel(
402
+ __in BURN_USER_EXPERIENCE* pUserExperience,
403
+ __in_z LPCWSTR wzPackageId,
404
+ __in DWORD dwProcessId,
405
+ __inout BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION* pAction
406
+ );
407
BAAPI UserExperienceOnExecuteProgress(
408
__in BURN_USER_EXPERIENCE* pUserExperience,
409
__in_z LPCWSTR wzPackageId,
src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
+2
@@ -80,7 +80,9 @@
80
<ItemGroup>
81
<None Include="TestData\CacheTest\CacheSignatureTest.File" CopyToOutputDirectory="PreserveNewest" />
82
<None Include="TestData\PlanTest\BasicFunctionality_BundleA_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
83
+ <None Include="TestData\PlanTest\Failure_BundleD_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
84
<None Include="TestData\PlanTest\MsiTransaction_BundleAv1_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
85
+ <None Include="TestData\PlanTest\MsuPackageFixture_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
86
<None Include="TestData\PlanTest\Slipstream_BundleA_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
87
<None Include="TestData\PlanTest\Slipstream_BundleA_modified_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
88
</ItemGroup>
src/burn/test/BurnUnitTest/PlanTest.cpp
+200
@@ -10,7 +10,9 @@ static HRESULT WINAPI PlanTestBAProc(
10
);
11
12
static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml";
13
+static LPCWSTR wzSingleExeManifestFileName = L"Failure_BundleD_manifest.xml";
14
static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml";
15
+static LPCWSTR wzSingleMsuManifestFileName = L"MsuPackageFixture_manifest.xml";
16
static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml";
17
static LPCWSTR wzSlipstreamModifiedManifestFileName = L"Slipstream_BundleA_modified_manifest.xml";
18
@@ -658,6 +660,97 @@ namespace Bootstrapper
660
ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
661
}
662
663
+ [Fact]
664
+ void SingleExeInstallTest()
665
+ {
666
+ HRESULT hr = S_OK;
667
+ BURN_ENGINE_STATE engineState = { };
668
+ BURN_ENGINE_STATE* pEngineState = &engineState;
669
+ BURN_PLAN* pPlan = &engineState.plan;
670
+
671
+ InitializeEngineStateForCorePlan(wzSingleExeManifestFileName, pEngineState);
672
+ DetectAttachedContainerAsAttached(pEngineState);
673
+ DetectPermanentPackagesAsPresentAndCached(pEngineState);
674
+
675
+ hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL);
676
+ NativeAssert::Succeeded(hr, "CorePlan failed");
677
+
678
+ Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action);
679
+ NativeAssert::StringEqual(L"{9C184683-04FB-49AD-9D79-65101BDC3EE3}", pPlan->wzBundleId);
680
+ NativeAssert::StringEqual(L"{9C184683-04FB-49AD-9D79-65101BDC3EE3}", pPlan->wzBundleProviderKey);
681
+ Assert::Equal<BOOL>(FALSE, pPlan->fEnabledForwardCompatibleBundle);
682
+ Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
683
+ Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
684
+ Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
685
+ Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
686
+ Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
687
+
688
+ BOOL fRollback = FALSE;
689
+ DWORD dwIndex = 0;
690
+ ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, TRUE, L"{9C184683-04FB-49AD-9D79-65101BDC3EE3}", L"{9C184683-04FB-49AD-9D79-65101BDC3EE3}");
691
+ Assert::Equal(dwIndex, pPlan->cRegistrationActions);
692
+
693
+ fRollback = TRUE;
694
+ dwIndex = 0;
695
+ ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, FALSE, L"{9C184683-04FB-49AD-9D79-65101BDC3EE3}", L"{9C184683-04FB-49AD-9D79-65101BDC3EE3}");
696
+ Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions);
697
+
698
+ fRollback = FALSE;
699
+ dwIndex = 0;
700
+ ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
701
+ ValidateCachePackage(pPlan, fRollback, dwIndex++, L"ExeA");
702
+ ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
703
+ Assert::Equal(dwIndex, pPlan->cCacheActions);
704
+
705
+ fRollback = TRUE;
706
+ dwIndex = 0;
707
+ Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
708
+
709
+ Assert::Equal(1463267ull, pPlan->qwEstimatedSize);
710
+ Assert::Equal(119695ull, pPlan->qwCacheSizeTotal);
711
+
712
+ fRollback = FALSE;
713
+ dwIndex = 0;
714
+ DWORD dwExecuteCheckpointId = 2;
715
+ ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
716
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
717
+ ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"ExeA");
718
+ ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_INSTALL);
719
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
720
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
721
+ ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
722
+ Assert::Equal(dwIndex, pPlan->cExecuteActions);
723
+
724
+ fRollback = TRUE;
725
+ dwIndex = 0;
726
+ dwExecuteCheckpointId = 2;
727
+ ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
728
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
729
+ ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL);
730
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
731
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
732
+ ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
733
+ Assert::Equal(dwIndex, pPlan->cRollbackActions);
734
+
735
+ Assert::Equal(1ul, pPlan->cExecutePackagesTotal);
736
+ Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
737
+
738
+ dwIndex = 0;
739
+ Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
740
+
741
+ dwIndex = 0;
742
+ ValidateCleanAction(pPlan, dwIndex++, L"NetFx48Web");
743
+ ValidateCleanAction(pPlan, dwIndex++, L"ExeA");
744
+ Assert::Equal(dwIndex, pPlan->cCleanActions);
745
+
746
+ UINT uIndex = 0;
747
+ ValidatePlannedProvider(pPlan, uIndex++, L"{9C184683-04FB-49AD-9D79-65101BDC3EE3}", NULL);
748
+ Assert::Equal(uIndex, pPlan->cPlannedProviders);
749
+
750
+ Assert::Equal(2ul, pEngineState->packages.cPackages);
751
+ ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"ExeA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
752
+ }
753
+
754
[Fact]
755
void SingleMsiCacheTest()
756
{
@@ -1532,6 +1625,98 @@ namespace Bootstrapper
1625
ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
1626
}
1627
1628
+ [Fact]
1629
+ void SingleMsuInstallTest()
1630
+ {
1631
+ HRESULT hr = S_OK;
1632
+ BURN_ENGINE_STATE engineState = { };
1633
+ BURN_ENGINE_STATE* pEngineState = &engineState;
1634
+ BURN_PLAN* pPlan = &engineState.plan;
1635
+
1636
+ InitializeEngineStateForCorePlan(wzSingleMsuManifestFileName, pEngineState);
1637
+ DetectAttachedContainerAsAttached(pEngineState);
1638
+ DetectPackagesAsAbsent(pEngineState);
1639
+
1640
+ hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL);
1641
+ NativeAssert::Succeeded(hr, "CorePlan failed");
1642
+
1643
+ Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action);
1644
+ NativeAssert::StringEqual(L"{06077C60-DC46-4F4A-8D3C-05F869187191}", pPlan->wzBundleId);
1645
+ NativeAssert::StringEqual(L"{06077C60-DC46-4F4A-8D3C-05F869187191}", pPlan->wzBundleProviderKey);
1646
+ Assert::Equal<BOOL>(FALSE, pPlan->fEnabledForwardCompatibleBundle);
1647
+ Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
1648
+ Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1649
+ Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1650
+ Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1651
+ Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1652
+
1653
+ BOOL fRollback = FALSE;
1654
+ DWORD dwIndex = 0;
1655
+ ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, TRUE, L"{06077C60-DC46-4F4A-8D3C-05F869187191}", L"{06077C60-DC46-4F4A-8D3C-05F869187191}");
1656
+ Assert::Equal(dwIndex, pPlan->cRegistrationActions);
1657
+
1658
+ fRollback = TRUE;
1659
+ dwIndex = 0;
1660
+ ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, FALSE, L"{06077C60-DC46-4F4A-8D3C-05F869187191}", L"{06077C60-DC46-4F4A-8D3C-05F869187191}");
1661
+ Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions);
1662
+
1663
+ fRollback = FALSE;
1664
+ dwIndex = 0;
1665
+ ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
1666
+ ValidateCachePackage(pPlan, fRollback, dwIndex++, L"test.msu");
1667
+ ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
1668
+ Assert::Equal(dwIndex, pPlan->cCacheActions);
1669
+
1670
+ fRollback = TRUE;
1671
+ dwIndex = 0;
1672
+ ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"test.msu");
1673
+ ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
1674
+ Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
1675
+
1676
+ Assert::Equal(56ull, pPlan->qwEstimatedSize);
1677
+ Assert::Equal(140ull, pPlan->qwCacheSizeTotal);
1678
+
1679
+ fRollback = FALSE;
1680
+ dwIndex = 0;
1681
+ DWORD dwExecuteCheckpointId = 2;
1682
+ ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
1683
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1684
+ ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"test.msu");
1685
+ ValidateExecuteMsuPackage(pPlan, fRollback, dwIndex++, L"test.msu", BOOTSTRAPPER_ACTION_STATE_INSTALL);
1686
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1687
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1688
+ ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
1689
+ Assert::Equal(dwIndex, pPlan->cExecuteActions);
1690
+
1691
+ fRollback = TRUE;
1692
+ dwIndex = 0;
1693
+ dwExecuteCheckpointId = 2;
1694
+ ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
1695
+ ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"test.msu");
1696
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1697
+ ValidateExecuteMsuPackage(pPlan, fRollback, dwIndex++, L"test.msu", BOOTSTRAPPER_ACTION_STATE_UNINSTALL);
1698
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1699
+ ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1700
+ ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
1701
+ Assert::Equal(dwIndex, pPlan->cRollbackActions);
1702
+
1703
+ Assert::Equal(1ul, pPlan->cExecutePackagesTotal);
1704
+ Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
1705
+
1706
+ dwIndex = 0;
1707
+ Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
1708
+
1709
+ dwIndex = 0;
1710
+ Assert::Equal(dwIndex, pPlan->cCleanActions);
1711
+
1712
+ UINT uIndex = 0;
1713
+ ValidatePlannedProvider(pPlan, uIndex++, L"{06077C60-DC46-4F4A-8D3C-05F869187191}", NULL);
1714
+ Assert::Equal(uIndex, pPlan->cPlannedProviders);
1715
+
1716
+ Assert::Equal(1ul, pEngineState->packages.cPackages);
1717
+ ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"test.msu", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
1718
+ }
1719
+
1720
[Fact]
1721
void SlipstreamInstallTest()
1722
{
@@ -2571,6 +2756,21 @@ namespace Bootstrapper
2756
NativeAssert::StringEqual(wzPackageId, pOrderedPatch->pPackage->sczId);
2757
}
2758
2759
+ void ValidateExecuteMsuPackage(
2760
+ __in BURN_PLAN* pPlan,
2761
+ __in BOOL fRollback,
2762
+ __in DWORD dwIndex,
2763
+ __in LPCWSTR wzPackageId,
2764
+ __in BOOTSTRAPPER_ACTION_STATE action
2765
+ )
2766
+ {
2767
+ BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
2768
+ Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE, pAction->type);
2769
+ NativeAssert::StringEqual(wzPackageId, pAction->msuPackage.pPackage->sczId);
2770
+ Assert::Equal<DWORD>(action, pAction->msuPackage.action);
2771
+ Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
2772
+ }
2773
+
2774
void ValidateExecutePackageDependency(
2775
__in BURN_PLAN* pPlan,
2776
__in BOOL fRollback,
src/burn/test/BurnUnitTest/TestData/PlanTest/Failure_BundleD_manifest.xml
new
+1
@@ -0,0 +1 @@
1
+<?xml version="1.0" encoding="utf-8"?><BurnManifest xmlns="http://wixtoolset.org/schemas/v4/2008/Burn"><Log PathVariable="WixBundleLog" Prefix="~FailureTests_BundleD" Extension=".log" /><RelatedBundle Id="{3C1A4842-81AC-4C90-8B35-A5E18F034C8D}" Action="Upgrade" /><Variable Id="TestGroupName" Value="FailureTests" Type="string" Hidden="no" Persisted="no" /><Variable Id="WixBundleInProgressName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSource" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSourceFolder" Hidden="no" Persisted="yes" /><Variable Id="WixBundleLastUsedSource" Hidden="no" Persisted="yes" /><RegistrySearch Id="NETFRAMEWORK45" Variable="NETFRAMEWORK45" Root="HKLM" Key="SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" Value="Release" Type="value" VariableType="string" /><RegistrySearch Id="wrsQ7JTGqvaQuDYjfHJoyjxtkLlR6c" Variable="ExeA_Version" Root="HKLM" Key="Software\WiX\Tests\FailureTests\ExeA" Value="Version" Type="value" VariableType="string" /><UX><Payload Id="WixManagedBootstrapperApplicationHost" FilePath="mbahost.dll" SourcePath="u30" /><Payload Id="payO60IVK4ATGzPpMz3rwVbUWl6DyU" FilePath="WixToolset.Mba.Host.config" SourcePath="u0" /><Payload Id="payxj4zDAKL2NVlz4ohp0GvwFHepyI" FilePath="TestBA.dll" SourcePath="u1" /><Payload Id="pay1hOSAUC8_D633cD2TXpIXCL30OU" FilePath="mbanative.dll" SourcePath="u2" /><Payload Id="payujy6Izl_BlUNfHt2eI.ADfjYAv4" FilePath="WixToolset.Mba.Core.dll" SourcePath="u3" /><Payload Id="payR4EbR4OTDZpPEycWaSSM_gZRBWM" FilePath="mbapreq.thm" SourcePath="u4" /><Payload Id="paylVCy2Ecl8pHPdJTCQZryUG4T9us" FilePath="mbapreq.png" SourcePath="u5" /><Payload Id="payTaG4B_lob1aLcKFaOqSSG3MPMpU" FilePath="mbapreq.wxl" SourcePath="u6" /><Payload Id="payZwIGuiezVTitZOoZKxyh2DdRSGs" FilePath="1028\mbapreq.wxl" SourcePath="u7" /><Payload Id="pay.herBWX.LlOh8jLsx24aWdunV_0" FilePath="1029\mbapreq.wxl" SourcePath="u8" /><Payload Id="pay8DkMszYsoxxdgX14huLDMYXylQg" FilePath="1030\mbapreq.wxl" SourcePath="u9" /><Payload Id="payPaHpoTeOdkW.TK99IDwktNLhTAg" FilePath="1031\mbapreq.wxl" SourcePath="u10" /><Payload Id="pay45AtAzterLTMzZgdxxtuYvaiXwU" FilePath="1032\mbapreq.wxl" SourcePath="u11" /><Payload Id="payA2VEKIqhePyNIEmr14eyH3JoVLc" FilePath="1035\mbapreq.wxl" SourcePath="u12" /><Payload Id="payvre23ObscjzhcaFIifUAkXMdPa8" FilePath="1036\mbapreq.wxl" SourcePath="u13" /><Payload Id="paytxUV3vuBbG2c.a9c.d_sZX2x6wA" FilePath="1038\mbapreq.wxl" SourcePath="u14" /><Payload Id="payYvMWRK9xelo5.sQn7jRkJIaBp9A" FilePath="1040\mbapreq.wxl" SourcePath="u15" /><Payload Id="pay68KKSApyQimbA25t6kSbqhdeH10" FilePath="1041\mbapreq.wxl" SourcePath="u16" /><Payload Id="paypiqxaHpYZqx.9eDVjQrj1igLbRY" FilePath="1042\mbapreq.wxl" SourcePath="u17" /><Payload Id="payTO0YwZzxKpbqdrBVUcVRTu3BFe8" FilePath="1043\mbapreq.wxl" SourcePath="u18" /><Payload Id="payIXg2ldBJukRzhqWolJVOEbTmF34" FilePath="1044\mbapreq.wxl" SourcePath="u19" /><Payload Id="payOHIZbSkIvrpwKkkXI173tv3u3B4" FilePath="1045\mbapreq.wxl" SourcePath="u20" /><Payload Id="payQRQ_UZl_R2UtV0xDXB2yeH2bg3E" FilePath="1046\mbapreq.wxl" SourcePath="u21" /><Payload Id="payhrejLLBfc1i27iN._QPhQ4K337I" FilePath="1049\mbapreq.wxl" SourcePath="u22" /><Payload Id="payqEzaDNzxB68vGp29jgDcCos6dvg" FilePath="1051\mbapreq.wxl" SourcePath="u23" /><Payload Id="paydz8Vk8xSTyYohgGXTSIxWGXL5.Q" FilePath="1053\mbapreq.wxl" SourcePath="u24" /><Payload Id="pay0HRUZTlbC3taSOffJBsEj92Br8Y" FilePath="1055\mbapreq.wxl" SourcePath="u25" /><Payload Id="payIvUOkc_EMH7laMFehefNolV8hZo" FilePath="1060\mbapreq.wxl" SourcePath="u26" /><Payload Id="payLFhOb.rHuk4sW5CYAPMShG0NjGI" FilePath="2052\mbapreq.wxl" SourcePath="u27" /><Payload Id="payqIKCmERK7Nhxx_nNXvRxdKqKDbI" FilePath="2070\mbapreq.wxl" SourcePath="u28" /><Payload Id="payqeWUzIVaEqjuRXN0z8ECC3Y4tCc" FilePath="3082\mbapreq.wxl" SourcePath="u29" /><Payload Id="paylfeHEjJSSTnNzY9QMZM2Ye3Ipy4" FilePath="mbapreq.dll" SourcePath="u31" /><Payload Id="payDPxs6uy8nbky.R7zhir2RRAfc.c" FilePath="WixToolset.Mba.Host.dll" SourcePath="u32" /><Payload Id="uxTxMXPVMXwQrPTMIGa5WGt93w0Ns" FilePath="BootstrapperApplicationData.xml" SourcePath="u33" /><Payload Id="uxYRbgitOs0K878jn5L_z7LdJ21KI" FilePath="BundleExtensionData.xml" SourcePath="u34" /></UX><Container Id="WixAttachedContainer" FileSize="24029" Hash="03F9C95A2ADA5563D3D937C0161F22A76E12F2F0AF2AA6BE567292D0AB122E2C42990E97CA9C1EE9A5F43A571B01C4ED7A3EA5759A6836AC8BFD959D7FFDCB18" FilePath="BundleD.exe" AttachedIndex="1" Attached="yes" Primary="yes" /><Payload Id="NetFx48Web" FilePath="redist\ndp48-web.exe" FileSize="1439328" CertificateRootPublicKeyIdentifier="F49F9B33E25E33CCA0BFB15A62B7C29FFAB3880B" CertificateRootThumbprint="ABDCA79AF9DD48A0EA702AD45260B3C03093FB4B" DownloadUrl="https://go.microsoft.com/fwlink/?LinkId=2085155" Packaging="external" SourcePath="redist\ndp48-web.exe" /><Payload Id="TestExe.exe" FilePath="TestExe.exe" FileSize="23552" Hash="4344604ECBA4DFE5DE7C680CB1AA5BD6FAA29BF95CE07740F02878C2BB1EF6DE6432944A0DB79B034D1C6F68CF80842EEE442EA8A551816E52D3F68901C50AB9" Packaging="embedded" SourcePath="a0" Container="WixAttachedContainer" /><Payload Id="paygJp32KbpyjbVEQFNbl5_izmhdZw" FilePath="TestExe.exe.config" FileSize="387" Hash="8C819A9E835F3921FA80C5C783AB0C42DDAADF0C0F2BEF8630EA122ABCB9DC8EAF0B14E061C46B37C92F55114BB09A8D5B1B613947A76A648953F2C63C0ACA63" Packaging="embedded" SourcePath="a1" Container="WixAttachedContainer" /><RollbackBoundary Id="WixDefaultBoundary" Vital="yes" Transaction="no" /><Registration Id="{9C184683-04FB-49AD-9D79-65101BDC3EE3}" ExecutableName="BundleD.exe" PerMachine="yes" Tag="" Version="1.0.0.0" ProviderKey="{9C184683-04FB-49AD-9D79-65101BDC3EE3}"><Arp Register="yes" DisplayName="~FailureTests - BundleD" DisplayVersion="1.0.0.0" /></Registration><Chain><ExePackage Id="NetFx48Web" Cache="remove" CacheId="642721C60D52051C7F3434D8710FE3406A7CFE10B2B39E90EA847719ED1697D7C614F2DF44AD50412B1DF8C98DD78FDC57CA1D047D28C81AC158092E5FB18040" InstallSize="1439328" Size="1439328" PerMachine="yes" Permanent="yes" Vital="yes" RollbackBoundaryForward="WixDefaultBoundary" LogPathVariable="NetFx48WebLog" RollbackLogPathVariable="WixBundleRollbackLog_NetFx48Web" DetectCondition="NETFRAMEWORK45 >= 528040" InstallArguments="/q /norestart /log "[NetFx48WebLog].html"" UninstallArguments="" Uninstallable="no" RepairArguments="" Repairable="no" Protocol="netfx4"><PayloadRef Id="NetFx48Web" /></ExePackage><ExePackage Id="ExeA" Cache="remove" CacheId="4344604ECBA4DFE5DE7C680CB1AA5BD6FAA29BF95CE07740F02878C2BB1EF6DE6432944A0DB79B034D1C6F68CF80842EEE442EA8A551816E52D3F68901C50AB9" InstallSize="23939" Size="23939" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryBackward="WixDefaultBoundary" LogPathVariable="WixBundleLog_ExeA" RollbackLogPathVariable="WixBundleRollbackLog_ExeA" DetectCondition="ExeA_Version AND ExeA_Version >= v1.0.0.0" InstallArguments="/s 5000 /regw "HKLM\Software\WiX\Tests\FailureTests\ExeA,Version,String,1.0.0.0"" UninstallArguments="/regd "HKLM\Software\WiX\Tests\FailureTests\ExeA,Version"" Uninstallable="yes" RepairArguments="/regw "HKLM\Software\WiX\Tests\FailureTests\ExeA,Version,String,1.0.0.0"" Repairable="yes"><PayloadRef Id="TestExe.exe" /><PayloadRef Id="paygJp32KbpyjbVEQFNbl5_izmhdZw" /></ExePackage></Chain><CommandLine Variables="upperCase" /></BurnManifest>
\ No newline at end of file
src/burn/test/BurnUnitTest/TestData/PlanTest/MsuPackageFixture_manifest.xml
new
+1
@@ -0,0 +1 @@
1
+<?xml version="1.0" encoding="utf-8"?><BurnManifest xmlns="http://wixtoolset.org/schemas/v4/2008/Burn"><Log PathVariable="WixBundleLog" Prefix="BurnBundle" Extension="log" /><RelatedBundle Id="{B94478B1-E1F3-4700-9CE8-6AA090854AEC}" Action="Upgrade" /><Variable Id="WixBundleInProgressName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSource" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSourceFolder" Hidden="no" Persisted="yes" /><Variable Id="WixBundleLastUsedSource" Hidden="no" Persisted="yes" /><UX><Payload Id="payaQenPi7_8hq6T._EXtBW0NvR7gA" FilePath="fakeba.dll" SourcePath="u0" /><Payload Id="uxTxMXPVMXwQrPTMIGa5WGt93w0Ns" FilePath="BootstrapperApplicationData.xml" SourcePath="u1" /><Payload Id="uxYRbgitOs0K878jn5L_z7LdJ21KI" FilePath="BundleExtensionData.xml" SourcePath="u2" /></UX><Container Id="WixAttachedContainer" FileSize="119" Hash="06D28293FD57CD231E125EF9C82418A488928A98832A6937A77A3283A17A5C37F8D619C51759319A57E8F8A948FA73E8C5814185A0114130F3213AB268073555" FilePath="test.exe" AttachedIndex="1" Attached="yes" Primary="yes" /><Payload Id="test.msu" FilePath="test.msu" FileSize="28" Hash="B040F02D2F90E04E9AFBDC91C00CEB5DF97D48E205D96DC0A44E10AF8870794DAE62CA70224F12BE9112AA730BBE470CA81FB5617AAC690E832F3F84510E92BA" Packaging="embedded" SourcePath="a0" Container="WixAttachedContainer" /><RollbackBoundary Id="WixDefaultBoundary" Vital="yes" Transaction="no" /><Registration Id="{06077C60-DC46-4F4A-8D3C-05F869187191}" ExecutableName="test.exe" PerMachine="yes" Tag="" Version="1.0.0.0" ProviderKey="{06077C60-DC46-4F4A-8D3C-05F869187191}"><Arp Register="yes" DisplayName="BurnBundle" DisplayVersion="1.0.0.0" Publisher="Example Corporation" /></Registration><Chain><MsuPackage Id="test.msu" Cache="keep" CacheId="B040F02D2F90E04E9AFBDC91C00CEB5DF97D48E205D96DC0A44E10AF8870794DAE62CA70224F12BE9112AA730BBE470CA81FB5617AAC690E832F3F84510E92BA" InstallSize="28" Size="28" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryForward="WixDefaultBoundary" RollbackBoundaryBackward="WixDefaultBoundary" DetectCondition="DetectedTheMsu" KB="xyz"><PayloadRef Id="test.msu" /></MsuPackage></Chain><CommandLine Variables="upperCase" /></BurnManifest>
\ No newline at end of file
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+8
@@ -2170,6 +2170,14 @@ private: // privates
2170
m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE, pArgs, pResults, m_pvBAFunctionsProcContext);
2171
}
2172
2173
+ void OnExecuteProcessCancelFallback(
2174
+ __in BA_ONEXECUTEPROCESSCANCEL_ARGS* pArgs,
2175
+ __inout BA_ONEXECUTEPROCESSCANCEL_RESULTS* pResults
2176
+ )
2177
+ {
2178
+ m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL, pArgs, pResults, m_pvBAFunctionsProcContext);
2179
+ }
2180
+
2181
2182
public: //CBalBaseBootstrapperApplication
2183
virtual STDMETHODIMP Initialize(
src/test/burn/TestBA/TestBA.cs
+60
-31
@@ -21,7 +21,7 @@ namespace WixToolset.Test.BA
21
private Form dummyWindow;
22
private IntPtr windowHandle;
23
private LaunchAction action;
24
- private ManualResetEvent wait;
24
+ private readonly ManualResetEvent wait;
25
private int result;
26
27
private string updateBundlePath;
@@ -397,6 +397,35 @@ namespace WixToolset.Test.BA
397
}
398
}
399
400
+ protected override void OnExecutePackageComplete(ExecutePackageCompleteEventArgs args)
401
+ {
402
+ bool logTestRegistryValue;
403
+ string recordTestRegistryValue = this.ReadPackageAction(args.PackageId, "RecordTestRegistryValue");
404
+ if (!String.IsNullOrEmpty(recordTestRegistryValue) && Boolean.TryParse(recordTestRegistryValue, out logTestRegistryValue) && logTestRegistryValue)
405
+ {
406
+ var value = this.ReadTestRegistryValue(args.PackageId);
407
+ this.Log("TestRegistryValue: {0}, Version, '{1}'", args.PackageId, value);
408
+ }
409
+ }
410
+
411
+ protected override void OnExecuteProcessCancel(ExecuteProcessCancelEventArgs args)
412
+ {
413
+ BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION action;
414
+ string actionValue = this.ReadPackageAction(args.PackageId, "ProcessCancelAction");
415
+ if (actionValue != null && TryParseEnum<BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION>(actionValue, out action))
416
+ {
417
+ args.Action = action;
418
+ }
419
+
420
+ if (args.Action == BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION.Abandon)
421
+ {
422
+ // Give time to the process to start before its files are deleted.
423
+ Thread.Sleep(2000);
424
+ }
425
+
426
+ this.Log("OnExecuteProcessCancel({0})", args.Action);
427
+ }
428
+
429
protected override void OnExecuteFilesInUse(ExecuteFilesInUseEventArgs args)
430
{
431
this.Log("OnExecuteFilesInUse() - package: {0}, source: {1}, retries remaining: {2}, data: {3}", args.PackageId, args.Source, this.retryExecuteFilesInUse, String.Join(", ", args.Files.ToArray()));
@@ -488,43 +517,34 @@ namespace WixToolset.Test.BA
517
private void TestVariables()
518
{
519
// First make sure we can check and get standard variables of each type.
520
+ if (this.Engine.ContainsVariable("WindowsFolder"))
521
{
492
- string value = null;
493
- if (this.Engine.ContainsVariable("WindowsFolder"))
494
- {
495
- value = this.Engine.GetVariableString("WindowsFolder");
496
- this.Engine.Log(LogLevel.Verbose, "TEST: Successfully retrieved a string variable: WindowsFolder");
497
- }
498
- else
499
- {
500
- throw new Exception("Engine did not define a standard variable: WindowsFolder");
501
- }
522
+ string value = this.Engine.GetVariableString("WindowsFolder");
523
+ this.Engine.Log(LogLevel.Verbose, String.Format("TEST: Successfully retrieved a string variable: WindowsFolder '{0}'", value));
524
+ }
525
+ else
526
+ {
527
+ throw new Exception("Engine did not define a standard variable: WindowsFolder");
528
}
529
530
+ if (this.Engine.ContainsVariable("NTProductType"))
531
{
505
- long value = 0;
506
- if (this.Engine.ContainsVariable("NTProductType"))
507
- {
508
- value = this.Engine.GetVariableNumeric("NTProductType");
509
- this.Engine.Log(LogLevel.Verbose, "TEST: Successfully retrieved a numeric variable: NTProductType");
510
- }
511
- else
512
- {
513
- throw new Exception("Engine did not define a standard variable: NTProductType");
514
- }
532
+ long value = this.Engine.GetVariableNumeric("NTProductType");
533
+ this.Engine.Log(LogLevel.Verbose, String.Format("TEST: Successfully retrieved a numeric variable: NTProductType '{0}'", value));
534
+ }
535
+ else
536
+ {
537
+ throw new Exception("Engine did not define a standard variable: NTProductType");
538
}
539
540
+ if (this.Engine.ContainsVariable("VersionMsi"))
541
{
518
- string value = null;
519
- if (this.Engine.ContainsVariable("VersionMsi"))
520
- {
521
- value = this.Engine.GetVariableVersion("VersionMsi");
522
- this.Engine.Log(LogLevel.Verbose, "TEST: Successfully retrieved a version variable: VersionMsi");
523
- }
524
- else
525
- {
526
- throw new Exception("Engine did not define a standard variable: VersionMsi");
527
- }
542
+ string value = this.Engine.GetVariableVersion("VersionMsi");
543
+ this.Engine.Log(LogLevel.Verbose, String.Format("TEST: Successfully retrieved a version variable: VersionMsi '{0}'", value));
544
+ }
545
+ else
546
+ {
547
+ throw new Exception("Engine did not define a standard variable: VersionMsi");
548
}
549
550
// Now validate that Contians returns false for non-existant variables of each type.
@@ -596,6 +616,15 @@ namespace WixToolset.Test.BA
616
}
617
}
618
619
+ private string ReadTestRegistryValue(string name)
620
+ {
621
+ string testName = this.Engine.GetVariableString("TestGroupName");
622
+ using (RegistryKey testKey = Registry.LocalMachine.OpenSubKey(String.Format(@"Software\WiX\Tests\{0}\{1}", testName, name)))
623
+ {
624
+ return testKey == null ? null : testKey.GetValue("Version") as string;
625
+ }
626
+ }
627
+
628
private static bool TryParseEnum<T>(string value, out T t)
629
{
630
try
src/test/burn/TestData/FailureTests/BundleD/BundleD.wixproj
new
+19
@@ -0,0 +1,19 @@
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>{3C1A4842-81AC-4C90-8B35-A5E18F034C8D}</UpgradeCode>
6
+ <Version>1.0.0.0</Version>
7
+ </PropertyGroup>
8
+ <ItemGroup>
9
+ <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10
+ </ItemGroup>
11
+ <ItemGroup>
12
+ <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
13
+ </ItemGroup>
14
+ <ItemGroup>
15
+ <PackageReference Include="WixToolset.Bal.wixext" />
16
+ <PackageReference Include="WixToolset.NetFx.wixext" />
17
+ <PackageReference Include="WixToolset.Util.wixext" />
18
+ </ItemGroup>
19
+</Project>
\ No newline at end of file
src/test/burn/TestData/FailureTests/BundleD/BundleD.wxs
new
+19
@@ -0,0 +1,19 @@
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
+<?define TestExeRegistryKey = Software\WiX\Tests\$(var.TestGroupName)\ExeA?>
4
+
5
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
6
+ <Fragment>
7
+ <util:RegistrySearch Root="HKLM" Key="$(var.TestExeRegistryKey)" Value="Version" Variable="ExeA_Version" />
8
+
9
+ <PackageGroup Id="BundlePackages">
10
+ <ExePackage Id="ExeA" Cache="remove" PerMachine="yes"
11
+ DetectCondition="ExeA_Version AND ExeA_Version >= v$(var.Version)"
12
+ InstallArguments="/s 5000 /regw "HKLM\$(var.TestExeRegistryKey),Version,String,$(var.Version)""
13
+ RepairArguments="/regw "HKLM\$(var.TestExeRegistryKey),Version,String,$(var.Version)""
14
+ UninstallArguments="/regd "HKLM\$(var.TestExeRegistryKey),Version"">
15
+ <PayloadGroupRef Id="TestExePayloads" />
16
+ </ExePackage>
17
+ </PackageGroup>
18
+ </Fragment>
19
+</Wix>
src/test/burn/WixToolsetTest.BurnE2E/FailureTests.cs
+42
@@ -2,7 +2,9 @@
2
3
namespace WixToolsetTest.BurnE2E
4
{
5
+ using System.Threading;
6
using WixTestTools;
7
+ using WixToolset.Mba.Core;
8
using Xunit;
9
using Xunit.Abstractions;
10
@@ -10,6 +12,46 @@ namespace WixToolsetTest.BurnE2E
12
{
13
public FailureTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
14
15
+ [Fact]
16
+ public void CanCancelExePackageAndAbandonIt()
17
+ {
18
+ var bundleD = this.CreateBundleInstaller("BundleD");
19
+ var testBAController = this.CreateTestBAController();
20
+
21
+ // Cancel package ExeA after it starts.
22
+ testBAController.SetPackageCancelExecuteAtProgress("ExeA", 1);
23
+ testBAController.SetPackageRecordTestRegistryValue("ExeA");
24
+
25
+ var logPath = bundleD.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT);
26
+ bundleD.VerifyUnregisteredAndRemovedFromPackageCache();
27
+
28
+ Assert.True(LogVerifier.MessageInLogFile(logPath, "TestRegistryValue: ExeA, Version, ''"));
29
+
30
+ // Make sure ExeA finishes running.
31
+ Thread.Sleep(3000);
32
+
33
+ bundleD.VerifyExeTestRegistryValue("ExeA", "1.0.0.0");
34
+ }
35
+
36
+ [Fact]
37
+ public void CanCancelExePackageAndWaitUntilItCompletes()
38
+ {
39
+ var bundleD = this.CreateBundleInstaller("BundleD");
40
+ var testBAController = this.CreateTestBAController();
41
+
42
+ // Cancel package ExeA after it starts.
43
+ testBAController.SetPackageCancelExecuteAtProgress("ExeA", 1);
44
+ testBAController.SetPackageProcessCancelAction("ExeA", BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION.Wait);
45
+ testBAController.SetPackageRecordTestRegistryValue("ExeA");
46
+
47
+ var logPath = bundleD.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT);
48
+ bundleD.VerifyUnregisteredAndRemovedFromPackageCache();
49
+
50
+ Assert.True(LogVerifier.MessageInLogFile(logPath, "TestRegistryValue: ExeA, Version, '1.0.0.0'"));
51
+
52
+ bundleD.VerifyExeTestRegistryValue("ExeA", "1.0.0.0");
53
+ }
54
+
55
[Fact]
56
public void CanCancelMsiPackageVeryEarly()
57
{
src/test/burn/WixToolsetTest.BurnE2E/TestBAController.cs
+15
@@ -147,6 +147,21 @@ namespace WixToolsetTest.BurnE2E
147
this.SetPackageState(packageId, String.Concat(featureId, "Requested"), state.ToString());
148
}
149
150
+ /// <summary>
151
+ /// Requests the BA to log the test registry value for the specified package.
152
+ /// </summary>
153
+ /// <param name="packageId"></param>
154
+ /// <param name="value"></param>
155
+ public void SetPackageRecordTestRegistryValue(string packageId, string value = "true")
156
+ {
157
+ this.SetPackageState(packageId, "RecordTestRegistryValue", value);
158
+ }
159
+
160
+ public void SetPackageProcessCancelAction(string packageId, BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION action)
161
+ {
162
+ this.SetPackageState(packageId, "ProcessCancelAction", action.ToString());
163
+ }
164
+
165
/// <summary>
166
/// Sets the number of times to re-run the Detect phase.
167
/// </summary>