@joebigelow / wix / commits / c6c17104

Automatically uninstall the bundle after Quit if eligible.

For now, the requirements are: * The bundle is installed and * The bundle is per-user or has already elevated and * No non-permanent packages are installed and * No non-permanent packages are cached and * No related bundle would run by default during uninstall and * The bundle didn't Uninstall/Cache/Install/Modify/Repair and * The BA didn't opt out of this behavior

Sean Hall committed Feb 3, 2021 at 17:09 UTC c6c17104b50936432a3fe9ca214ba9a3dfa32780
10 files changed +131 -12
src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+4
@@ -221,6 +221,9 @@ enum BOOTSTRAPPER_SHUTDOWN_ACTION
221 // restart the engine which will load the bootstrapper application again.
222 // Typically used to switch from a native bootstrapper application to a managed one.
223 BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER,
224 + // Opts out of the engine behavior of trying to uninstall itself
225 + // when no non-permanent packages are installed.
226 + BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP,
227 };
228
229 enum BURN_MSI_PROPERTY
@@ -470,6 +473,7 @@ struct BA_ONDETECTCOMPLETE_ARGS
473 {
474 DWORD cbSize;
475 HRESULT hrStatus;
476 + BOOL fEligibleForCleanup;
477 };
478
479 struct BA_ONDETECTCOMPLETE_RESULTS
src/engine/core.cpp
+59 -3
@@ -300,7 +300,7 @@ extern "C" HRESULT CoreDetect(
300 ExitOnFailure(hr, "Failed to detect provider key bundle id.");
301
302 // Report the related bundles.
303 - hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action);
303 + hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action, &pEngineState->registration.fEligibleForCleanup);
304 ExitOnFailure(hr, "Failed to report detected related bundles.");
305
306 // Do update detection.
@@ -344,6 +344,14 @@ extern "C" HRESULT CoreDetect(
344 {
345 pPackage = pEngineState->packages.rgPackages + iPackage;
346
347 + // If any packages that can affect registration are present, then the bundle should not automatically be uninstalled.
348 + if (pEngineState->registration.fEligibleForCleanup && pPackage->fCanAffectRegistration &&
349 + (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState ||
350 + BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState))
351 + {
352 + pEngineState->registration.fEligibleForCleanup = FALSE;
353 + }
354 +
355 LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState));
356
357 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
@@ -377,12 +385,12 @@ LExit:
385
386 if (fDetectBegan)
387 {
380 - UserExperienceOnDetectComplete(&pEngineState->userExperience, hr);
388 + UserExperienceOnDetectComplete(&pEngineState->userExperience, hr, pEngineState->registration.fEligibleForCleanup);
389 }
390
391 pEngineState->userExperience.hwndDetect = NULL;
392
385 - LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr);
393 + LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fInstalled), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup));
394
395 return hr;
396 }
@@ -1053,6 +1061,54 @@ LExit:
1061 return hr;
1062 }
1063
1064 +extern "C" HRESULT CoreCleanup(
1065 + __in BURN_ENGINE_STATE* pEngineState
1066 + )
1067 +{
1068 + HRESULT hr = S_OK;
1069 + LONGLONG llValue = 0;
1070 + BOOL fNeedsElevation = pEngineState->registration.fPerMachine && INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe;
1071 +
1072 + if (fNeedsElevation)
1073 + {
1074 + hr = VariableGetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, &llValue);
1075 + ExitOnFailure(hr, "Failed to get value of WixBundleElevated variable during cleanup");
1076 +
1077 + if (llValue)
1078 + {
1079 + fNeedsElevation = FALSE;
1080 + }
1081 + }
1082 +
1083 + if (pEngineState->fApplied && BOOTSTRAPPER_ACTION_LAYOUT < pEngineState->plan.action && BOOTSTRAPPER_ACTION_UPDATE_REPLACE > pEngineState->plan.action ||
1084 + fNeedsElevation)
1085 + {
1086 + ExitFunction();
1087 + }
1088 +
1089 + if (!pEngineState->fDetected)
1090 + {
1091 + hr = CoreDetect(pEngineState, pEngineState->hMessageWindow);
1092 + ExitOnFailure(hr, "Detect during cleanup failed");
1093 + }
1094 +
1095 + if (!pEngineState->registration.fEligibleForCleanup)
1096 + {
1097 + ExitFunction();
1098 + }
1099 +
1100 + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
1101 + ExitOnFailure(hr, "Plan during cleanup failed");
1102 +
1103 + hr = CoreApply(pEngineState, pEngineState->hMessageWindow);
1104 + ExitOnFailure(hr, "Apply during cleanup failed");
1105 +
1106 + // Need to think about cache=always
1107 +
1108 +LExit:
1109 + return hr;
1110 +}
1111 +
1112 // internal helper functions
1113
1114 static HRESULT ParseCommandLine(
src/engine/core.h
+3
@@ -204,6 +204,9 @@ HRESULT CoreAppendFileHandleSelfToCommandLine(
204 __deref_inout_z LPWSTR* psczCommandLine,
205 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
206 );
207 +HRESULT CoreCleanup(
208 + __in BURN_ENGINE_STATE* pEngineState
209 + );
210
211 #if defined(__cplusplus)
212 }
src/engine/detect.cpp
+19 -2
@@ -42,6 +42,7 @@ extern "C" void DetectReset(
42 pRegistration->fEnabledForwardCompatibleBundle = FALSE;
43 PackageUninitialize(&pRegistration->forwardCompatibleBundle);
44 pRegistration->fSelfRegisteredAsDependent = FALSE;
45 + pRegistration->fEligibleForCleanup = FALSE;
46
47 if (pRegistration->rgIgnoredDependencies)
48 {
@@ -184,11 +185,14 @@ extern "C" HRESULT DetectReportRelatedBundles(
185 __in BURN_USER_EXPERIENCE* pUX,
186 __in BURN_REGISTRATION* pRegistration,
187 __in BOOTSTRAPPER_RELATION_TYPE relationType,
187 - __in BOOTSTRAPPER_ACTION action
188 + __in BOOTSTRAPPER_ACTION action,
189 + __out BOOL* pfEligibleForCleanup
190 )
191 {
192 HRESULT hr = S_OK;
193 int nCompareResult = 0;
194 + BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
195 + *pfEligibleForCleanup = pRegistration->fInstalled;
196
197 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
198 {
@@ -201,7 +205,7 @@ extern "C" HRESULT DetectReportRelatedBundles(
205 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
206 {
207 hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult);
204 - ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion, pRelatedBundle->pVersion);
208 + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion);
209
210 if (nCompareResult < 0)
211 {
@@ -244,6 +248,19 @@ extern "C" HRESULT DetectReportRelatedBundles(
248
249 hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation);
250 ExitOnRootFailure(hr, "BA aborted detect related bundle.");
251 +
252 + // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle.
253 + if (*pfEligibleForCleanup)
254 + {
255 + uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
256 + hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, BOOTSTRAPPER_ACTION_UNINSTALL, pRegistration->pVersion, pRelatedBundle->pVersion, &uninstallRequestState);
257 + ExitOnFailure(hr, "Failed to get the default request state for related bundle for calculating fEligibleForCleanup");
258 +
259 + if (BOOTSTRAPPER_REQUEST_STATE_NONE != uninstallRequestState)
260 + {
261 + *pfEligibleForCleanup = FALSE;
262 + }
263 + }
264 }
265
266 LExit:
src/engine/detect.h
+2 -1
@@ -30,7 +30,8 @@ HRESULT DetectReportRelatedBundles(
30 __in BURN_USER_EXPERIENCE* pUX,
31 __in BURN_REGISTRATION* pRegistration,
32 __in BOOTSTRAPPER_RELATION_TYPE relationType,
33 - __in BOOTSTRAPPER_ACTION action
33 + __in BOOTSTRAPPER_ACTION action,
34 + __out BOOL* pfEligibleForCleanup
35 );
36
37 HRESULT DetectUpdate(
src/engine/engine.cpp
+18 -3
@@ -39,7 +39,8 @@ static HRESULT RunRunOnce(
39 );
40 static HRESULT RunApplication(
41 __in BURN_ENGINE_STATE* pEngineState,
42 - __out BOOL* pfReloadApp
42 + __out BOOL* pfReloadApp,
43 + __out BOOL* pfSkipCleanup
44 );
45 static HRESULT ProcessMessage(
46 __in BURN_ENGINE_STATE* pEngineState,
@@ -529,6 +530,7 @@ static HRESULT RunNormal(
530 HANDLE hPipesCreatedEvent = NULL;
531 BOOL fContinueExecution = TRUE;
532 BOOL fReloadApp = FALSE;
533 + BOOL fSkipCleanup = FALSE;
534 BURN_EXTENSION_ENGINE_CONTEXT extensionEngineContext = { };
535
536 // Initialize logging.
@@ -584,11 +586,18 @@ static HRESULT RunNormal(
586 do
587 {
588 fReloadApp = FALSE;
589 + pEngineState->fQuit = FALSE;
590
588 - hr = RunApplication(pEngineState, &fReloadApp);
591 + hr = RunApplication(pEngineState, &fReloadApp, &fSkipCleanup);
592 ExitOnFailure(hr, "Failed while running ");
593 } while (fReloadApp);
594
595 + if (!fSkipCleanup)
596 + {
597 + hr = CoreCleanup(pEngineState);
598 + ExitOnFailure(hr, "Failed to cleanup before shutting down");
599 + }
600 +
601 LExit:
602 BurnExtensionUnload(&pEngineState->extensions);
603
@@ -732,7 +741,8 @@ LExit:
741
742 static HRESULT RunApplication(
743 __in BURN_ENGINE_STATE* pEngineState,
735 - __out BOOL* pfReloadApp
744 + __out BOOL* pfReloadApp,
745 + __out BOOL* pfSkipCleanup
746 )
747 {
748 HRESULT hr = S_OK;
@@ -787,6 +797,11 @@ LExit:
797 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD);
798 *pfReloadApp = TRUE;
799 }
800 + else if (BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP == shutdownAction)
801 + {
802 + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_SKIP_CLEANUP);
803 + *pfSkipCleanup = TRUE;
804 + }
805 }
806
807 // Unload BA.
src/engine/engine.mc
+8 -1
@@ -121,6 +121,13 @@ Language=English
121 The manifest contains an invalid version string: '%1!ls!'
122 .
123
124 +MessageId=14
125 +Severity=Success
126 +SymbolicName=MSG_BA_REQUESTED_SKIP_CLEANUP
127 +Language=English
128 +Bootstrapper application opted out of any engine behavior to automatically uninstall the bundle during shutdown.
129 +.
130 +
131 MessageId=51
132 Severity=Error
133 SymbolicName=MSG_FAILED_PARSE_CONDITION
@@ -286,7 +293,7 @@ MessageId=199
293 Severity=Success
294 SymbolicName=MSG_DETECT_COMPLETE
295 Language=English
289 -Detect complete, result: 0x%1!x!
296 +Detect complete, result: 0x%1!x!, installed: %2!hs!, eligible for cleanup: %3!hs!
297 .
298
299 MessageId=200
src/engine/registration.h
+1
@@ -146,6 +146,7 @@ typedef struct _BURN_REGISTRATION
146 UINT cDependents; // Only valid after detect.
147 LPCWSTR wzSelfDependent; // Only valid after detect.
148 BOOL fSelfRegisteredAsDependent; // Only valid after detect.
149 + BOOL fEligibleForCleanup; // Only valid after detect.
150
151 LPWSTR sczDetectedProviderKeyBundleId;
152 LPWSTR sczAncestors;
src/engine/userexperience.cpp
+15 -1
@@ -736,7 +736,8 @@ LExit:
736
737 EXTERN_C BAAPI UserExperienceOnDetectComplete(
738 __in BURN_USER_EXPERIENCE* pUserExperience,
739 - __in HRESULT hrStatus
739 + __in HRESULT hrStatus,
740 + __in BOOL fEligibleForCleanup
741 )
742 {
743 HRESULT hr = S_OK;
@@ -745,6 +746,7 @@ EXTERN_C BAAPI UserExperienceOnDetectComplete(
746
747 args.cbSize = sizeof(args);
748 args.hrStatus = hrStatus;
749 + args.fEligibleForCleanup = fEligibleForCleanup;
750
751 results.cbSize = sizeof(results);
752
@@ -2296,12 +2298,18 @@ static HRESULT SendBAMessage(
2298 {
2299 HRESULT hr = S_OK;
2300
2301 + if (!pUserExperience->hUXModule)
2302 + {
2303 + ExitFunction();
2304 + }
2305 +
2306 hr = pUserExperience->pfnBAProc(message, pvArgs, pvResults, pUserExperience->pvBAProcContext);
2307 if (hr == E_NOTIMPL)
2308 {
2309 hr = S_OK;
2310 }
2311
2312 +LExit:
2313 return hr;
2314 }
2315
@@ -2314,11 +2322,17 @@ static HRESULT SendBAMessageFromInactiveEngine(
2322 {
2323 HRESULT hr = S_OK;
2324
2325 + if (!pUserExperience->hUXModule)
2326 + {
2327 + ExitFunction();
2328 + }
2329 +
2330 UserExperienceDeactivateEngine(pUserExperience);
2331
2332 hr = SendBAMessage(pUserExperience, message, pvArgs, pvResults);
2333
2334 UserExperienceActivateEngine(pUserExperience);
2335
2336 +LExit:
2337 return hr;
2338 }
src/engine/userexperience.h
+2 -1
@@ -191,7 +191,8 @@ BAAPI UserExperienceOnDetectBegin(
191 );
192 BAAPI UserExperienceOnDetectComplete(
193 __in BURN_USER_EXPERIENCE* pUserExperience,
194 - __in HRESULT hrStatus
194 + __in HRESULT hrStatus,
195 + __in BOOL fEligibleForCleanup
196 );
197 BAAPI UserExperienceOnDetectForwardCompatibleBundle(
198 __in BURN_USER_EXPERIENCE* pUserExperience,