main
cpp 1,111 lines 42.3 KB
Raw
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 #include "precomp.h"
4
5 typedef struct _BUNDLE_QUERY_CONTEXT
6 {
7 BURN_PACKAGE* pPackage;
8 BURN_USER_EXPERIENCE* pUserExperience;
9 BOOL fSelfFound;
10 BOOL fNewerFound;
11 } BUNDLE_QUERY_CONTEXT;
12
13 static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
14 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
15 __in_opt LPVOID pvContext
16 );
17 static HRESULT ExecuteBundle(
18 __in BURN_CACHE* pCache,
19 __in BURN_VARIABLES* pVariables,
20 __in BOOL fRollback,
21 __in BOOL fCacheAvailable,
22 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
23 __in LPVOID pvContext,
24 __in BOOTSTRAPPER_ACTION_STATE action,
25 __in BOOTSTRAPPER_RELATION_TYPE relationType,
26 __in BURN_PACKAGE* pPackage,
27 __in BOOL fPseudoPackage,
28 __in_z_opt LPCWSTR wzParent,
29 __in_z_opt LPCWSTR wzIgnoreDependencies,
30 __in_z_opt LPCWSTR wzAncestors,
31 __in_z_opt LPCWSTR wzEngineWorkingDirectory,
32 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
33 );
34 static HRESULT DetectArpEntry(
35 __in BURN_PACKAGE* pPackage,
36 __out BOOL* pfRegistered,
37 __out LPWSTR* psczQuietUninstallString
38 );
39 static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType(
40 __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType
41 );
42
43 // function definitions
44
45 extern "C" HRESULT BundlePackageEngineParsePackageFromXml(
46 __in IXMLDOMNode* pixnBundlePackage,
47 __in BURN_PACKAGE* pPackage
48 )
49 {
50 HRESULT hr = S_OK;
51 BOOL fFoundXml = FALSE;
52 LPWSTR scz = NULL;
53
54 // @DetectCondition
55 hr = XmlGetAttributeEx(pixnBundlePackage, L"BundleId", &pPackage->Bundle.sczBundleId);
56 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @BundleId.");
57
58 // @Version
59 hr = XmlGetAttributeEx(pixnBundlePackage, L"Version", &scz);
60 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Version.");
61
62 hr = VerParseVersion(scz, 0, FALSE, &pPackage->Bundle.pVersion);
63 ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
64
65 if (pPackage->Bundle.pVersion->fInvalid)
66 {
67 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
68 }
69
70 // @InstallArguments
71 hr = XmlGetAttributeEx(pixnBundlePackage, L"InstallArguments", &pPackage->Bundle.sczInstallArguments);
72 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments.");
73
74 // @UninstallArguments
75 hr = XmlGetAttributeEx(pixnBundlePackage, L"UninstallArguments", &pPackage->Bundle.sczUninstallArguments);
76 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArguments.");
77
78 // @RepairArguments
79 hr = XmlGetAttributeEx(pixnBundlePackage, L"RepairArguments", &pPackage->Bundle.sczRepairArguments);
80 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairArguments.");
81
82 // @HideARP
83 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"HideARP", &pPackage->Bundle.fHideARP);
84 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @HideARP.");
85
86 // @SupportsBurnProtocol
87 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"SupportsBurnProtocol", &pPackage->Bundle.fSupportsBurnProtocol);
88 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @SupportsBurnProtocol.");
89
90 // @Win64
91 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"Win64", &pPackage->Bundle.fWin64);
92 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Win64.");
93
94 hr = BundlePackageEngineParseRelatedCodes(pixnBundlePackage, &pPackage->Bundle.rgsczDetectCodes, &pPackage->Bundle.cDetectCodes, &pPackage->Bundle.rgsczUpgradeCodes, &pPackage->Bundle.cUpgradeCodes, &pPackage->Bundle.rgsczAddonCodes, &pPackage->Bundle.cAddonCodes, &pPackage->Bundle.rgsczPatchCodes, &pPackage->Bundle.cPatchCodes);
95 ExitOnFailure(hr, "Failed to parse related codes.");
96
97 hr = ExeEngineParseExitCodesFromXml(pixnBundlePackage, &pPackage->Bundle.rgExitCodes, &pPackage->Bundle.cExitCodes);
98 ExitOnFailure(hr, "Failed to parse exit codes.");
99
100 hr = ExeEngineParseCommandLineArgumentsFromXml(pixnBundlePackage, &pPackage->Bundle.rgCommandLineArguments, &pPackage->Bundle.cCommandLineArguments);
101 ExitOnFailure(hr, "Failed to parse command lines.");
102
103 hr = StrAllocFormatted(&pPackage->Bundle.sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pPackage->Bundle.sczBundleId);
104 ExitOnFailure(hr, "Failed to build uninstall registry key path.");
105
106 LExit:
107 ReleaseStr(scz);
108
109 return hr;
110 }
111
112
113 extern "C" HRESULT BundlePackageEngineParseRelatedCodes(
114 __in IXMLDOMNode* pixnBundle,
115 __in LPWSTR** prgsczDetectCodes,
116 __in DWORD* pcDetectCodes,
117 __in LPWSTR** prgsczUpgradeCodes,
118 __in DWORD* pcUpgradeCodes,
119 __in LPWSTR** prgsczAddonCodes,
120 __in DWORD* pcAddonCodes,
121 __in LPWSTR** prgsczPatchCodes,
122 __in DWORD* pcPatchCodes
123 )
124 {
125 HRESULT hr = S_OK;
126 IXMLDOMNodeList* pixnNodes = NULL;
127 IXMLDOMNode* pixnElement = NULL;
128 LPWSTR sczAction = NULL;
129 LPWSTR sczId = NULL;
130 DWORD cElements = 0;
131
132 hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes);
133 ExitOnFailure(hr, "Failed to get RelatedBundle nodes");
134
135 hr = pixnNodes->get_length((long*)&cElements);
136 ExitOnFailure(hr, "Failed to get RelatedBundle element count.");
137
138 for (DWORD i = 0; i < cElements; ++i)
139 {
140 hr = XmlNextElement(pixnNodes, &pixnElement, NULL);
141 ExitOnFailure(hr, "Failed to get next RelatedBundle element.");
142
143 hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction);
144 ExitOnFailure(hr, "Failed to get @Action.");
145
146 hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId);
147 ExitOnFailure(hr, "Failed to get @Id.");
148
149 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1))
150 {
151 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczDetectCodes), *pcDetectCodes, 1, sizeof(LPWSTR), 5);
152 ExitOnFailure(hr, "Failed to resize Detect code array");
153
154 (*prgsczDetectCodes)[*pcDetectCodes] = sczId;
155 sczId = NULL;
156 *pcDetectCodes += 1;
157 }
158 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1))
159 {
160 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczUpgradeCodes), *pcUpgradeCodes, 1, sizeof(LPWSTR), 5);
161 ExitOnFailure(hr, "Failed to resize Upgrade code array");
162
163 (*prgsczUpgradeCodes)[*pcUpgradeCodes] = sczId;
164 sczId = NULL;
165 *pcUpgradeCodes += 1;
166 }
167 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1))
168 {
169 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczAddonCodes), *pcAddonCodes, 1, sizeof(LPWSTR), 5);
170 ExitOnFailure(hr, "Failed to resize Addon code array");
171
172 (*prgsczAddonCodes)[*pcAddonCodes] = sczId;
173 sczId = NULL;
174 *pcAddonCodes += 1;
175 }
176 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1))
177 {
178 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczPatchCodes), *pcPatchCodes, 1, sizeof(LPWSTR), 5);
179 ExitOnFailure(hr, "Failed to resize Patch code array");
180
181 (*prgsczPatchCodes)[*pcPatchCodes] = sczId;
182 sczId = NULL;
183 *pcPatchCodes += 1;
184 }
185 else
186 {
187 hr = E_INVALIDARG;
188 ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction);
189 }
190 }
191
192 LExit:
193 ReleaseObject(pixnNodes);
194 ReleaseObject(pixnElement);
195 ReleaseStr(sczAction);
196 ReleaseStr(sczId);
197
198 return hr;
199 }
200
201 extern "C" void BundlePackageEnginePackageUninitialize(
202 __in BURN_PACKAGE* pPackage
203 )
204 {
205 ReleaseStr(pPackage->Bundle.sczBundleId);
206 ReleaseStr(pPackage->Bundle.sczArpKeyPath);
207 ReleaseVerutilVersion(pPackage->Bundle.pVersion);
208 ReleaseStr(pPackage->Bundle.sczRegistrationKey);
209 ReleaseStr(pPackage->Bundle.sczInstallArguments);
210 ReleaseStr(pPackage->Bundle.sczRepairArguments);
211 ReleaseStr(pPackage->Bundle.sczUninstallArguments);
212 ReleaseStr(pPackage->Bundle.sczIgnoreDependencies);
213 ReleaseMem(pPackage->Bundle.rgExitCodes);
214
215 // free command-line arguments
216 if (pPackage->Bundle.rgCommandLineArguments)
217 {
218 for (DWORD i = 0; i < pPackage->Bundle.cCommandLineArguments; ++i)
219 {
220 ExeEngineCommandLineArgumentUninitialize(pPackage->Bundle.rgCommandLineArguments + i);
221 }
222 MemFree(pPackage->Bundle.rgCommandLineArguments);
223 }
224
225 for (DWORD i = 0; i < pPackage->Bundle.cDetectCodes; ++i)
226 {
227 ReleaseStr(pPackage->Bundle.rgsczDetectCodes[i]);
228 }
229 ReleaseMem(pPackage->Bundle.rgsczDetectCodes);
230
231 for (DWORD i = 0; i < pPackage->Bundle.cUpgradeCodes; ++i)
232 {
233 ReleaseStr(pPackage->Bundle.rgsczUpgradeCodes[i]);
234 }
235 ReleaseMem(pPackage->Bundle.rgsczUpgradeCodes);
236
237 for (DWORD i = 0; i < pPackage->Bundle.cAddonCodes; ++i)
238 {
239 ReleaseStr(pPackage->Bundle.rgsczAddonCodes[i]);
240 }
241 ReleaseMem(pPackage->Bundle.rgsczAddonCodes);
242
243 for (DWORD i = 0; i < pPackage->Bundle.cPatchCodes; ++i)
244 {
245 ReleaseStr(pPackage->Bundle.rgsczPatchCodes[i]);
246 }
247 ReleaseMem(pPackage->Bundle.rgsczPatchCodes);
248
249 // clear struct
250 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle));
251 }
252
253 extern "C" HRESULT BundlePackageEngineDetectPackage(
254 __in BURN_PACKAGE* pPackage,
255 __in BURN_REGISTRATION* pRegistration,
256 __in BURN_USER_EXPERIENCE* pUserExperience
257 )
258 {
259 HRESULT hr = S_OK;
260 BUNDLE_QUERY_CONTEXT queryContext = { };
261
262 queryContext.pPackage = pPackage;
263 queryContext.pUserExperience = pUserExperience;
264
265 hr = BundleQueryRelatedBundles(
266 BUNDLE_INSTALL_CONTEXT_MACHINE,
267 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczDetectCodes),
268 pPackage->Bundle.cDetectCodes,
269 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczUpgradeCodes),
270 pPackage->Bundle.cUpgradeCodes,
271 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczAddonCodes),
272 pPackage->Bundle.cAddonCodes,
273 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczPatchCodes),
274 pPackage->Bundle.cPatchCodes,
275 QueryRelatedBundlesCallback,
276 &queryContext);
277 ExitOnFailure(hr, "Failed to query per-machine related bundle packages.");
278
279 hr = BundleQueryRelatedBundles(
280 BUNDLE_INSTALL_CONTEXT_USER,
281 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczDetectCodes),
282 pPackage->Bundle.cDetectCodes,
283 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczUpgradeCodes),
284 pPackage->Bundle.cUpgradeCodes,
285 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczAddonCodes),
286 pPackage->Bundle.cAddonCodes,
287 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczPatchCodes),
288 pPackage->Bundle.cPatchCodes,
289 QueryRelatedBundlesCallback,
290 &queryContext);
291 ExitOnFailure(hr, "Failed to query per-user related bundle packages.");
292
293 if (queryContext.fNewerFound)
294 {
295 pPackage->currentState = queryContext.fSelfFound ? BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED : BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
296 }
297 else
298 {
299 pPackage->currentState = queryContext.fSelfFound ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
300 }
301
302 if (pPackage->fCanAffectRegistration)
303 {
304 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
305 }
306
307 hr = DependencyDetectChainPackage(pPackage, pRegistration);
308 ExitOnFailure(hr, "Failed to detect dependencies for BUNDLE package.");
309
310 // TODO: uninstalling compatible Bundles like MsiEngine supports?
311
312 LExit:
313 return hr;
314 }
315
316 //
317 // PlanCalculate - calculates the execute and rollback state for the requested package state.
318 //
319 extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
320 __in BURN_PACKAGE* pPackage
321 )
322 {
323 HRESULT hr = S_OK;
324 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
325 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
326
327 // execute action
328 switch (pPackage->currentState)
329 {
330 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
331 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
332 switch (pPackage->requested)
333 {
334 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
335 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
336 break;
337 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
338 execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
339 break;
340 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
341 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
342 execute = !pPackage->fPermanent ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
343 break;
344 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
345 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
346 break;
347 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT:
348 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
349 break;
350 default:
351 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
352 break;
353 }
354 break;
355
356 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
357 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
358 switch (pPackage->requested)
359 {
360 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
361 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
362 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
363 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
364 break;
365 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
366 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
367 break;
368 default:
369 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
370 break;
371 }
372 break;
373
374 default:
375 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid package current state: %d.", pPackage->currentState);
376 }
377
378 // Calculate the rollback action if there is an execute action.
379 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
380 {
381 switch (pPackage->currentState)
382 {
383 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
384 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
385 switch (pPackage->requested)
386 {
387 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
388 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
389 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
390 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
391 break;
392 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
393 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
394 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
395 break;
396 default:
397 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
398 break;
399 }
400 break;
401
402 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
403 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
404 switch (pPackage->requested)
405 {
406 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
407 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
408 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
409 rollback = !pPackage->fPermanent ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
410 break;
411 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
412 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
413 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
414 break;
415 default:
416 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
417 break;
418 }
419 break;
420
421 default:
422 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid package expected state.");
423 }
424 }
425
426 // return values
427 pPackage->execute = execute;
428 pPackage->rollback = rollback;
429
430 LExit:
431 return hr;
432 }
433
434 //
435 // PlanAdd - adds the calculated execute and rollback actions for the package.
436 //
437 extern "C" HRESULT BundlePackageEnginePlanAddPackage(
438 __in BURN_PACKAGE* pPackage,
439 __in BURN_PLAN* pPlan,
440 __in BURN_LOGGING* pLog,
441 __in BURN_VARIABLES* pVariables
442 )
443 {
444 HRESULT hr = S_OK;
445 BURN_EXECUTE_ACTION* pAction = NULL;
446
447 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
448 ExitOnFailure(hr, "Failed to plan package dependency actions.");
449
450 // add rollback action
451 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
452 {
453 hr = PlanAppendRollbackAction(pPlan, &pAction);
454 ExitOnFailure(hr, "Failed to append rollback action.");
455
456 pAction->type = BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE;
457 pAction->bundlePackage.pPackage = pPackage;
458 pAction->bundlePackage.action = pPackage->rollback;
459
460 hr = StrAllocString(&pAction->bundlePackage.sczParent, pPlan->wzBundleId, 0);
461 ExitOnFailure(hr, "Failed to allocate the parent.");
462
463 if (pPackage->Bundle.wzAncestors)
464 {
465 hr = StrAllocString(&pAction->bundlePackage.sczAncestors, pPackage->Bundle.wzAncestors, 0);
466 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
467 }
468
469 if (pPackage->Bundle.wzEngineWorkingDirectory)
470 {
471 hr = StrAllocString(&pAction->bundlePackage.sczEngineWorkingDirectory, pPackage->Bundle.wzEngineWorkingDirectory, 0);
472 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
473 }
474
475 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
476
477 hr = PlanExecuteCheckpoint(pPlan);
478 ExitOnFailure(hr, "Failed to append execute checkpoint.");
479 }
480
481 // add execute action
482 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
483 {
484 hr = PlanAppendExecuteAction(pPlan, &pAction);
485 ExitOnFailure(hr, "Failed to append execute action.");
486
487 pAction->type = BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE;
488 pAction->bundlePackage.pPackage = pPackage;
489 pAction->bundlePackage.action = pPackage->execute;
490
491 hr = StrAllocString(&pAction->bundlePackage.sczParent, pPlan->wzBundleId, 0);
492 ExitOnFailure(hr, "Failed to allocate the parent.");
493
494 if (pPackage->Bundle.wzAncestors)
495 {
496 hr = StrAllocString(&pAction->bundlePackage.sczAncestors, pPackage->Bundle.wzAncestors, 0);
497 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
498 }
499
500 if (pPackage->Bundle.wzEngineWorkingDirectory)
501 {
502 hr = StrAllocString(&pAction->bundlePackage.sczEngineWorkingDirectory, pPackage->Bundle.wzEngineWorkingDirectory, 0);
503 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
504 }
505
506 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
507 }
508
509 LExit:
510 return hr;
511 }
512
513 //
514 // PlanAdd - adds the calculated execute and rollback actions for the related bundle.
515 //
516 extern "C" HRESULT BundlePackageEnginePlanAddRelatedBundle(
517 __in_opt DWORD *pdwInsertSequence,
518 __in BURN_RELATED_BUNDLE* pRelatedBundle,
519 __in BURN_PLAN* pPlan,
520 __in BURN_LOGGING* pLog,
521 __in BURN_VARIABLES* pVariables
522 )
523 {
524 HRESULT hr = S_OK;
525 BURN_EXECUTE_ACTION* pAction = NULL;
526 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
527
528 hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan);
529 ExitOnFailure(hr, "Failed to plan related bundle dependency actions.");
530
531 // add execute action
532 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
533 {
534 if (pdwInsertSequence)
535 {
536 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
537 ExitOnFailure(hr, "Failed to insert execute action.");
538 }
539 else
540 {
541 hr = PlanAppendExecuteAction(pPlan, &pAction);
542 ExitOnFailure(hr, "Failed to append execute action.");
543 }
544
545 pAction->type = BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE;
546 pAction->relatedBundle.pRelatedBundle = pRelatedBundle;
547 pAction->relatedBundle.action = pPackage->execute;
548
549 if (pPackage->Bundle.sczIgnoreDependencies)
550 {
551 hr = StrAllocString(&pAction->relatedBundle.sczIgnoreDependencies, pPackage->Bundle.sczIgnoreDependencies, 0);
552 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
553 }
554
555 if (pPackage->Bundle.wzAncestors)
556 {
557 hr = StrAllocString(&pAction->relatedBundle.sczAncestors, pPackage->Bundle.wzAncestors, 0);
558 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
559 }
560
561 if (pPackage->Bundle.wzEngineWorkingDirectory)
562 {
563 hr = StrAllocString(&pAction->relatedBundle.sczEngineWorkingDirectory, pPackage->Bundle.wzEngineWorkingDirectory, 0);
564 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
565 }
566
567 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
568 }
569
570 // add rollback action
571 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
572 {
573 hr = PlanAppendRollbackAction(pPlan, &pAction);
574 ExitOnFailure(hr, "Failed to append rollback action.");
575
576 pAction->type = BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE;
577 pAction->relatedBundle.pRelatedBundle = pRelatedBundle;
578 pAction->relatedBundle.action = pPackage->rollback;
579
580 if (pPackage->Bundle.sczIgnoreDependencies)
581 {
582 hr = StrAllocString(&pAction->relatedBundle.sczIgnoreDependencies, pPackage->Bundle.sczIgnoreDependencies, 0);
583 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
584 }
585
586 if (pPackage->Bundle.wzAncestors)
587 {
588 hr = StrAllocString(&pAction->relatedBundle.sczAncestors, pPackage->Bundle.wzAncestors, 0);
589 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
590 }
591
592 if (pPackage->Bundle.wzEngineWorkingDirectory)
593 {
594 hr = StrAllocString(&pAction->relatedBundle.sczEngineWorkingDirectory, pPackage->Bundle.wzEngineWorkingDirectory, 0);
595 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
596 }
597
598 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
599 }
600
601 LExit:
602 return hr;
603 }
604
605 extern "C" HRESULT BundlePackageEngineExecutePackage(
606 __in BURN_EXECUTE_ACTION* pExecuteAction,
607 __in BURN_CACHE* pCache,
608 __in BURN_VARIABLES* pVariables,
609 __in BOOL fRollback,
610 __in BOOL fCacheAvailable,
611 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
612 __in LPVOID pvContext,
613 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
614 )
615 {
616 BOOTSTRAPPER_ACTION_STATE action = pExecuteAction->bundlePackage.action;
617 LPCWSTR wzParent = pExecuteAction->bundlePackage.sczParent;
618 LPCWSTR wzIgnoreDependencies = pExecuteAction->bundlePackage.sczIgnoreDependencies;
619 LPCWSTR wzAncestors = pExecuteAction->bundlePackage.sczAncestors;
620 LPCWSTR wzEngineWorkingDirectory = pExecuteAction->bundlePackage.sczEngineWorkingDirectory;
621 BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_CHAIN_PACKAGE;
622 BURN_PACKAGE* pPackage = pExecuteAction->bundlePackage.pPackage;
623
624 return ExecuteBundle(pCache, pVariables, fRollback, fCacheAvailable, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, FALSE, wzParent, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart);
625 }
626
627 extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle(
628 __in BURN_EXECUTE_ACTION* pExecuteAction,
629 __in BURN_CACHE* pCache,
630 __in BURN_VARIABLES* pVariables,
631 __in BOOL fRollback,
632 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
633 __in LPVOID pvContext,
634 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
635 )
636 {
637 BOOTSTRAPPER_ACTION_STATE action = pExecuteAction->relatedBundle.action;
638 LPCWSTR wzParent = NULL;
639 LPCWSTR wzIgnoreDependencies = pExecuteAction->relatedBundle.sczIgnoreDependencies;
640 LPCWSTR wzAncestors = pExecuteAction->relatedBundle.sczAncestors;
641 LPCWSTR wzEngineWorkingDirectory = pExecuteAction->relatedBundle.sczEngineWorkingDirectory;
642 BURN_RELATED_BUNDLE* pRelatedBundle = pExecuteAction->relatedBundle.pRelatedBundle;
643 BOOTSTRAPPER_RELATION_TYPE relationType = ConvertRelationType(pRelatedBundle->planRelationType);
644 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
645
646 return ExecuteBundle(pCache, pVariables, fRollback, TRUE, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, TRUE, wzParent, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart);
647 }
648
649 extern "C" void BundlePackageEngineUpdateInstallRegistrationState(
650 __in BURN_EXECUTE_ACTION* pAction,
651 __in HRESULT hrExecute
652 )
653 {
654 BURN_PACKAGE* pPackage = pAction->bundlePackage.pPackage;
655
656 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
657 {
658 ExitFunction();
659 }
660
661 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->bundlePackage.action)
662 {
663 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
664 }
665 else
666 {
667 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
668 }
669
670 LExit:
671 return;
672 }
673
674 static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
675 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
676 __in_opt LPVOID pvContext
677 )
678 {
679 HRESULT hr = S_OK;
680 BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
681 LPWSTR sczBundleVersion = NULL;
682 VERUTIL_VERSION* pVersion = NULL;
683 int nCompare = 0;
684 BUNDLE_QUERY_CONTEXT* pContext = reinterpret_cast<BUNDLE_QUERY_CONTEXT*>(pvContext);
685 BURN_PACKAGE* pPackage = pContext->pPackage;
686 BOOTSTRAPPER_RELATION_TYPE relationType = RelatedBundleConvertRelationType(pBundle->relationType);
687 BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext;
688
689 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pPackage->Bundle.sczBundleId, -1) &&
690 pPackage->Bundle.fWin64 == (REG_KEY_64BIT == pBundle->regBitness))
691 {
692 Assert(BOOTSTRAPPER_RELATION_UPGRADE == relationType);
693
694 pContext->fSelfFound = TRUE;
695 }
696
697 hr = RegReadString(pBundle->hkBundle, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion);
698 ExitOnFailure(hr, "Failed to read version from registry for related bundle package: %ls", pBundle->wzBundleId);
699
700 hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pVersion);
701 ExitOnFailure(hr, "Failed to parse related bundle package version: %ls", sczBundleVersion);
702
703 if (pVersion->fInvalid)
704 {
705 LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, pBundle->wzBundleId, sczBundleVersion);
706 }
707
708 if (BOOTSTRAPPER_RELATION_UPGRADE == relationType)
709 {
710 hr = VerCompareParsedVersions(pPackage->Bundle.pVersion, pVersion, &nCompare);
711 ExitOnFailure(hr, "Failed to compare related bundle package version: %ls", pVersion->sczVersion);
712
713 if (nCompare < 0)
714 {
715 pContext->fNewerFound = TRUE;
716 }
717 }
718
719 result = BUNDLE_QUERY_CALLBACK_RESULT_CANCEL;
720
721 // Pass to BA.
722 hr = BACallbackOnDetectRelatedBundlePackage(pContext->pUserExperience, pPackage->sczId, pBundle->wzBundleId, relationType, fPerMachine, pVersion);
723 ExitOnRootFailure(hr, "BA aborted detect related BUNDLE package.");
724
725 result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
726
727 LExit:
728 ReleaseVerutilVersion(pVersion);
729 ReleaseStr(sczBundleVersion);
730
731 return result;
732 }
733
734 static HRESULT ExecuteBundle(
735 __in BURN_CACHE* pCache,
736 __in BURN_VARIABLES* pVariables,
737 __in BOOL fRollback,
738 __in BOOL fCacheAvailable,
739 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
740 __in LPVOID pvContext,
741 __in BOOTSTRAPPER_ACTION_STATE action,
742 __in BOOTSTRAPPER_RELATION_TYPE relationType,
743 __in BURN_PACKAGE* pPackage,
744 __in BOOL fPseudoPackage,
745 __in_z_opt LPCWSTR wzParent,
746 __in_z_opt LPCWSTR wzIgnoreDependencies,
747 __in_z_opt LPCWSTR wzAncestors,
748 __in_z_opt LPCWSTR wzEngineWorkingDirectory,
749 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
750 )
751 {
752 HRESULT hr = S_OK;
753 LPCWSTR wzArguments = NULL;
754 LPWSTR sczCachedDirectory = NULL;
755 LPWSTR sczExecutablePath = NULL;
756 LPWSTR sczBaseCommand = NULL;
757 LPWSTR sczUnformattedUserArgs = NULL;
758 LPWSTR sczUserArgs = NULL;
759 LPWSTR sczUserArgsObfuscated = NULL;
760 LPWSTR sczCommandObfuscated = NULL;
761 LPWSTR sczArpUninstallString = NULL;
762 int argcArp = 0;
763 LPWSTR* argvArp = NULL;
764 BOOL fRegistered = FALSE;
765 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
766 BURN_PIPE_CONNECTION connection = { };
767 DWORD dwExitCode = 0;
768 GENERIC_EXECUTE_MESSAGE message = { };
769 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
770 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
771 LPCWSTR wzOperationCommandLine = NULL;
772 BOOL fRunEmbedded = pPackage->Bundle.fSupportsBurnProtocol;
773
774 if (fPseudoPackage)
775 {
776 if (!PathIsFullyQualified(pPackagePayload->sczFilePath))
777 {
778 ExitWithRootFailure(hr, E_INVALIDSTATE, "Related bundles must have a fully qualified target path.");
779 }
780
781 hr = StrAllocString(&sczExecutablePath, pPackagePayload->sczFilePath, 0);
782 ExitOnFailure(hr, "Failed to build executable path.");
783
784 hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory);
785 ExitOnFailure(hr, "Failed to get cached path for related bundle: %ls", pPackage->sczId);
786 }
787 else if (!fCacheAvailable)
788 {
789 ExitOnNull(BOOTSTRAPPER_ACTION_STATE_UNINSTALL == action, hr, E_INVALIDARG, "The only supported action when the cache is not available is UNINSTALL.");
790
791 hr = DetectArpEntry(pPackage, &fRegistered, &sczArpUninstallString);
792 ExitOnFailure(hr, "Failed to query ARP for uninstall.");
793
794 if (!fRegistered)
795 {
796 if (fRollback)
797 {
798 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT));
799 }
800 else
801 {
802 LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId);
803 }
804
805 ExitFunction();
806 }
807
808 ExitOnNull(sczArpUninstallString, hr, E_INVALIDARG, "QuietUninstallString is null.");
809
810 hr = AppParseCommandLine(sczArpUninstallString, &argcArp, &argvArp);
811 ExitOnFailure(hr, "Failed to parse QuietUninstallString: %ls.", sczArpUninstallString);
812
813 ExitOnNull(argcArp, hr, E_INVALIDARG, "QuietUninstallString must contain an executable path.");
814
815 hr = StrAllocString(&sczExecutablePath, argvArp[0], 0);
816 ExitOnFailure(hr, "Failed to copy executable path.");
817
818 if (pPackage->fPerMachine)
819 {
820 hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath);
821 ExitOnFailure(hr, "Failed to verify the QuietUninstallString executable path is in a secure location: %ls", sczExecutablePath);
822 if (S_FALSE == hr)
823 {
824 LogStringLine(REPORT_STANDARD, "The QuietUninstallString executable path is not in a secure location: %ls", sczExecutablePath);
825 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
826 }
827 }
828
829 hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory);
830 ExitOnFailure(hr, "Failed to get parent directory for QuietUninstallString executable path: %ls", sczExecutablePath);
831 }
832 else
833 {
834 // get cached executable path
835 hr = CacheGetCompletedPath(pCache, pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory);
836 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId);
837
838 hr = PathConcatRelativeToFullyQualifiedBase(sczCachedDirectory, pPackagePayload->sczFilePath, &sczExecutablePath);
839 ExitOnFailure(hr, "Failed to build executable path.");
840 }
841
842 // Best effort to set the execute package cache folder and action variables.
843 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
844 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, action, TRUE);
845
846 // pick arguments
847 switch (action)
848 {
849 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
850 wzArguments = pPackage->Bundle.sczInstallArguments;
851 break;
852
853 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
854 wzOperationCommandLine = L"-uninstall";
855 wzArguments = pPackage->Bundle.sczUninstallArguments;
856 break;
857
858 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
859 wzOperationCommandLine = L"-repair";
860 wzArguments = pPackage->Bundle.sczRepairArguments;
861 break;
862
863 default:
864 hr = E_INVALIDARG;
865 ExitOnFailure(hr, "Invalid Bundle package action: %d.", action);
866 }
867
868 // now add optional arguments
869 if (wzArguments && *wzArguments)
870 {
871 hr = StrAllocString(&sczUnformattedUserArgs, wzArguments, 0);
872 ExitOnFailure(hr, "Failed to copy package arguments.");
873 }
874
875 for (DWORD i = 0; i < pPackage->Bundle.cCommandLineArguments; ++i)
876 {
877 BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pPackage->Bundle.rgCommandLineArguments[i];
878 BOOL fCondition = FALSE;
879
880 hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition);
881 ExitOnFailure(hr, "Failed to evaluate bundle package command-line condition.");
882
883 if (fCondition)
884 {
885 if (sczUnformattedUserArgs)
886 {
887 hr = StrAllocConcat(&sczUnformattedUserArgs, L" ", 0);
888 ExitOnFailure(hr, "Failed to separate command-line arguments.");
889 }
890
891 switch (action)
892 {
893 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
894 hr = StrAllocConcat(&sczUnformattedUserArgs, commandLineArgument->sczInstallArgument, 0);
895 ExitOnFailure(hr, "Failed to get command-line argument for install.");
896 break;
897
898 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
899 hr = StrAllocConcat(&sczUnformattedUserArgs, commandLineArgument->sczUninstallArgument, 0);
900 ExitOnFailure(hr, "Failed to get command-line argument for uninstall.");
901 break;
902
903 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
904 hr = StrAllocConcat(&sczUnformattedUserArgs, commandLineArgument->sczRepairArgument, 0);
905 ExitOnFailure(hr, "Failed to get command-line argument for repair.");
906 break;
907
908 default:
909 hr = E_INVALIDARG;
910 ExitOnFailure(hr, "Invalid Bundle package action: %d.", action);
911 }
912 }
913 }
914
915 // build base command
916 hr = StrAllocFormatted(&sczBaseCommand, L"\"%ls\"", sczExecutablePath);
917 ExitOnFailure(hr, "Failed to allocate base command.");
918
919 for (int i = 1; i < argcArp; ++i)
920 {
921 hr = AppAppendCommandLineArgument(&sczBaseCommand, argvArp[i]);
922 ExitOnFailure(hr, "Failed to append argument from ARP.");
923 }
924
925 if (!fRunEmbedded)
926 {
927 hr = StrAllocConcat(&sczBaseCommand, L" -quiet", 0);
928 ExitOnFailure(hr, "Failed to append quiet argument.");
929
930 // Embedded bundles will disable system restore so might as well make non-embedded do it, too.
931 hr = StrAllocConcat(&sczBaseCommand, L" -disablesystemrestore", 0);
932 ExitOnFailure(hr, "Failed to append disable system restore.");
933 }
934
935 if (wzOperationCommandLine)
936 {
937 hr = StrAllocConcatFormatted(&sczBaseCommand, L" %ls", wzOperationCommandLine);
938 ExitOnFailure(hr, "Failed to append operation argument.");
939 }
940
941 if (wzRelationTypeCommandLine)
942 {
943 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls", wzRelationTypeCommandLine);
944 ExitOnFailure(hr, "Failed to append relation type argument.");
945 }
946
947 if (wzParent)
948 {
949 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls", BURN_COMMANDLINE_SWITCH_PARENT);
950 ExitOnFailure(hr, "Failed to append the parent switch to the command line.");
951
952 hr = AppAppendCommandLineArgument(&sczBaseCommand, wzParent);
953 ExitOnFailure(hr, "Failed to append the parent to the command line.");
954 }
955
956 if (pPackage->Bundle.fHideARP)
957 {
958 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls", BURN_COMMANDLINE_SWITCH_SYSTEM_COMPONENT);
959 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_SYSTEM_COMPONENT);
960 }
961
962 // Add the list of dependencies to ignore, if any, to the burn command line.
963 if (BOOTSTRAPPER_RELATION_CHAIN_PACKAGE == relationType)
964 {
965 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=ALL", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
966 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
967 }
968 else if (wzIgnoreDependencies)
969 {
970 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, wzIgnoreDependencies);
971 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
972 }
973
974 // Add the list of ancestors, if any, to the burn command line.
975 if (wzAncestors)
976 {
977 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors);
978 ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");
979 }
980
981 if (wzEngineWorkingDirectory)
982 {
983 hr = CoreAppendEngineWorkingDirectoryToCommandLine(wzEngineWorkingDirectory, &sczBaseCommand, NULL);
984 ExitOnFailure(hr, "Failed to append the custom working directory to the bundlepackage command line.");
985 }
986
987 hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczBaseCommand, NULL);
988 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
989
990 // build user args
991 if (sczUnformattedUserArgs && *sczUnformattedUserArgs)
992 {
993 hr = VariableFormatString(pVariables, sczUnformattedUserArgs, &sczUserArgs, NULL);
994 ExitOnFailure(hr, "Failed to format argument string.");
995
996 hr = VariableFormatStringObfuscated(pVariables, sczUnformattedUserArgs, &sczUserArgsObfuscated, NULL);
997 ExitOnFailure(hr, "Failed to format obfuscated argument string.");
998
999 hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls %ls", sczBaseCommand, sczUserArgsObfuscated);
1000 ExitOnFailure(hr, "Failed to allocate obfuscated bundle command.");
1001 }
1002
1003 // Append logging to command line if it doesn't contain '-log'
1004 CoreAppendLogToCommandLine(&sczBaseCommand, &sczCommandObfuscated, fRollback, pVariables, pPackage);
1005
1006 // Log obfuscated command, which won't include raw hidden variable values or protocol specific arguments to avoid exposing secrets.
1007 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(action), sczExecutablePath, sczCommandObfuscated ? sczCommandObfuscated : sczBaseCommand);
1008
1009 if (fRunEmbedded)
1010 {
1011 hr = EmbeddedRunBundle(&connection, sczExecutablePath, sczBaseCommand, sczUserArgs, pfnGenericMessageHandler, pvContext, &dwExitCode);
1012 ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath);
1013 }
1014 else
1015 {
1016 hr = ExeEngineRunProcess(pfnGenericMessageHandler, pvContext, pPackage, sczExecutablePath, sczBaseCommand, sczUserArgs, sczCachedDirectory, &dwExitCode);
1017 ExitOnFailure(hr, "Failed to run BUNDLE process");
1018 }
1019
1020 hr = ExeEngineHandleExitCode(pPackage->Bundle.rgExitCodes, pPackage->Bundle.cExitCodes, pPackage->sczId, dwExitCode, pRestart);
1021 ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode);
1022
1023 LExit:
1024 ReleaseStr(sczCachedDirectory);
1025 ReleaseStr(sczExecutablePath);
1026 ReleaseStr(sczBaseCommand);
1027 ReleaseStr(sczUnformattedUserArgs);
1028 StrSecureZeroFreeString(sczUserArgs);
1029 ReleaseStr(sczUserArgsObfuscated);
1030 ReleaseStr(sczCommandObfuscated);
1031 ReleaseStr(sczArpUninstallString);
1032
1033 if (argvArp)
1034 {
1035 AppFreeCommandLineArgs(argvArp);
1036 }
1037
1038 ReleaseFileHandle(hExecutableFile);
1039
1040 // Best effort to clear the execute package cache folder and action variables.
1041 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
1042 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
1043
1044 return hr;
1045 }
1046
1047 static HRESULT DetectArpEntry(
1048 __in BURN_PACKAGE* pPackage,
1049 __out BOOL* pfRegistered,
1050 __out LPWSTR* psczQuietUninstallString
1051 )
1052 {
1053 HRESULT hr = S_OK;
1054 HKEY hKey = NULL;
1055 BOOL fExists = FALSE;
1056 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1057 REG_KEY_BITNESS keyBitness = pPackage->Bundle.fWin64 ? REG_KEY_64BIT : REG_KEY_32BIT;
1058
1059 *pfRegistered = FALSE;
1060 if (psczQuietUninstallString)
1061 {
1062 ReleaseNullStr(*psczQuietUninstallString);
1063 }
1064
1065 if (!pPackage->Bundle.sczArpKeyPath)
1066 {
1067 hr = PathConcatRelativeToBase(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\", pPackage->Bundle.sczBundleId, &pPackage->Bundle.sczArpKeyPath);
1068 ExitOnFailure(hr, "Failed to build full key path.");
1069 }
1070
1071 hr = RegOpenEx(hkRoot, pPackage->Bundle.sczArpKeyPath, KEY_READ, keyBitness, &hKey);
1072 ExitOnPathFailure(hr, fExists, "Failed to open registry key: %ls.", pPackage->Bundle.sczArpKeyPath);
1073
1074 if (!fExists)
1075 {
1076 ExitFunction();
1077 }
1078
1079 *pfRegistered = TRUE;
1080
1081 hr = RegReadString(hKey, L"QuietUninstallString", psczQuietUninstallString);
1082 ExitOnPathFailure(hr, fExists, "Failed to read QuietUninstallString.");
1083
1084 LExit:
1085 ReleaseRegKey(hKey);
1086
1087 return hr;
1088 }
1089
1090 static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType(
1091 __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType
1092 )
1093 {
1094 switch (relationType)
1095 {
1096 case BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_DOWNGRADE: __fallthrough;
1097 case BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_UPGRADE:
1098 return BOOTSTRAPPER_RELATION_UPGRADE;
1099 case BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_ADDON:
1100 return BOOTSTRAPPER_RELATION_ADDON;
1101 case BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_PATCH:
1102 return BOOTSTRAPPER_RELATION_PATCH;
1103 case BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_DEPENDENT_ADDON:
1104 return BOOTSTRAPPER_RELATION_DEPENDENT_ADDON;
1105 case BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_DEPENDENT_PATCH:
1106 return BOOTSTRAPPER_RELATION_DEPENDENT_PATCH;
1107 default:
1108 AssertSz(BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_NONE == relationType, "Unknown BUNDLE_RELATION_TYPE");
1109 return BOOTSTRAPPER_RELATION_NONE;
1110 }
1111 }