@joebigelow / wix-1 / commits / fbc1a737

Add checkpoint so Exe and Msu packages rollback after being cancelled.

Fixes 5950

Sean Hall committed Mar 18, 2022 at 20:16 UTC fbc1a73743368211d5d8c7fc0625adf6eb9ca50c
10 files changed +171 -57
src/burn/engine/apply.cpp
+71 -22
@@ -63,6 +63,7 @@ typedef struct _BURN_EXECUTE_CONTEXT
63 LPCWSTR wzExecutingPackageId;
64 DWORD cExecutedPackages;
65 DWORD cExecutePackagesTotal;
66 + BOOL fAbandonedProcess;
67 } BURN_EXECUTE_CONTEXT;
68
69
@@ -322,6 +323,7 @@ static HRESULT ExecutePackageComplete(
323 __in BURN_VARIABLES* pVariables,
324 __in LPCWSTR wzPackageId,
325 __in BOOL fPackageVital,
326 + __in BOOL fAbandonedProcess,
327 __in HRESULT hrOverall,
328 __in HRESULT hrExecute,
329 __in BOOL fRollback,
@@ -369,6 +371,7 @@ extern "C" void ApplyReset(
371 BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
372 pPackage->hrCacheResult = S_OK;
373 pPackage->fReachedExecution = FALSE;
374 + pPackage->fAbandonedProcess = FALSE;
375 pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
376 }
377 }
@@ -715,6 +718,9 @@ extern "C" HRESULT ApplyExecute(
718 continue;
719 }
720
721 + context.wzExecutingPackageId = NULL;
722 + context.fAbandonedProcess = FALSE;
723 +
724 // If we are seeking the end of the rollback boundary, skip if this action wasn't it.
725 if (fSeekRollbackBoundaryEnd)
726 {
@@ -2437,6 +2443,9 @@ static HRESULT DoRollbackActions(
2443 continue;
2444 }
2445
2446 + pContext->wzExecutingPackageId = NULL;
2447 + pContext->fAbandonedProcess = FALSE;
2448 +
2449 if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type)
2450 {
2451 if (pRollbackAction->checkpoint.dwId == dwCheckpoint)
@@ -2533,6 +2542,28 @@ LExit:
2542 return hr;
2543 }
2544
2545 +static BOOL ShouldSkipPackage(
2546 + __in BURN_PACKAGE* pPackage,
2547 + __in BOOL fRollback
2548 + )
2549 +{
2550 + BOOL fSkip = FALSE;
2551 +
2552 + if (FAILED(pPackage->hrCacheResult))
2553 + {
2554 + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2555 + ExitFunction1(fSkip = TRUE);
2556 + }
2557 + else if (fRollback && pPackage->fAbandonedProcess)
2558 + {
2559 + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_PACKAGE_WITH_ABANDONED_PROCESS, pPackage->sczId);
2560 + ExitFunction1(fSkip = TRUE);
2561 + }
2562 +
2563 +LExit:
2564 + return fSkip;
2565 +}
2566 +
2567 static HRESULT ExecuteRelatedBundle(
2568 __in BURN_ENGINE_STATE* pEngineState,
2569 __in BURN_EXECUTE_ACTION* pExecuteAction,
@@ -2551,13 +2582,13 @@ static HRESULT ExecuteRelatedBundle(
2582 BURN_RELATED_BUNDLE* pRelatedBundle = pExecuteAction->relatedBundle.pRelatedBundle;
2583 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
2584
2554 - if (FAILED(pPackage->hrCacheResult))
2585 + Assert(pContext->fRollback == fRollback);
2586 +
2587 + if (ShouldSkipPackage(pPackage, fRollback))
2588 {
2556 - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2589 ExitFunction1(hr = S_OK);
2590 }
2591
2560 - Assert(pContext->fRollback == fRollback);
2592 pContext->wzExecutingPackageId = pPackage->sczId;
2593 fBeginCalled = TRUE;
2594
@@ -2599,7 +2630,8 @@ static HRESULT ExecuteRelatedBundle(
2630 LExit:
2631 if (fBeginCalled)
2632 {
2602 - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2633 + pPackage->fAbandonedProcess = pContext->fAbandonedProcess;
2634 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2635 }
2636
2637 return hr;
@@ -2624,6 +2656,9 @@ static HRESULT DoRestoreRelatedBundleActions(
2656 continue;
2657 }
2658
2659 + pContext->wzExecutingPackageId = NULL;
2660 + pContext->fAbandonedProcess = FALSE;
2661 +
2662 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2663 switch (pRestoreRelatedBundleAction->type)
2664 {
@@ -2665,13 +2700,13 @@ static HRESULT ExecuteExePackage(
2700 BOOL fExecuted = FALSE;
2701 BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage;
2702
2668 - if (FAILED(pPackage->hrCacheResult))
2703 + Assert(pContext->fRollback == fRollback);
2704 +
2705 + if (ShouldSkipPackage(pPackage, fRollback))
2706 {
2670 - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2707 ExitFunction1(hr = S_OK);
2708 }
2709
2674 - Assert(pContext->fRollback == fRollback);
2710 pContext->wzExecutingPackageId = pPackage->sczId;
2711 fBeginCalled = TRUE;
2712
@@ -2720,7 +2755,8 @@ LExit:
2755
2756 if (fBeginCalled)
2757 {
2723 - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2758 + pPackage->fAbandonedProcess = pContext->fAbandonedProcess;
2759 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2760 }
2761
2762 return hr;
@@ -2743,13 +2779,13 @@ static HRESULT ExecuteMsiPackage(
2779 BOOL fExecuted = FALSE;
2780 BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage;
2781
2746 - if (FAILED(pPackage->hrCacheResult))
2782 + Assert(pContext->fRollback == fRollback);
2783 +
2784 + if (ShouldSkipPackage(pPackage, fRollback))
2785 {
2748 - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2786 ExitFunction1(hr = S_OK);
2787 }
2788
2752 - Assert(pContext->fRollback == fRollback);
2789 pContext->wzExecutingPackageId = pPackage->sczId;
2790 fBeginCalled = TRUE;
2791
@@ -2784,7 +2820,8 @@ LExit:
2820
2821 if (fBeginCalled)
2822 {
2787 - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2823 + Assert(!pContext->fAbandonedProcess);
2824 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, FALSE, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2825 }
2826
2827 return hr;
@@ -2807,13 +2844,13 @@ static HRESULT ExecuteMspPackage(
2844 BOOL fExecuted = FALSE;
2845 BURN_PACKAGE* pPackage = pExecuteAction->mspTarget.pPackage;
2846
2810 - if (FAILED(pPackage->hrCacheResult))
2847 + Assert(pContext->fRollback == fRollback);
2848 +
2849 + if (ShouldSkipPackage(pPackage, fRollback))
2850 {
2812 - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2851 ExitFunction1(hr = S_OK);
2852 }
2853
2816 - Assert(pContext->fRollback == fRollback);
2854 pContext->wzExecutingPackageId = pPackage->sczId;
2855 fBeginCalled = TRUE;
2856
@@ -2857,7 +2894,8 @@ LExit:
2894
2895 if (fBeginCalled)
2896 {
2860 - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2897 + Assert(!pContext->fAbandonedProcess);
2898 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, FALSE, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2899 }
2900
2901 return hr;
@@ -2882,13 +2920,13 @@ static HRESULT ExecuteMsuPackage(
2920 BOOL fExecuted = FALSE;
2921 BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage;
2922
2885 - if (FAILED(pPackage->hrCacheResult))
2923 + Assert(pContext->fRollback == fRollback);
2924 +
2925 + if (ShouldSkipPackage(pPackage, fRollback))
2926 {
2887 - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2927 ExitFunction1(hr = S_OK);
2928 }
2929
2891 - Assert(pContext->fRollback == fRollback);
2930 pContext->wzExecutingPackageId = pPackage->sczId;
2931 fBeginCalled = TRUE;
2932
@@ -2937,7 +2975,8 @@ LExit:
2975
2976 if (fBeginCalled)
2977 {
2940 - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2978 + pPackage->fAbandonedProcess = pContext->fAbandonedProcess;
2979 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2980 }
2981
2982 return hr;
@@ -3256,7 +3295,8 @@ LExit:
3295
3296 if (fBeginCalled)
3297 {
3259 - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pContext->wzExecutingPackageId, FALSE, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
3298 + Assert(!pContext->fAbandonedProcess);
3299 + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pContext->wzExecutingPackageId, FALSE, FALSE, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
3300 }
3301
3302 return hr;
@@ -3334,6 +3374,14 @@ static int GenericExecuteMessageHandler(
3374 }
3375 break;
3376
3377 + case GENERIC_EXECUTE_MESSAGE_PROCESS_STARTED:
3378 + pContext->fAbandonedProcess = TRUE;
3379 + break;
3380 +
3381 + case GENERIC_EXECUTE_MESSAGE_PROCESS_COMPLETED:
3382 + pContext->fAbandonedProcess = FALSE;
3383 + break;
3384 +
3385 case GENERIC_EXECUTE_MESSAGE_ERROR:
3386 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->wzExecutingPackageId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, 0, NULL, &nResult); // ignore return value.
3387 break;
@@ -3428,6 +3476,7 @@ static HRESULT ExecutePackageComplete(
3476 __in BURN_VARIABLES* pVariables,
3477 __in LPCWSTR wzPackageId,
3478 __in BOOL fPackageVital,
3479 + __in BOOL fAbandonedProcess,
3480 __in HRESULT hrOverall,
3481 __in HRESULT hrExecute,
3482 __in BOOL fRollback,
@@ -3445,7 +3494,7 @@ static HRESULT ExecutePackageComplete(
3494 {
3495 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
3496 }
3448 - *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures.
3497 + *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction && !fAbandonedProcess); // allow retry only on failures.
3498 *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction);
3499
3500 // Remember this package as the package that initiated the forced restart.
src/burn/engine/apply.h
+2
@@ -14,6 +14,8 @@ enum GENERIC_EXECUTE_MESSAGE_TYPE
14 GENERIC_EXECUTE_MESSAGE_PROGRESS,
15 GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE,
16 GENERIC_EXECUTE_MESSAGE_PROCESS_CANCEL,
17 + GENERIC_EXECUTE_MESSAGE_PROCESS_STARTED,
18 + GENERIC_EXECUTE_MESSAGE_PROCESS_COMPLETED,
19 };
20
21 typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
src/burn/engine/elevation.cpp
+18
@@ -44,6 +44,8 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE
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_PROCESS_STARTED,
48 + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_COMPLETED,
49 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR,
50 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE,
51 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_FILES_IN_USE,
@@ -1823,6 +1825,14 @@ static HRESULT ProcessGenericExecuteMessages(
1825 ExitOnFailure(hr, "Failed to read processId.");
1826 break;
1827
1828 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_STARTED:
1829 + message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_STARTED;
1830 + break;
1831 +
1832 + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_COMPLETED:
1833 + message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_COMPLETED;
1834 + break;
1835 +
1836 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR:
1837 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
1838
@@ -3465,6 +3475,14 @@ static int GenericExecuteMessageHandler(
3475 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_CANCEL;
3476 break;
3477
3478 + case GENERIC_EXECUTE_MESSAGE_PROCESS_STARTED:
3479 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_STARTED;
3480 + break;
3481 +
3482 + case GENERIC_EXECUTE_MESSAGE_PROCESS_COMPLETED:
3483 + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROCESS_COMPLETED;
3484 + break;
3485 +
3486 case GENERIC_EXECUTE_MESSAGE_ERROR:
3487 // serialize message data
3488 hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode);
src/burn/engine/engine.mc
+7
@@ -940,6 +940,13 @@ Language=English
940 Bootstrapper application requested delayed cancel during package process progress, id: %1!ls!. Waiting...
941 .
942
943 +MessageId=365
944 +Severity=Success
945 +SymbolicName=MSG_APPLY_SKIPPED_PACKAGE_WITH_ABANDONED_PROCESS
946 +Language=English
947 +Skipping rollback of package: %1!ls! due to abandoning its process. Continuing...
948 +.
949 +
950 MessageId=370
951 Severity=Success
952 SymbolicName=MSG_SESSION_BEGIN
src/burn/engine/exeengine.cpp
+25 -12
@@ -276,30 +276,33 @@ extern "C" HRESULT ExeEnginePlanAddPackage(
276 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
277 ExitOnFailure(hr, "Failed to plan package dependency actions.");
278
279 - // add execute action
280 - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
279 + // add rollback action
280 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
281 {
282 - hr = PlanAppendExecuteAction(pPlan, &pAction);
283 - ExitOnFailure(hr, "Failed to append execute action.");
282 + hr = PlanAppendRollbackAction(pPlan, &pAction);
283 + ExitOnFailure(hr, "Failed to append rollback action.");
284
285 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
286 pAction->exePackage.pPackage = pPackage;
287 - pAction->exePackage.action = pPackage->execute;
287 + pAction->exePackage.action = pPackage->rollback;
288
289 - LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
289 + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
290 +
291 + hr = PlanExecuteCheckpoint(pPlan);
292 + ExitOnFailure(hr, "Failed to append execute checkpoint.");
293 }
294
292 - // add rollback action
293 - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
295 + // add execute action
296 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
297 {
295 - hr = PlanAppendRollbackAction(pPlan, &pAction);
296 - ExitOnFailure(hr, "Failed to append rollback action.");
298 + hr = PlanAppendExecuteAction(pPlan, &pAction);
299 + ExitOnFailure(hr, "Failed to append execute action.");
300
301 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
302 pAction->exePackage.pPackage = pPackage;
300 - pAction->exePackage.action = pPackage->rollback;
303 + pAction->exePackage.action = pPackage->execute;
304
302 - LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
305 + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
306 }
307
308 LExit:
@@ -493,6 +496,10 @@ extern "C" HRESULT ExeEngineRunProcess(
496 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
497 }
498
499 + message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_STARTED;
500 + message.dwUIHint = MB_OK;
501 + pfnGenericMessageHandler(&message, pvContext);
502 +
503 if (fFireAndForget)
504 {
505 ::WaitForInputIdle(pi.hProcess, 5000);
@@ -504,6 +511,7 @@ extern "C" HRESULT ExeEngineRunProcess(
511 // Wait for the executable process while sending fake progress to allow cancel.
512 do
513 {
514 + memset(&message, 0, sizeof(message));
515 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
516 message.dwUIHint = MB_OKCANCEL;
517 message.progress.dwPercentage = 50;
@@ -546,6 +554,11 @@ extern "C" HRESULT ExeEngineRunProcess(
554 }
555 } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
556
557 + memset(&message, 0, sizeof(message));
558 + message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_COMPLETED;
559 + message.dwUIHint = MB_OK;
560 + pfnGenericMessageHandler(&message, pvContext);
561 +
562 if (fDelayedCancel)
563 {
564 ExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT), "Bootstrapper application cancelled during package process progress, exit code: 0x%x", *pdwExitCode);
src/burn/engine/msuengine.cpp
+15 -12
@@ -222,30 +222,33 @@ extern "C" HRESULT MsuEnginePlanAddPackage(
222 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
223 ExitOnFailure(hr, "Failed to plan package dependency actions.");
224
225 - // add execute action
226 - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
225 + // add rollback action
226 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
227 {
228 - hr = PlanAppendExecuteAction(pPlan, &pAction);
229 - ExitOnFailure(hr, "Failed to append execute action.");
228 + hr = PlanAppendRollbackAction(pPlan, &pAction);
229 + ExitOnFailure(hr, "Failed to append rollback action.");
230
231 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
232 pAction->msuPackage.pPackage = pPackage;
233 - pAction->msuPackage.action = pPackage->execute;
233 + pAction->msuPackage.action = pPackage->rollback;
234
235 - LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
235 + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
236 +
237 + hr = PlanExecuteCheckpoint(pPlan);
238 + ExitOnFailure(hr, "Failed to append execute checkpoint.");
239 }
240
238 - // add rollback action
239 - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
241 + // add execute action
242 + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
243 {
241 - hr = PlanAppendRollbackAction(pPlan, &pAction);
242 - ExitOnFailure(hr, "Failed to append rollback action.");
244 + hr = PlanAppendExecuteAction(pPlan, &pAction);
245 + ExitOnFailure(hr, "Failed to append execute action.");
246
247 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
248 pAction->msuPackage.pPackage = pPackage;
246 - pAction->msuPackage.action = pPackage->rollback;
249 + pAction->msuPackage.action = pPackage->execute;
250
248 - LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
251 + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
252 }
253
254 LExit:
src/burn/engine/package.h
+1
@@ -282,6 +282,7 @@ typedef struct _BURN_PACKAGE
282 LPWSTR sczCacheFolder; // only valid during Apply.
283 HRESULT hrCacheResult; // only valid during Apply.
284 BOOL fReachedExecution; // only valid during Apply.
285 + BOOL fAbandonedProcess; // only valid during Apply.
286
287 BURN_PACKAGE_REGISTRATION_STATE cacheRegistrationState; // initialized during Detect, updated during Apply.
288 BURN_PACKAGE_REGISTRATION_STATE installRegistrationState; // initialized during Detect, updated during Apply.
src/burn/test/BurnUnitTest/PlanTest.cpp
+4
@@ -715,6 +715,7 @@ namespace Bootstrapper
715 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
716 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
717 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"ExeA");
718 + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
719 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_INSTALL);
720 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
721 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
@@ -729,6 +730,7 @@ namespace Bootstrapper
730 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL);
731 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
732 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
733 + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
734 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
735 Assert::Equal(dwIndex, pPlan->cRollbackActions);
736
@@ -1682,6 +1684,7 @@ namespace Bootstrapper
1684 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
1685 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1686 ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"test.msu");
1687 + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1688 ValidateExecuteMsuPackage(pPlan, fRollback, dwIndex++, L"test.msu", BOOTSTRAPPER_ACTION_STATE_INSTALL);
1689 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1690 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
@@ -1697,6 +1700,7 @@ namespace Bootstrapper
1700 ValidateExecuteMsuPackage(pPlan, fRollback, dwIndex++, L"test.msu", BOOTSTRAPPER_ACTION_STATE_UNINSTALL);
1701 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1702 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1703 + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
1704 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
1705 Assert::Equal(dwIndex, pPlan->cRollbackActions);
1706
src/test/burn/TestBA/TestBA.cs
+22 -3
@@ -4,6 +4,7 @@ namespace WixToolset.Test.BA
4 {
5 using System;
6 using System.Collections.Generic;
7 + using System.Diagnostics;
8 using System.IO;
9 using System.Linq;
10 using System.Threading;
@@ -37,6 +38,7 @@ namespace WixToolset.Test.BA
38 private string cancelExecuteActionName;
39 private int cancelOnProgressAtProgress;
40 private int retryExecuteFilesInUse;
41 + private bool rollingBack;
42
43 private IBootstrapperCommand Command { get; }
44
@@ -350,6 +352,8 @@ namespace WixToolset.Test.BA
352 {
353 this.Log("OnExecutePackageBegin() - package: {0}, rollback: {1}", args.PackageId, !args.ShouldExecute);
354
355 + this.rollingBack = !args.ShouldExecute;
356 +
357 string slowProgress = this.ReadPackageAction(args.PackageId, "SlowExecute");
358 if (String.IsNullOrEmpty(slowProgress) || !Int32.TryParse(slowProgress, out this.sleepDuringExecute))
359 {
@@ -404,7 +408,7 @@ namespace WixToolset.Test.BA
408 if (!String.IsNullOrEmpty(recordTestRegistryValue) && Boolean.TryParse(recordTestRegistryValue, out logTestRegistryValue) && logTestRegistryValue)
409 {
410 var value = this.ReadTestRegistryValue(args.PackageId);
407 - this.Log("TestRegistryValue: {0}, Version, '{1}'", args.PackageId, value);
411 + this.Log("TestRegistryValue: {0}, {1}, Version, '{2}'", this.rollingBack ? "Rollback" : "Execute", args.PackageId, value);
412 }
413 }
414
@@ -419,8 +423,22 @@ namespace WixToolset.Test.BA
423
424 if (args.Action == BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION.Abandon)
425 {
422 - // Give time to the process to start before its files are deleted.
423 - Thread.Sleep(2000);
426 + // Kill process to make sure it doesn't affect other tests.
427 + try
428 + {
429 + using (Process process = Process.GetProcessById(args.ProcessId))
430 + {
431 + if (process != null)
432 + {
433 + process.Kill();
434 + }
435 + }
436 + }
437 + catch (Exception e)
438 + {
439 + this.Log("Failed to kill process {0}: {1}", args.ProcessId, e);
440 + Thread.Sleep(5000);
441 + }
442 }
443
444 this.Log("OnExecuteProcessCancel({0})", args.Action);
@@ -494,6 +512,7 @@ namespace WixToolset.Test.BA
512 this.cancelOnProgressAtProgress = -1;
513 this.cancelExecuteAtProgress = -1;
514 this.cancelCacheAtProgress = -1;
515 + this.rollingBack = false;
516 }
517
518 protected override void OnApplyComplete(ApplyCompleteEventArgs args)
src/test/burn/WixToolsetTest.BurnE2E/FailureTests.cs
+6 -8
@@ -25,12 +25,8 @@ namespace WixToolsetTest.BurnE2E
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");
28 + Assert.True(LogVerifier.MessageInLogFile(logPath, "TestRegistryValue: Execute, ExeA, Version, ''"));
29 + Assert.False(LogVerifier.MessageInLogFile(logPath, "TestRegistryValue: Rollback, ExeA, Version"));
30 }
31
32 [Fact]
@@ -47,9 +43,11 @@ namespace WixToolsetTest.BurnE2E
43 var logPath = bundleD.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT);
44 bundleD.VerifyUnregisteredAndRemovedFromPackageCache();
45
50 - Assert.True(LogVerifier.MessageInLogFile(logPath, "TestRegistryValue: ExeA, Version, '1.0.0.0'"));
46 + Assert.True(LogVerifier.MessageInLogFile(logPath, "TestRegistryValue: Execute, ExeA, Version, '1.0.0.0'"));
47 + Assert.True(LogVerifier.MessageInLogFile(logPath, "TestRegistryValue: Rollback, ExeA, Version, ''"));
48
52 - bundleD.VerifyExeTestRegistryValue("ExeA", "1.0.0.0");
49 + // The package should have rolled back.
50 + bundleD.VerifyExeTestRegistryRootDeleted("ExeA");
51 }
52
53 [Fact]