main
cpp 1,213 lines 45.1 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
6 // constants
7
8
9 // structs
10
11 struct POSSIBLE_TARGETPRODUCT
12 {
13 WCHAR wzProductCode[39];
14 LPWSTR pszLocalPackage;
15 MSIINSTALLCONTEXT context;
16 };
17
18 // internal function declarations
19
20 static HRESULT GetPossibleTargetProductCodes(
21 __in BURN_PACKAGES* pPackages,
22 __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes,
23 __inout DWORD* pcPossibleTargetProductCodes
24 );
25 static HRESULT AddPossibleTargetProduct(
26 __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes,
27 __in_z LPCWSTR wzPossibleTargetProductCode,
28 __in MSIINSTALLCONTEXT context,
29 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
30 __inout DWORD* pcPossibleTargetProducts
31 );
32 static HRESULT AddDetectedTargetProduct(
33 __in BURN_PACKAGE* pPackage,
34 __in DWORD dwOrder,
35 __in_z LPCWSTR wzProductCode,
36 __in MSIINSTALLCONTEXT context,
37 __out DWORD* pdwTargetProductIndex
38 );
39 static HRESULT AddMsiChainedPatch(
40 __in BURN_PACKAGE* pPackage,
41 __in BURN_PACKAGE* pMspPackage,
42 __in DWORD dwMspTargetProductIndex,
43 __out DWORD* pdwChainedPatchIndex
44 );
45 static HRESULT DeterminePatchChainedTarget(
46 __in BURN_PACKAGES* pPackages,
47 __in BURN_PACKAGE* pMspPackage,
48 __in LPCWSTR wzTargetProductCode,
49 __in DWORD dwMspTargetProductIndex
50 );
51 static HRESULT PlanTargetProduct(
52 __in BOOTSTRAPPER_DISPLAY display,
53 __in BURN_USER_EXPERIENCE* pUserExperience,
54 __in BOOL fRollback,
55 __in BURN_PLAN* pPlan,
56 __in BURN_LOGGING* pLog,
57 __in BURN_VARIABLES* pVariables,
58 __in BOOTSTRAPPER_ACTION_STATE actionState,
59 __in BURN_PACKAGE* pPackage,
60 __in BURN_MSPTARGETPRODUCT* pTargetProduct
61 );
62
63
64 // function definitions
65
66 extern "C" HRESULT MspEngineParsePackageFromXml(
67 __in IXMLDOMNode* pixnMspPackage,
68 __in BURN_PACKAGE* pPackage
69 )
70 {
71 HRESULT hr = S_OK;
72
73 // @PatchCode
74 hr = XmlGetAttributeEx(pixnMspPackage, L"PatchCode", &pPackage->Msp.sczPatchCode);
75 ExitOnFailure(hr, "Failed to get @PatchCode.");
76
77 // @PatchXml
78 hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml);
79 ExitOnFailure(hr, "Failed to get @PatchXml.");
80
81 // Read properties.
82 hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties);
83 ExitOnFailure(hr, "Failed to parse properties from XML.");
84
85 LExit:
86
87 return hr;
88 }
89
90 extern "C" void MspEnginePackageUninitialize(
91 __in BURN_PACKAGE* pPackage
92 )
93 {
94 ReleaseStr(pPackage->Msp.sczPatchCode);
95 ReleaseStr(pPackage->Msp.sczApplicabilityXml);
96
97 // free properties
98 if (pPackage->Msp.rgProperties)
99 {
100 for (DWORD i = 0; i < pPackage->Msp.cProperties; ++i)
101 {
102 BURN_MSIPROPERTY* pProperty = &pPackage->Msp.rgProperties[i];
103
104 ReleaseStr(pProperty->sczId);
105 ReleaseStr(pProperty->sczValue);
106 ReleaseStr(pProperty->sczRollbackValue);
107 }
108 MemFree(pPackage->Msp.rgProperties);
109 }
110
111 // free target products
112 ReleaseMem(pPackage->Msp.rgTargetProducts);
113
114 // clear struct
115 memset(&pPackage->Msp, 0, sizeof(pPackage->Msp));
116 }
117
118 extern "C" HRESULT MspEngineDetectInitialize(
119 __in BURN_PACKAGES* pPackages
120 )
121 {
122 AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages.");
123
124 HRESULT hr = S_OK;
125 POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL;
126 DWORD cPossibleTargetProducts = 0;
127
128 #ifdef DEBUG
129 // All patch info should be initialized to zero.
130 for (DWORD i = 0; i < pPackages->cPatchInfo; ++i)
131 {
132 BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i];
133 Assert(!pPackage->Msp.cTargetProductCodes);
134 Assert(!pPackage->Msp.rgTargetProducts);
135 }
136 #endif
137
138 // Figure out which product codes to target on the machine. In the worst case all products on the machine
139 // will be returned.
140 hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts);
141 ExitOnFailure(hr, "Failed to get possible target product codes.");
142
143 // Loop through possible target products, testing the collective patch applicability against each product in
144 // the appropriate context. Store the result with the appropriate patch package.
145 for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch)
146 {
147 const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch;
148
149 LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context));
150
151 if (pPossibleTargetProduct->pszLocalPackage)
152 {
153 // Ignores current machine state to determine just patch applicability.
154 // Superseded and obsolesced patches will be planned separately.
155 hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo);
156 }
157 else
158 {
159 hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo);
160 }
161
162 if (SUCCEEDED(hr))
163 {
164 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
165 {
166 hr = HRESULT_FROM_WIN32(pPackages->rgPatchInfo[iPatchInfo].uStatus);
167 BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo];
168 Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type);
169
170 if (S_OK == hr)
171 {
172 // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later.
173 hr = MspEngineAddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context);
174 ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId);
175 }
176 else
177 {
178 LogStringLine(REPORT_DEBUG, " 0x%x: Patch applicability failed for package: %ls", hr, pMspPackage->sczId);
179 }
180 }
181 }
182 else
183 {
184 LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr);
185 }
186
187 hr = S_OK; // always reset so we test all possible target products.
188 }
189
190 LExit:
191 if (rgPossibleTargetProducts)
192 {
193 for (DWORD i = 0; i < cPossibleTargetProducts; ++i)
194 {
195 ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage);
196 }
197 MemFree(rgPossibleTargetProducts);
198 }
199
200 return hr;
201 }
202
203 extern "C" HRESULT MspEngineAddDetectedTargetProduct(
204 __in BURN_PACKAGES* pPackages,
205 __in BURN_PACKAGE* pPackage,
206 __in DWORD dwOrder,
207 __in_z LPCWSTR wzProductCode,
208 __in MSIINSTALLCONTEXT context
209 )
210 {
211 HRESULT hr = S_OK;
212 DWORD dwTargetProductIndex = 0;
213
214 hr = AddDetectedTargetProduct(pPackage, dwOrder, wzProductCode, context, &dwTargetProductIndex);
215 ExitOnFailure(hr, "Failed to add detected target product.");
216
217 hr = DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, dwTargetProductIndex);
218 ExitOnFailure(hr, "Failed to determine patch chained target.");
219
220 LExit:
221 return hr;
222 }
223
224 extern "C" HRESULT MspEngineAddMissingSlipstreamTarget(
225 __in BURN_PACKAGE* pMsiPackage,
226 __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp
227 )
228 {
229 HRESULT hr = S_OK;
230 DWORD dwTargetProductIndex = 0;
231 BURN_MSPTARGETPRODUCT* pTargetProduct = NULL;
232 DWORD dwChainedPatchIndex = 0;
233
234 hr = AddDetectedTargetProduct(pSlipstreamMsp->pMspPackage, 0, pMsiPackage->Msi.sczProductCode, pMsiPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, &dwTargetProductIndex);
235 ExitOnFailure(hr, "Failed to add missing slipstream target.");
236
237 pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + dwTargetProductIndex;
238 pTargetProduct->fSlipstream = TRUE;
239 pTargetProduct->fSlipstreamRequired = TRUE;
240 pTargetProduct->pChainedTargetPackage = pMsiPackage;
241
242 hr = AddMsiChainedPatch(pMsiPackage, pSlipstreamMsp->pMspPackage, dwTargetProductIndex, &dwChainedPatchIndex);
243 ExitOnFailure(hr, "Failed to add chained patch.");
244
245 pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex;
246
247 LExit:
248 return hr;
249 }
250
251 extern "C" HRESULT MspEngineDetectPackage(
252 __in BURN_PACKAGE* pPackage,
253 __in BURN_REGISTRATION* pRegistration,
254 __in BURN_USER_EXPERIENCE* pUserExperience
255 )
256 {
257 HRESULT hr = S_OK;
258 LPWSTR sczState = NULL;
259
260 if (pPackage->fCanAffectRegistration)
261 {
262 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
263 }
264
265 if (0 == pPackage->Msp.cTargetProductCodes)
266 {
267 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
268 }
269 else
270 {
271 // Start the package state at the highest state then loop through all the
272 // target product codes and end up setting the current state to the lowest
273 // package state applied to the target product codes.
274 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
275
276 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
277 {
278 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
279
280 hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState);
281 if (SUCCEEDED(hr))
282 {
283 switch (*sczState)
284 {
285 case '1':
286 pTargetProduct->fInstalled = TRUE;
287 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
288 break;
289
290 case '2':
291 pTargetProduct->fInstalled = TRUE;
292 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
293 break;
294
295 case '4':
296 pTargetProduct->fInstalled = TRUE;
297 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
298 break;
299
300 default:
301 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
302 break;
303 }
304 }
305 else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
306 {
307 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
308 hr = S_OK;
309 }
310 ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode);
311
312 if (pPackage->currentState > pTargetProduct->patchPackageState)
313 {
314 pPackage->currentState = pTargetProduct->patchPackageState;
315 }
316
317 if (pPackage->fCanAffectRegistration)
318 {
319 pTargetProduct->registrationState = pTargetProduct->fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
320
321 if (pTargetProduct->fInstalled)
322 {
323 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
324 }
325 }
326
327 hr = BACallbackOnDetectPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState);
328 ExitOnRootFailure(hr, "BA aborted detect patch target.");
329 }
330 }
331
332 hr = DependencyDetectChainPackage(pPackage, pRegistration);
333 ExitOnFailure(hr, "Failed to detect dependencies for MSP package.");
334
335 LExit:
336 ReleaseStr(sczState);
337
338 return hr;
339 }
340
341 extern "C" HRESULT MspEnginePlanInitializePackage(
342 __in BURN_PACKAGE* pPackage,
343 __in BURN_USER_EXPERIENCE* pUserExperience
344 )
345 {
346 HRESULT hr = S_OK;
347
348 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
349 {
350 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
351
352 if (!pTargetProduct->fInstalled && pTargetProduct->fSlipstreamRequired && BOOTSTRAPPER_REQUEST_STATE_PRESENT > pTargetProduct->pChainedTargetPackage->requested)
353 {
354 // There's no way to apply the patch if the target isn't installed.
355 pTargetProduct->defaultRequested = pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
356 continue;
357 }
358
359 pTargetProduct->defaultRequested = pTargetProduct->requested = pPackage->requested;
360
361 hr = BACallbackOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &pTargetProduct->requested);
362 ExitOnRootFailure(hr, "BA aborted plan patch target.");
363 }
364
365 LExit:
366 return hr;
367 }
368
369 //
370 // PlanCalculate - calculates the execute and rollback state for the requested package state.
371 //
372 extern "C" HRESULT MspEnginePlanCalculatePackage(
373 __in BURN_PACKAGE* pPackage,
374 __in BOOL fInsideMsiTransaction
375 )
376 {
377 HRESULT hr = S_OK;
378 BOOL fWillUninstallAll = TRUE;
379
380 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
381 {
382 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
383
384 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
385 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
386
387 // Calculate the execute action.
388 switch (pTargetProduct->patchPackageState)
389 {
390 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
391 switch (pTargetProduct->requested)
392 {
393 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
394 execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
395 fWillUninstallAll = FALSE;
396 break;
397
398 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
399 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
400 execute = !pPackage->fPermanent ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
401 break;
402
403 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
404 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
405 break;
406
407 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT:
408 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
409 fWillUninstallAll = FALSE;
410 break;
411
412 default:
413 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
414 fWillUninstallAll = FALSE;
415 break;
416 }
417 break;
418
419 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
420 switch (pTargetProduct->requested)
421 {
422 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
423 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
424 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
425 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
426 fWillUninstallAll = FALSE;
427 break;
428
429 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
430 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
431 break;
432
433 default:
434 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
435 break;
436 }
437 break;
438
439 default:
440 switch (pTargetProduct->requested)
441 {
442 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
443 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
444 break;
445
446 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT:
447 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
448 fWillUninstallAll = FALSE;
449 break;
450
451 default:
452 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
453
454 if (pTargetProduct->fInstalled)
455 {
456 fWillUninstallAll = FALSE;
457 }
458 break;
459 }
460 break;
461 }
462
463 // Calculate the rollback action if there is an execute action.
464 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction)
465 {
466 switch (pPackage->currentState)
467 {
468 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
469 switch (pTargetProduct->requested)
470 {
471 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
472 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
473 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
474 break;
475
476 default:
477 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
478 break;
479 }
480 break;
481
482 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
483 switch (pTargetProduct->requested)
484 {
485 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
486 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
487 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
488 rollback = !pPackage->fPermanent ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
489 break;
490
491 default:
492 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
493 break;
494 }
495 break;
496
497 default:
498 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
499 break;
500 }
501 }
502
503 pTargetProduct->execute = execute;
504 pTargetProduct->rollback = rollback;
505
506 // The highest aggregate action state found will be returned.
507 if (pPackage->execute < execute)
508 {
509 pPackage->execute = execute;
510 }
511
512 if (pPackage->rollback < rollback)
513 {
514 pPackage->rollback = rollback;
515 }
516 }
517
518 // The dependency manager will do the wrong thing if the package level action is UNINSTALL
519 // when the patch will still be applied to at least one product.
520 if (!fWillUninstallAll && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
521 {
522 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
523 }
524
525 return hr;
526 }
527
528 //
529 // PlanAdd - adds the calculated execute and rollback actions for the package.
530 //
531 extern "C" HRESULT MspEnginePlanAddPackage(
532 __in BOOTSTRAPPER_DISPLAY display,
533 __in BURN_USER_EXPERIENCE* pUserExperience,
534 __in BURN_PACKAGE* pPackage,
535 __in BURN_PLAN* pPlan,
536 __in BURN_LOGGING* pLog,
537 __in BURN_VARIABLES* pVariables
538 )
539 {
540 HRESULT hr = S_OK;
541
542 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
543 ExitOnFailure(hr, "Failed to plan package dependency actions.");
544
545 // Plan the actions for each target product code.
546 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
547 {
548 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
549
550 // If the dependency manager changed the action state for the patch, change the target product actions.
551 if (pPackage->fDependencyManagerWasHere)
552 {
553 pTargetProduct->execute = pPackage->execute;
554 pTargetProduct->rollback = pPackage->rollback;
555 }
556
557 if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute)
558 {
559 hr = PlanTargetProduct(display, pUserExperience, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct);
560 ExitOnFailure(hr, "Failed to plan target product.");
561 }
562
563 if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback)
564 {
565 hr = PlanTargetProduct(display, pUserExperience, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct);
566 ExitOnFailure(hr, "Failed to plan rollback target product.");
567 }
568 }
569
570 LExit:
571
572 return hr;
573 }
574
575 extern "C" HRESULT MspEngineExecutePackage(
576 __in_opt HWND hwndParent,
577 __in BURN_EXECUTE_ACTION* pExecuteAction,
578 __in BURN_CACHE* pCache,
579 __in BURN_VARIABLES* pVariables,
580 __in BOOL fRollback,
581 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
582 __in LPVOID pvContext,
583 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
584 )
585 {
586 HRESULT hr = S_OK;
587 WIU_MSI_EXECUTE_CONTEXT context = { };
588 WIU_RESTART restart = WIU_RESTART_NONE;
589
590 LPWSTR sczCachedDirectory = NULL;
591 LPWSTR sczMspPath = NULL;
592 LPWSTR sczPatches = NULL;
593 LPWSTR sczProperties = NULL;
594 LPWSTR sczObfuscatedProperties = NULL;
595
596 // default to "verbose" logging
597 DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
598
599 // get cached MSP paths
600 for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
601 {
602 LPCWSTR wzAppend = NULL;
603 BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
604 BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload;
605 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches.");
606
607 if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action)
608 {
609 hr = CacheGetCompletedPath(pCache, pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
610 ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);
611
612 // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only
613 // Best effort to set the execute package cache folder variable.
614 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
615
616 hr = PathConcatRelativeToFullyQualifiedBase(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath);
617 ExitOnFailure(hr, "Failed to build MSP path.");
618
619 wzAppend = sczMspPath;
620 }
621 else // uninstall
622 {
623 wzAppend = pMspPackage->Msp.sczPatchCode;
624 }
625
626 if (sczPatches)
627 {
628 hr = StrAllocConcat(&sczPatches, L";", 0);
629 ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
630 }
631
632 hr = StrAllocConcat(&sczPatches, wzAppend, 0);
633 ExitOnFailure(hr, "Failed to append patch.");
634 }
635
636 // Best effort to set the execute package action variable.
637 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE);
638
639 // Wire up the external UI handler and logging.
640 if (pExecuteAction->mspTarget.fDisableExternalUiHandler)
641 {
642 hr = WiuInitializeInternalUI(pExecuteAction->mspTarget.uiLevel, hwndParent, &context);
643 ExitOnFailure(hr, "Failed to initialize internal UI for MSP package.");
644 }
645 else
646 {
647 hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->mspTarget.uiLevel, hwndParent, pvContext, fRollback, &context);
648 ExitOnFailure(hr, "Failed to initialize external UI handler.");
649 }
650
651 //if (BURN_LOGGING_LEVEL_DEBUG == logLevel)
652 //{
653 // dwLogMode | INSTALLLOGMODE_EXTRADEBUG;
654 //}
655
656 if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath)
657 {
658 hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, INSTALLLOGATTRIBUTES_APPEND);
659 ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath);
660 }
661
662 // set up properties
663 hr = MsiEngineConcatPackageProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE);
664 ExitOnFailure(hr, "Failed to add properties to argument string.");
665
666 hr = MsiEngineConcatPackageProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
667 ExitOnFailure(hr, "Failed to add properties to obfuscated argument string.");
668
669 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->mspTarget.action)
670 {
671 hr = StrAllocConcatFormattedSecure(&sczProperties, L" PATCH=\"%ls\"", sczPatches);
672 ExitOnFailure(hr, "Failed to add PATCH property to argument string.");
673
674 hr = StrAllocConcatFormatted(&sczObfuscatedProperties, L" PATCH=\"%ls\"", sczPatches);
675 ExitOnFailure(hr, "Failed to add PATCH property to obfuscated argument string.");
676 }
677
678 // Always add Burn properties last.
679 hr = MsiEngineConcatBurnProperties(pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.actionMsiProperty, pExecuteAction->mspTarget.fileVersioning, FALSE, FALSE, &sczProperties);
680 ExitOnFailure(hr, "Failed to add action property to argument string.");
681
682 hr = MsiEngineConcatBurnProperties(pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.actionMsiProperty, pExecuteAction->mspTarget.fileVersioning, FALSE, FALSE, &sczObfuscatedProperties);
683 ExitOnFailure(hr, "Failed to add action property to obfuscated argument string.");
684
685 LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode);
686
687 //
688 // Do the actual action.
689 //
690 switch (pExecuteAction->mspTarget.action)
691 {
692 case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
693 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
694 hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart);
695 ExitOnFailure(hr, "Failed to install MSP package.");
696 break;
697
698 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
699 hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart);
700 ExitOnFailure(hr, "Failed to uninstall MSP package.");
701 break;
702 }
703
704 LExit:
705 WiuUninitializeExternalUI(&context);
706
707 ReleaseStr(sczCachedDirectory);
708 ReleaseStr(sczMspPath);
709 StrSecureZeroFreeString(sczProperties);
710 ReleaseStr(sczObfuscatedProperties);
711 ReleaseStr(sczPatches);
712
713 switch (restart)
714 {
715 case WIU_RESTART_NONE:
716 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
717 break;
718
719 case WIU_RESTART_REQUIRED:
720 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
721 break;
722
723 case WIU_RESTART_INITIATED:
724 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
725 break;
726 }
727
728 // Best effort to clear the execute package cache folder and action variables.
729 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
730 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
731
732 return hr;
733 }
734
735 extern "C" void MspEngineUpdateInstallRegistrationState(
736 __in BURN_EXECUTE_ACTION* pAction,
737 __in HRESULT hrExecute,
738 __in BOOL fInsideMsiTransaction
739 )
740 {
741 BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
742
743 if (FAILED(hrExecute))
744 {
745 ExitFunction();
746 }
747
748 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->mspTarget.action)
749 {
750 newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
751 }
752 else
753 {
754 newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
755 }
756
757 for (DWORD i = 0; i < pAction->mspTarget.cOrderedPatches; ++i)
758 {
759 BURN_ORDERED_PATCHES* pOrderedPatches = pAction->mspTarget.rgOrderedPatches + i;
760 BURN_PACKAGE* pPackage = pOrderedPatches->pPackage;
761 BURN_MSPTARGETPRODUCT* pTargetProduct = NULL;
762
763 Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type);
764
765 if (!pPackage->fCanAffectRegistration)
766 {
767 continue;
768 }
769
770 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
771 {
772 pTargetProduct = pPackage->Msp.rgTargetProducts + j;
773 if (pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) &&
774 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1))
775 {
776 break;
777 }
778
779 pTargetProduct = NULL;
780 }
781
782 if (!pTargetProduct)
783 {
784 AssertSz(pTargetProduct, "Ordered patch didn't have corresponding target product");
785 continue;
786 }
787
788 if (fInsideMsiTransaction)
789 {
790 pTargetProduct->transactionRegistrationState = newState;
791 }
792 else
793 {
794 pTargetProduct->registrationState = newState;
795 }
796 }
797
798 LExit:
799 return;
800 }
801
802 extern "C" void MspEngineFinalizeInstallRegistrationState(
803 __in BURN_PACKAGE* pPackage
804 )
805 {
806 if (!pPackage->fCanAffectRegistration)
807 {
808 ExitFunction();
809 }
810
811 if (!pPackage->Msp.cTargetProductCodes)
812 {
813 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
814 }
815 else
816 {
817 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
818
819 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
820 {
821 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
822
823 if (pPackage->installRegistrationState < pTargetProduct->registrationState)
824 {
825 pPackage->installRegistrationState = pTargetProduct->registrationState;
826 }
827 }
828 }
829
830 LExit:
831 return;
832 }
833
834
835 // internal helper functions
836
837 static HRESULT GetPossibleTargetProductCodes(
838 __in BURN_PACKAGES* pPackages,
839 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
840 __inout DWORD* pcPossibleTargetProducts
841 )
842 {
843 HRESULT hr = S_OK;
844 STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL;
845 BOOL fCheckAll = FALSE;
846 WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1];
847
848 // Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up
849 // doing patch applicability for the same product code multiple times and that would confuse
850 // everything down stream.
851 hr = DictCreateStringList(&sdUniquePossibleTargetProductCodes, 5, DICT_FLAG_NONE);
852 ExitOnFailure(hr, "Failed to create unique target product codes.");
853
854 // If the patches target a specific set of product/upgrade codes, search only those. This
855 // should be much faster than searching all packages on the machine.
856 if (pPackages->rgPatchTargetCodes)
857 {
858 for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i)
859 {
860 BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i;
861
862 // If targeting a product, add the unique product code to the list.
863 if (BURN_PATCH_TARGETCODE_TYPE_PRODUCT == pTargetCode->type)
864 {
865 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts);
866 ExitOnFailure(hr, "Failed to add product code to possible target product codes.");
867 }
868 else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type)
869 {
870 // Enumerate all unique related products to the target upgrade code.
871 for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct)
872 {
873 hr = WiuEnumRelatedProducts(pTargetCode->sczTargetCode, iProduct, wzPossibleTargetProductCode);
874 if (SUCCEEDED(hr))
875 {
876 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts);
877 ExitOnFailure(hr, "Failed to add upgrade product code to possible target product codes.");
878 }
879 else if (E_BADCONFIGURATION == hr)
880 {
881 // Skip product's with bad configuration and continue.
882 LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode);
883
884 hr = S_OK;
885 }
886 }
887
888 if (E_NOMOREITEMS == hr)
889 {
890 hr = S_OK;
891 }
892 ExitOnFailure(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode);
893 }
894 else
895 {
896 // The element does not target a specific product.
897 fCheckAll = TRUE;
898
899 break;
900 }
901 }
902 }
903 else
904 {
905 fCheckAll = TRUE;
906 }
907
908 // One or more of the patches do not target a specific product so search everything on the machine.
909 if (fCheckAll)
910 {
911 for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct)
912 {
913 MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_NONE;
914
915 hr = WiuEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_ALL, iProduct, wzPossibleTargetProductCode, &context, NULL, NULL);
916 if (SUCCEEDED(hr))
917 {
918 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, context, prgPossibleTargetProducts, pcPossibleTargetProducts);
919 ExitOnFailure(hr, "Failed to add product code to search product codes.");
920 }
921 else if (E_BADCONFIGURATION == hr)
922 {
923 // Skip products with bad configuration and continue.
924 LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode);
925
926 hr = S_OK;
927 }
928 }
929
930 if (E_NOMOREITEMS == hr)
931 {
932 hr = S_OK;
933 }
934 ExitOnFailure(hr, "Failed to enumerate all products on the machine for patches applicability.");
935 }
936
937 LExit:
938 ReleaseDict(sdUniquePossibleTargetProductCodes);
939
940 return hr;
941 }
942
943 static HRESULT AddPossibleTargetProduct(
944 __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes,
945 __in_z LPCWSTR wzPossibleTargetProductCode,
946 __in MSIINSTALLCONTEXT context,
947 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
948 __inout DWORD* pcPossibleTargetProducts
949 )
950 {
951 HRESULT hr = S_OK;
952 LPWSTR pszLocalPackage = NULL;
953
954 // Only add this possible target code if we haven't queried for it already.
955 if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode))
956 {
957 // If the install context is not known, ask the Windows Installer for it. If we can't get the context
958 // then bail.
959 if (MSIINSTALLCONTEXT_NONE == context)
960 {
961 hr = WiuEnumProductsEx(wzPossibleTargetProductCode, NULL, MSIINSTALLCONTEXT_ALL, 0, NULL, &context, NULL, NULL);
962 if (FAILED(hr))
963 {
964 ExitFunction1(hr = S_OK);
965 }
966 }
967
968 hr = DictAddKey(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode);
969 ExitOnFailure(hr, "Failed to add possible target code to unique product codes.");
970
971 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3);
972 ExitOnFailure(hr, "Failed to grow array of possible target products.");
973
974 POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts;
975
976 hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode);
977 ExitOnFailure(hr, "Failed to copy possible target product code.");
978
979 // Attempt to get the local package path so we can more quickly determine patch applicability later.
980 hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage);
981 if (SUCCEEDED(hr))
982 {
983 pPossibleTargetProduct->pszLocalPackage = pszLocalPackage;
984 pszLocalPackage = NULL;
985 }
986 else
987 {
988 // Will instead call MsiDeterminePatchSequence later.
989 hr = S_OK;
990 }
991
992 pPossibleTargetProduct->context = context;
993
994 ++(*pcPossibleTargetProducts);
995 }
996
997 LExit:
998 ReleaseStr(pszLocalPackage);
999
1000 return hr;
1001 }
1002
1003 static HRESULT AddDetectedTargetProduct(
1004 __in BURN_PACKAGE* pPackage,
1005 __in DWORD dwOrder,
1006 __in_z LPCWSTR wzProductCode,
1007 __in MSIINSTALLCONTEXT context,
1008 __out DWORD* pdwTargetProductIndex
1009 )
1010 {
1011 HRESULT hr = S_OK;
1012 BURN_MSPTARGETPRODUCT* pTargetProduct = NULL;
1013
1014 *pdwTargetProductIndex = BURN_PACKAGE_INVALID_PATCH_INDEX;
1015
1016 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5);
1017 ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated.");
1018
1019 pTargetProduct = pPackage->Msp.rgTargetProducts + pPackage->Msp.cTargetProductCodes;
1020
1021 hr = ::StringCchCopyW(pTargetProduct->wzTargetProductCode, countof(pTargetProduct->wzTargetProductCode), wzProductCode);
1022 ExitOnFailure(hr, "Failed to copy target product code.");
1023
1024 pTargetProduct->context = context;
1025 pTargetProduct->dwOrder = dwOrder;
1026
1027 *pdwTargetProductIndex = pPackage->Msp.cTargetProductCodes;
1028 ++pPackage->Msp.cTargetProductCodes;
1029
1030 LExit:
1031 return hr;
1032 }
1033
1034 static HRESULT AddMsiChainedPatch(
1035 __in BURN_PACKAGE* pPackage,
1036 __in BURN_PACKAGE* pMspPackage,
1037 __in DWORD dwMspTargetProductIndex,
1038 __out DWORD* pdwChainedPatchIndex
1039 )
1040 {
1041 HRESULT hr = S_OK;
1042
1043 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPackage->Msi.rgChainedPatches), pPackage->Msi.cChainedPatches + 1, sizeof(BURN_CHAINED_PATCH), 5);
1044 ExitOnFailure(hr, "Failed to ensure enough chained patches were allocated.");
1045
1046 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pPackage->Msi.cChainedPatches;
1047 pChainedPatch->pMspPackage = pMspPackage;
1048 pChainedPatch->dwMspTargetProductIndex = dwMspTargetProductIndex;
1049
1050 *pdwChainedPatchIndex = pPackage->Msi.cChainedPatches;
1051 ++pPackage->Msi.cChainedPatches;
1052
1053 LExit:
1054 return hr;
1055 }
1056
1057 static HRESULT DeterminePatchChainedTarget(
1058 __in BURN_PACKAGES* pPackages,
1059 __in BURN_PACKAGE* pMspPackage,
1060 __in LPCWSTR wzTargetProductCode,
1061 __in DWORD dwMspTargetProductIndex
1062 )
1063 {
1064 HRESULT hr = S_OK;
1065 DWORD dwChainedPatchIndex = 0;
1066 BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + dwMspTargetProductIndex;
1067
1068 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
1069 {
1070 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
1071
1072 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1))
1073 {
1074 pTargetProduct->pChainedTargetPackage = pPackage;
1075
1076 hr = AddMsiChainedPatch(pPackage, pMspPackage, dwMspTargetProductIndex, &dwChainedPatchIndex);
1077 ExitOnFailure(hr, "Failed to add chained patch.");
1078
1079 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
1080 {
1081 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j;
1082 if (pSlipstreamMsp->pMspPackage == pMspPackage)
1083 {
1084 AssertSz(BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex, "An MSP should only show up as a slipstreamed patch in an MSI once.");
1085 pTargetProduct->fSlipstream = TRUE;
1086 pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex;
1087 break;
1088 }
1089 }
1090
1091 break;
1092 }
1093 }
1094
1095 LExit:
1096 return hr;
1097 }
1098
1099 static HRESULT PlanTargetProduct(
1100 __in BOOTSTRAPPER_DISPLAY display,
1101 __in BURN_USER_EXPERIENCE* pUserExperience,
1102 __in BOOL fRollback,
1103 __in BURN_PLAN* pPlan,
1104 __in BURN_LOGGING* pLog,
1105 __in BURN_VARIABLES* pVariables,
1106 __in BOOTSTRAPPER_ACTION_STATE actionState,
1107 __in BURN_PACKAGE* pPackage,
1108 __in BURN_MSPTARGETPRODUCT* pTargetProduct
1109 )
1110 {
1111 HRESULT hr = S_OK;
1112 BURN_EXECUTE_ACTION* rgActions = fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions;
1113 DWORD cActions = fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions;
1114 BURN_EXECUTE_ACTION* pAction = NULL;
1115 DWORD dwInsertSequence = 0;
1116
1117 // Try to find another MSP action with the exact same action (install or uninstall) targeting
1118 // the same product in the same machine context (per-user or per-machine).
1119 for (DWORD i = 0; i < cActions; ++i)
1120 {
1121 pAction = rgActions + i;
1122
1123 if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type &&
1124 pAction->mspTarget.action == actionState &&
1125 pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) &&
1126 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1))
1127 {
1128 dwInsertSequence = i;
1129 break;
1130 }
1131
1132 pAction = NULL;
1133 }
1134
1135 // If we didn't find an MSP target action already updating the product, create a new action.
1136 if (!pAction)
1137 {
1138 if (fRollback)
1139 {
1140 hr = PlanAppendRollbackAction(pPlan, &pAction);
1141 }
1142 else
1143 {
1144 hr = PlanAppendExecuteAction(pPlan, &pAction);
1145 }
1146 ExitOnFailure(hr, "Failed to plan action for target product.");
1147
1148 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET;
1149 pAction->mspTarget.action = actionState;
1150 pAction->mspTarget.pPackage = pPackage;
1151 pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context);
1152 pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage;
1153 pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream;
1154 hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0);
1155 ExitOnFailure(hr, "Failed to copy target product code.");
1156
1157 hr = MsiEnginePlanPackageOptions(display, pUserExperience, pPackage->sczId, !fRollback, pAction->mspTarget.action,
1158 &pAction->mspTarget.actionMsiProperty, &pAction->mspTarget.uiLevel, &pAction->mspTarget.fDisableExternalUiHandler, &pAction->mspTarget.fileVersioning);
1159 ExitOnFailure(hr, "Failed to get msp ui options.");
1160
1161 // If this is a per-machine target product, then the plan needs to be per-machine as well.
1162 if (pAction->mspTarget.fPerMachineTarget)
1163 {
1164 pPlan->fPerMachine = TRUE;
1165 }
1166
1167 LoggingSetPackageVariable(pPackage, pAction->mspTarget.sczTargetProductCode, fRollback, pLog, pVariables, &pAction->mspTarget.sczLogPath); // ignore errors.
1168 }
1169 else
1170 {
1171 if (!fRollback && pPackage->hCacheEvent)
1172 {
1173 // TODO: need to properly handle rolling back the caching of the package since this causes cache and execute plans to get out of sync.
1174 // Since a previous MSP target action is being updated with the new MSP,
1175 // insert a wait syncpoint to before this action since we need to cache the current MSI before using it.
1176 BURN_EXECUTE_ACTION* pWaitSyncPointAction = NULL;
1177 hr = PlanInsertExecuteAction(dwInsertSequence, pPlan, &pWaitSyncPointAction);
1178 ExitOnFailure(hr, "Failed to insert execute action.");
1179
1180 pWaitSyncPointAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_CACHE_PACKAGE;
1181 pWaitSyncPointAction->waitCachePackage.pPackage = pPackage;
1182
1183 // Since we inserted an action before the MSP target action that we will be updating, need to update the pointer.
1184 pAction = pPlan->rgExecuteActions + (dwInsertSequence + 1);
1185 }
1186 }
1187
1188 // Add our target product to the array and sort based on their order determined during detection.
1189 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2);
1190 ExitOnFailure(hr, "Failed to grow array of ordered patches.");
1191
1192 pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pTargetProduct = pTargetProduct;
1193 pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage;
1194 ++pAction->mspTarget.cOrderedPatches;
1195
1196 // Insertion sort to keep the patches ordered.
1197 for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i)
1198 {
1199 if (pAction->mspTarget.rgOrderedPatches[i].pTargetProduct->dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].pTargetProduct->dwOrder)
1200 {
1201 BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1];
1202 pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i];
1203 pAction->mspTarget.rgOrderedPatches[i] = temp;
1204 }
1205 else // no swap necessary, we're done.
1206 {
1207 break;
1208 }
1209 }
1210
1211 LExit:
1212 return hr;
1213 }