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,