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