main
cpp 2,294 lines 83.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
12
13 // internal function declarations
14
15 static HRESULT ParseRelatedMsiFromXml(
16 __in IXMLDOMNode* pixnRelatedMsi,
17 __in BURN_RELATED_MSI* pRelatedMsi
18 );
19 static HRESULT EvaluateActionStateConditions(
20 __in BURN_VARIABLES* pVariables,
21 __in_z_opt LPCWSTR sczAddLocalCondition,
22 __in_z_opt LPCWSTR sczAddSourceCondition,
23 __in_z_opt LPCWSTR sczAdvertiseCondition,
24 __out BOOTSTRAPPER_FEATURE_STATE* pState
25 );
26 static HRESULT CalculateFeatureAction(
27 __in BOOTSTRAPPER_FEATURE_STATE currentState,
28 __in BOOTSTRAPPER_FEATURE_STATE requestedState,
29 __in BOOL fRepair,
30 __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction,
31 __inout BOOL* pfDelta
32 );
33 static HRESULT EscapePropertyArgumentString(
34 __in LPCWSTR wzProperty,
35 __inout_z LPWSTR* psczEscapedValue,
36 __in BOOL fZeroOnRealloc
37 );
38 static HRESULT ConcatFeatureActionProperties(
39 __in BURN_PACKAGE* pPackage,
40 __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions,
41 __inout_z LPWSTR* psczArguments
42 );
43 static HRESULT ConcatPatchProperty(
44 __in BURN_CACHE* pCache,
45 __in BURN_PACKAGE* pPackage,
46 __in BOOL fRollback,
47 __inout_z LPWSTR* psczArguments
48 );
49 static void RegisterSourceDirectory(
50 __in BURN_PACKAGE* pPackage,
51 __in_z LPCWSTR wzCacheDirectory
52 );
53
54
55 // function definitions
56
57 extern "C" HRESULT MsiEngineParsePackageFromXml(
58 __in IXMLDOMNode* pixnMsiPackage,
59 __in BURN_PACKAGE* pPackage
60 )
61 {
62 HRESULT hr = S_OK;
63 IXMLDOMNodeList* pixnNodes = NULL;
64 IXMLDOMNode* pixnNode = NULL;
65 DWORD cNodes = 0;
66 LPWSTR scz = NULL;
67
68 // @ProductCode
69 hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode);
70 ExitOnFailure(hr, "Failed to get @ProductCode.");
71
72 // @Language
73 hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage);
74 ExitOnFailure(hr, "Failed to get @Language.");
75
76 // @Version
77 hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz);
78 ExitOnFailure(hr, "Failed to get @Version.");
79
80 hr = VerParseVersion(scz, 0, FALSE, &pPackage->Msi.pVersion);
81 ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
82
83 if (pPackage->Msi.pVersion->fInvalid)
84 {
85 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
86 }
87
88 // @UpgradeCode
89 hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode);
90 if (E_NOTFOUND != hr)
91 {
92 ExitOnFailure(hr, "Failed to get @UpgradeCode.");
93 }
94
95 // select feature nodes
96 hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes);
97 ExitOnFailure(hr, "Failed to select feature nodes.");
98
99 // get feature node count
100 hr = pixnNodes->get_length((long*)&cNodes);
101 ExitOnFailure(hr, "Failed to get feature node count.");
102
103 if (cNodes)
104 {
105 // allocate memory for features
106 pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE);
107 ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs.");
108
109 pPackage->Msi.cFeatures = cNodes;
110
111 // parse feature elements
112 for (DWORD i = 0; i < cNodes; ++i)
113 {
114 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
115
116 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
117 ExitOnFailure(hr, "Failed to get next node.");
118
119 // @Id
120 hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId);
121 ExitOnFailure(hr, "Failed to get @Id.");
122
123 // @AddLocalCondition
124 hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition);
125 if (E_NOTFOUND != hr)
126 {
127 ExitOnFailure(hr, "Failed to get @AddLocalCondition.");
128 }
129
130 // @AddSourceCondition
131 hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition);
132 if (E_NOTFOUND != hr)
133 {
134 ExitOnFailure(hr, "Failed to get @AddSourceCondition.");
135 }
136
137 // @AdvertiseCondition
138 hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition);
139 if (E_NOTFOUND != hr)
140 {
141 ExitOnFailure(hr, "Failed to get @AdvertiseCondition.");
142 }
143
144 // @RollbackAddLocalCondition
145 hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition);
146 if (E_NOTFOUND != hr)
147 {
148 ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition.");
149 }
150
151 // @RollbackAddSourceCondition
152 hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition);
153 if (E_NOTFOUND != hr)
154 {
155 ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition.");
156 }
157
158 // @RollbackAdvertiseCondition
159 hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition);
160 if (E_NOTFOUND != hr)
161 {
162 ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition.");
163 }
164
165 // prepare next iteration
166 ReleaseNullObject(pixnNode);
167 }
168 }
169
170 ReleaseNullObject(pixnNodes); // done with the MsiFeature elements.
171
172 hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties);
173 ExitOnFailure(hr, "Failed to parse properties from XML.");
174
175 // select related MSI nodes
176 hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes);
177 ExitOnFailure(hr, "Failed to select related MSI nodes.");
178
179 // get related MSI node count
180 hr = pixnNodes->get_length((long*)&cNodes);
181 ExitOnFailure(hr, "Failed to get related MSI node count.");
182
183 if (cNodes)
184 {
185 // allocate memory for related MSIs
186 pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE);
187 ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs.");
188
189 pPackage->Msi.cRelatedMsis = cNodes;
190
191 // parse related MSI elements
192 for (DWORD i = 0; i < cNodes; ++i)
193 {
194 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
195 ExitOnFailure(hr, "Failed to get next node.");
196
197 // parse related MSI element
198 hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]);
199 ExitOnFailure(hr, "Failed to parse related MSI element.");
200
201 // prepare next iteration
202 ReleaseNullObject(pixnNode);
203 }
204 }
205
206 ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements.
207
208 // Select slipstream MSP nodes.
209 hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes);
210 ExitOnFailure(hr, "Failed to select related MSI nodes.");
211
212 hr = pixnNodes->get_length((long*)&cNodes);
213 ExitOnFailure(hr, "Failed to get related MSI node count.");
214
215 if (cNodes)
216 {
217 pPackage->Msi.rgSlipstreamMsps = reinterpret_cast<BURN_SLIPSTREAM_MSP*>(MemAlloc(sizeof(BURN_SLIPSTREAM_MSP) * cNodes, TRUE));
218 ExitOnNull(pPackage->Msi.rgSlipstreamMsps, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages.");
219
220 pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE));
221 ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids.");
222
223 pPackage->Msi.cSlipstreamMspPackages = cNodes;
224
225 // Parse slipstream MSP Ids.
226 for (DWORD i = 0; i < cNodes; ++i)
227 {
228 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
229 ExitOnFailure(hr, "Failed to get next slipstream MSP node.");
230
231 hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i);
232 ExitOnFailure(hr, "Failed to parse slipstream MSP ids.");
233
234 ReleaseNullObject(pixnNode);
235 }
236 }
237
238 hr = S_OK;
239
240 LExit:
241 ReleaseObject(pixnNodes);
242 ReleaseObject(pixnNode);
243 ReleaseStr(scz);
244
245 return hr;
246 }
247
248 extern "C" HRESULT MsiEngineParsePropertiesFromXml(
249 __in IXMLDOMNode* pixnPackage,
250 __out BURN_MSIPROPERTY** prgProperties,
251 __out DWORD* pcProperties
252 )
253 {
254 HRESULT hr = S_OK;
255 IXMLDOMNodeList* pixnNodes = NULL;
256 IXMLDOMNode* pixnNode = NULL;
257 DWORD cNodes = 0;
258
259 BURN_MSIPROPERTY* pProperties = NULL;
260
261 // select property nodes
262 hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes);
263 ExitOnFailure(hr, "Failed to select property nodes.");
264
265 // get property node count
266 hr = pixnNodes->get_length((long*)&cNodes);
267 ExitOnFailure(hr, "Failed to get property node count.");
268
269 if (cNodes)
270 {
271 // allocate memory for properties
272 pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE);
273 ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs.");
274
275 // parse property elements
276 for (DWORD i = 0; i < cNodes; ++i)
277 {
278 BURN_MSIPROPERTY* pProperty = &pProperties[i];
279
280 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
281 ExitOnFailure(hr, "Failed to get next node.");
282
283 // @Id
284 hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId);
285 ExitOnFailure(hr, "Failed to get @Id.");
286
287 // @Value
288 hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue);
289 ExitOnFailure(hr, "Failed to get @Value.");
290
291 // @RollbackValue
292 hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue);
293 if (E_NOTFOUND != hr)
294 {
295 ExitOnFailure(hr, "Failed to get @RollbackValue.");
296 }
297
298 // @Condition
299 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition);
300 if (E_NOTFOUND != hr)
301 {
302 ExitOnFailure(hr, "Failed to get @Condition.");
303 }
304
305 // prepare next iteration
306 ReleaseNullObject(pixnNode);
307 }
308 }
309
310 *pcProperties = cNodes;
311 *prgProperties = pProperties;
312 pProperties = NULL;
313
314 hr = S_OK;
315
316 LExit:
317 ReleaseNullObject(pixnNodes);
318 ReleaseMem(pProperties);
319
320 return hr;
321 }
322
323 extern "C" void MsiEnginePackageUninitialize(
324 __in BURN_PACKAGE* pPackage
325 )
326 {
327 ReleaseStr(pPackage->Msi.sczProductCode);
328 ReleaseStr(pPackage->Msi.sczUpgradeCode);
329
330 // free features
331 if (pPackage->Msi.rgFeatures)
332 {
333 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
334 {
335 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
336
337 ReleaseStr(pFeature->sczId);
338 ReleaseStr(pFeature->sczAddLocalCondition);
339 ReleaseStr(pFeature->sczAddSourceCondition);
340 ReleaseStr(pFeature->sczAdvertiseCondition);
341 ReleaseStr(pFeature->sczRollbackAddLocalCondition);
342 ReleaseStr(pFeature->sczRollbackAddSourceCondition);
343 ReleaseStr(pFeature->sczRollbackAdvertiseCondition);
344 }
345 MemFree(pPackage->Msi.rgFeatures);
346 }
347
348 // free properties
349 if (pPackage->Msi.rgProperties)
350 {
351 for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i)
352 {
353 BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i];
354
355 ReleaseStr(pProperty->sczId);
356 ReleaseStr(pProperty->sczValue);
357 ReleaseStr(pProperty->sczRollbackValue);
358 ReleaseStr(pProperty->sczCondition);
359 }
360 MemFree(pPackage->Msi.rgProperties);
361 }
362
363 // free related MSIs
364 if (pPackage->Msi.rgRelatedMsis)
365 {
366 for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i)
367 {
368 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
369
370 ReleaseStr(pRelatedMsi->sczUpgradeCode);
371 ReleaseMem(pRelatedMsi->rgdwLanguages);
372 }
373 MemFree(pPackage->Msi.rgRelatedMsis);
374 }
375
376 // free slipstream MSPs
377 if (pPackage->Msi.rgsczSlipstreamMspPackageIds)
378 {
379 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
380 {
381 ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]);
382 }
383
384 MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds);
385 }
386
387 if (pPackage->Msi.rgSlipstreamMsps)
388 {
389 MemFree(pPackage->Msi.rgSlipstreamMsps);
390 }
391
392 if (pPackage->Msi.rgChainedPatches)
393 {
394 MemFree(pPackage->Msi.rgChainedPatches);
395 }
396
397 // clear struct
398 memset(&pPackage->Msi, 0, sizeof(pPackage->Msi));
399 }
400
401 extern "C" HRESULT MsiEngineDetectInitialize(
402 __in BURN_PACKAGES* pPackages
403 )
404 {
405 AssertSz(pPackages->cPatchInfo, "MsiEngineDetectInitialize() should only be called if there are MSP packages.");
406
407 HRESULT hr = S_OK;
408
409 // Add target products for slipstream MSIs that weren't detected.
410 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
411 {
412 BURN_PACKAGE* pMsiPackage = pPackages->rgPackages + iPackage;
413 if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type)
414 {
415 for (DWORD j = 0; j < pMsiPackage->Msi.cSlipstreamMspPackages; ++j)
416 {
417 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + j;
418 Assert(pSlipstreamMsp->pMspPackage && BURN_PACKAGE_TYPE_MSP == pSlipstreamMsp->pMspPackage->type);
419
420 if (pSlipstreamMsp->pMspPackage && BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex)
421 {
422 hr = MspEngineAddMissingSlipstreamTarget(pMsiPackage, pSlipstreamMsp);
423 ExitOnFailure(hr, "Failed to add slipstreamed target product code to package: %ls", pSlipstreamMsp->pMspPackage->sczId);
424 }
425 }
426 }
427 }
428
429 LExit:
430 return hr;
431 }
432
433 extern "C" HRESULT MsiEngineDetectPackage(
434 __in BURN_PACKAGE* pPackage,
435 __in BURN_REGISTRATION* pRegistration,
436 __in BURN_USER_EXPERIENCE* pUserExperience
437 )
438 {
439 Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage);
440
441 HRESULT hr = S_OK;
442 int nCompareResult = 0;
443 LPWSTR sczInstalledVersion = NULL;
444 LPWSTR sczInstalledLanguage = NULL;
445 INSTALLSTATE installState = INSTALLSTATE_UNKNOWN;
446 BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
447 WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { };
448 VERUTIL_VERSION* pVersion = NULL;
449 UINT uLcid = 0;
450 BOOL fPerMachine = FALSE;
451 BOOL fDetectFeatures = FALSE;
452
453 // detect self by product code
454 // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED?
455 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
456 if (SUCCEEDED(hr))
457 {
458 fDetectFeatures = TRUE;
459
460 hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion);
461 ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode);
462
463 if (pVersion->fInvalid)
464 {
465 LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, pPackage->Msi.sczProductCode, sczInstalledVersion);
466 }
467
468 // compare versions
469 hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pVersion, &nCompareResult);
470 ExitOnFailure(hr, "Failed to compare version '%ls' to installed version: '%ls'", pPackage->Msi.pVersion->sczVersion, pVersion->sczVersion);
471
472 if (nCompareResult < 0)
473 {
474 pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
475 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
476 }
477 else if (nCompareResult > 0)
478 {
479 pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE;
480 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
481 }
482 else
483 {
484 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
485 }
486
487 // Report related MSI package to BA.
488 if (BOOTSTRAPPER_RELATED_OPERATION_NONE != pPackage->Msi.operation)
489 {
490 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), pVersion->sczVersion, pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(pPackage->Msi.operation));
491
492 hr = BACallbackOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pVersion, pPackage->Msi.operation);
493 ExitOnRootFailure(hr, "BA aborted detect related MSI package.");
494 }
495 }
496 else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present.
497 {
498 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
499 hr = S_OK;
500 }
501 else
502 {
503 ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode);
504 }
505
506 // detect related packages by upgrade code
507 for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i)
508 {
509 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
510
511 ReleaseVerutilVersion(pVersion);
512 pVersion = NULL;
513
514 for (DWORD iProduct = 0; ; ++iProduct)
515 {
516 // get product
517 hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode);
518 if (E_NOMOREITEMS == hr)
519 {
520 hr = S_OK;
521 break;
522 }
523 ExitOnFailure(hr, "Failed to enum related products.");
524
525 // If we found ourselves, skip because saying that a package is related to itself is nonsensical.
526 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1))
527 {
528 continue;
529 }
530
531 // get product version
532 hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
533 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr)
534 {
535 ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode);
536 fPerMachine = TRUE;
537 }
538 else
539 {
540 hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
541 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr)
542 {
543 ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode);
544 fPerMachine = FALSE;
545 }
546 else
547 {
548 hr = S_OK;
549 continue;
550 }
551 }
552
553 hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion);
554 ExitOnFailure(hr, "Failed to parse related installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, wzProductCode);
555
556 if (pVersion->fInvalid)
557 {
558 LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzProductCode, sczInstalledVersion);
559 }
560
561 // compare versions
562 if (pRelatedMsi->fMinProvided)
563 {
564 hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMinVersion, &nCompareResult);
565 ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related min version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMinVersion->sczVersion);
566
567 if (pRelatedMsi->fMinInclusive ? (nCompareResult < 0) : (nCompareResult <= 0))
568 {
569 continue;
570 }
571 }
572
573 if (pRelatedMsi->fMaxProvided)
574 {
575 hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMaxVersion, &nCompareResult);
576 ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related max version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMaxVersion->sczVersion);
577
578 if (pRelatedMsi->fMaxInclusive ? (nCompareResult > 0) : (nCompareResult >= 0))
579 {
580 continue;
581 }
582 }
583
584 // Filter by language if necessary.
585 uLcid = 0; // always reset the found language.
586 if (pRelatedMsi->cLanguages)
587 {
588 // If there is a language to get, convert it into an LCID.
589 hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage);
590 if (SUCCEEDED(hr))
591 {
592 hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid);
593 }
594
595 // Ignore related product where we can't read the language.
596 if (FAILED(hr))
597 {
598 LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL);
599
600 hr = S_OK;
601 continue;
602 }
603
604 BOOL fMatchedLcid = FALSE;
605 for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage)
606 {
607 if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage])
608 {
609 fMatchedLcid = TRUE;
610 break;
611 }
612 }
613
614 // Skip the product if the language did not meet the inclusive/exclusive criteria.
615 if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid))
616 {
617 continue;
618 }
619 }
620
621 // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade
622 // would take place since that is the overwhelmingly common use of detect-only related packages. If
623 // not detect-only then it's easy; we're clearly doing a major upgrade.
624 if (pRelatedMsi->fOnlyDetect)
625 {
626 // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade
627 // or even something else.
628 if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == pPackage->Msi.operation)
629 {
630 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
631 }
632 // It can't be a downgrade if the upgrade codes aren't the same.
633 else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState &&
634 pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1))
635 {
636 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
637 pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
638 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
639 }
640 else // we're already on the machine so the detect-only *must* be for detection purposes only.
641 {
642 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
643 }
644 }
645 else
646 {
647 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
648 pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
649 }
650
651 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), pVersion->sczVersion, uLcid, LoggingRelatedOperationToString(relatedMsiOperation));
652
653 // Pass to BA.
654 hr = BACallbackOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, pVersion, relatedMsiOperation);
655 ExitOnRootFailure(hr, "BA aborted detect related MSI package.");
656 }
657 }
658
659 // detect features
660 if (pPackage->Msi.cFeatures)
661 {
662 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
663 {
664 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
665
666 // Try to detect features state if the product is present on the machine.
667 if (fDetectFeatures)
668 {
669 hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState);
670 ExitOnFailure(hr, "Failed to query feature state.");
671
672 if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed.
673 {
674 installState = INSTALLSTATE_ABSENT;
675 }
676 }
677 else // MSI not installed then the features can't be either.
678 {
679 installState = INSTALLSTATE_ABSENT;
680 }
681
682 // set current state
683 switch (installState)
684 {
685 case INSTALLSTATE_ABSENT:
686 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
687 break;
688 case INSTALLSTATE_ADVERTISED:
689 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED;
690 break;
691 case INSTALLSTATE_LOCAL:
692 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
693 break;
694 case INSTALLSTATE_SOURCE:
695 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE;
696 break;
697 default:
698 hr = E_UNEXPECTED;
699 ExitOnRootFailure(hr, "Invalid state value.");
700 }
701
702 // Pass to BA.
703 hr = BACallbackOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState);
704 ExitOnRootFailure(hr, "BA aborted detect MSI feature.");
705 }
706 }
707
708 if (pPackage->fCanAffectRegistration)
709 {
710 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
711 }
712
713 hr = DependencyDetectChainPackage(pPackage, pRegistration);
714 ExitOnFailure(hr, "Failed to detect dependencies for MSI package.");
715
716 if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState)
717 {
718 hr = MsiEngineDetectCompatiblePackage(pPackage);
719 ExitOnFailure(hr, "Failed to detect compatible package for MSI package.");
720
721 if (BURN_PACKAGE_TYPE_MSI == pPackage->compatiblePackage.type)
722 {
723 LPCWSTR wzCompatibleProductCode = pPackage->compatiblePackage.compatibleEntry.sczId;
724 LPCWSTR wzCompatibleInstalledVersion = pPackage->compatiblePackage.Msi.sczVersion;
725
726 ReleaseVerutilVersion(pVersion);
727 pVersion = NULL;
728
729 hr = VerParseVersion(wzCompatibleInstalledVersion, 0, FALSE, &pVersion);
730 ExitOnFailure(hr, "Failed to parse dependency version: '%ls' for ProductCode: %ls", wzCompatibleInstalledVersion, wzCompatibleProductCode);
731
732 if (pVersion->fInvalid)
733 {
734 LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzCompatibleProductCode, wzCompatibleInstalledVersion);
735 }
736
737 pPackage->compatiblePackage.Msi.pVersion = pVersion;
738 pVersion = NULL;
739
740 // compare versions
741 hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pPackage->compatiblePackage.Msi.pVersion, &nCompareResult);
742 ExitOnFailure(hr, "Failed to compare version '%ls' to dependency version: '%ls'", pPackage->Msi.pVersion->sczVersion, wzCompatibleInstalledVersion);
743
744 if (nCompareResult < 0)
745 {
746 pPackage->compatiblePackage.fPlannable = TRUE;
747
748 LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczProviderKey, wzCompatibleProductCode, wzCompatibleInstalledVersion, pPackage->Msi.sczProductCode);
749
750 hr = BACallbackOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, wzCompatibleProductCode, pPackage->compatiblePackage.Msi.pVersion);
751 ExitOnRootFailure(hr, "BA aborted detect compatible MSI package.");
752 }
753 }
754 }
755
756 LExit:
757 ReleaseStr(sczInstalledLanguage);
758 ReleaseStr(sczInstalledVersion);
759 ReleaseVerutilVersion(pVersion);
760
761 return hr;
762 }
763
764 extern "C" HRESULT MsiEngineDetectCompatiblePackage(
765 __in BURN_PACKAGE* pPackage
766 )
767 {
768 HRESULT hr = S_OK;
769 LPWSTR sczVersion = NULL;
770 LPWSTR sczCacheId = NULL;
771 LPCWSTR wzCompatibleProductCode = pPackage->compatiblePackage.compatibleEntry.sczId;
772
773 if (!pPackage->compatiblePackage.fDetected)
774 {
775 ExitFunction();
776 }
777
778 hr = WiuGetProductInfoEx(wzCompatibleProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczVersion);
779 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr || E_INVALIDARG == hr)
780 {
781 ExitFunction1(hr = S_OK);
782 }
783 ExitOnFailure(hr, "Failed to get product information for compatible ProductCode: %ls", wzCompatibleProductCode);
784
785 // Assume the package used the default cache ID generation from the binder.
786 hr = StrAllocFormatted(&sczCacheId, L"%lsv%ls", wzCompatibleProductCode, sczVersion);
787 ExitOnFailure(hr, "Failed to format cache ID for compatible package.");
788
789 pPackage->compatiblePackage.sczCacheId = sczCacheId;
790 sczCacheId = NULL;
791
792 pPackage->compatiblePackage.Msi.sczVersion = sczVersion;
793 sczVersion = NULL;
794
795 pPackage->compatiblePackage.type = BURN_PACKAGE_TYPE_MSI;
796
797 LExit:
798 ReleaseStr(sczVersion);
799 ReleaseStr(sczCacheId);
800
801 return hr;
802 }
803
804 extern "C" HRESULT MsiEnginePlanInitializePackage(
805 __in BURN_PACKAGE* pPackage,
806 __in BOOTSTRAPPER_ACTION overallAction,
807 __in BURN_VARIABLES* pVariables,
808 __in BURN_USER_EXPERIENCE* pUserExperience
809 )
810 {
811 HRESULT hr = S_OK;
812
813 if (pPackage->Msi.cFeatures)
814 {
815 // get feature request states
816 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
817 {
818 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
819
820 // Evaluate feature conditions.
821 hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &pFeature->defaultRequested);
822 ExitOnFailure(hr, "Failed to evaluate requested state conditions.");
823
824 hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &pFeature->expectedState);
825 ExitOnFailure(hr, "Failed to evaluate expected state conditions.");
826
827 // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
828 pFeature->requested = pFeature->defaultRequested;
829
830 // Send plan MSI feature message to BA.
831 hr = BACallbackOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &pFeature->requested);
832 ExitOnRootFailure(hr, "BA aborted plan MSI feature.");
833 }
834 }
835
836 if (pPackage->compatiblePackage.fPlannable)
837 {
838 Assert(BURN_PACKAGE_TYPE_MSI == pPackage->compatiblePackage.type);
839
840 pPackage->compatiblePackage.fDefaultRequested = BOOTSTRAPPER_ACTION_UNINSTALL == overallAction || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == overallAction;
841 pPackage->compatiblePackage.fRequested = pPackage->compatiblePackage.fDefaultRequested;
842
843 hr = BACallbackOnPlanCompatibleMsiPackageBegin(pUserExperience, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, pPackage->compatiblePackage.Msi.pVersion, &pPackage->compatiblePackage.fRequested);
844 BACallbackOnPlanCompatibleMsiPackageComplete(pUserExperience, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, hr, pPackage->compatiblePackage.fRequested);
845 ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin.");
846 }
847
848 LExit:
849 return hr;
850 }
851
852 //
853 // PlanCalculate - calculates the execute and rollback state for the requested package state.
854 //
855 extern "C" HRESULT MsiEnginePlanCalculatePackage(
856 __in BURN_PACKAGE* pPackage,
857 __in BOOL fInsideMsiTransaction
858 )
859 {
860 Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage);
861
862 HRESULT hr = S_OK;
863 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
864 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
865 BOOL fFeatureActionDelta = FALSE;
866 BOOL fRollbackFeatureActionDelta = FALSE;
867
868 if (pPackage->Msi.cFeatures)
869 {
870 // If the package is present and we're repairing it, or we're doing a minor update.
871 BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) ||
872 BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE == pPackage->Msi.operation;
873
874 // plan features
875 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
876 {
877 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
878
879 // Calculate feature actions.
880 hr = CalculateFeatureAction(pFeature->currentState, pFeature->requested, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta);
881 ExitOnFailure(hr, "Failed to calculate execute feature state.");
882
883 hr = CalculateFeatureAction(pFeature->requested, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? pFeature->expectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta);
884 ExitOnFailure(hr, "Failed to calculate rollback feature state.");
885 }
886 }
887
888 // execute action
889 switch (pPackage->currentState)
890 {
891 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
892 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
893 if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)
894 {
895 if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)
896 {
897 execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
898 }
899 else
900 {
901 execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE;
902 }
903 }
904 else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) &&
905 !pPackage->fPermanent) // removing a package that should be removed.
906 {
907 execute = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == pPackage->currentState ? BOOTSTRAPPER_ACTION_STATE_NONE : BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
908 }
909 else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested)
910 {
911 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
912 }
913 else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT == pPackage->requested)
914 {
915 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
916 }
917 else
918 {
919 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
920 }
921 break;
922
923 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
924 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
925 switch (pPackage->requested)
926 {
927 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
928 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
929 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
930 if (BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE == pPackage->Msi.operation)
931 {
932 execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE;
933 }
934 else
935 {
936 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
937 }
938 break;
939
940 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
941 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
942 break;
943
944 default:
945 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
946 break;
947 }
948 break;
949
950 default:
951 hr = E_INVALIDARG;
952 ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState);
953 }
954
955 // Calculate the rollback action if there is an execute action.
956 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction)
957 {
958 switch (pPackage->currentState)
959 {
960 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
961 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
962 switch (pPackage->requested)
963 {
964 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
965 rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE;
966 break;
967 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
968 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
969 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
970 break;
971 case BOOTSTRAPPER_REQUEST_STATE_CACHE: __fallthrough;
972 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
973 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
974 rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL == execute ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
975 break;
976 default:
977 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
978 break;
979 }
980 break;
981
982 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
983 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
984 // If the package is not permanent and we requested to put the package on the machine then
985 // remove the package during rollback.
986 if (!pPackage->fPermanent &&
987 (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested ||
988 BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT == pPackage->requested ||
989 BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested))
990 {
991 rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
992 }
993 else
994 {
995 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
996 }
997 break;
998
999 default:
1000 hr = E_INVALIDARG;
1001 ExitOnRootFailure(hr, "Invalid package detection result encountered.");
1002 }
1003 }
1004
1005 // return values
1006 pPackage->execute = execute;
1007 pPackage->rollback = rollback;
1008
1009 LExit:
1010 return hr;
1011 }
1012
1013 //
1014 // PlanAdd - adds the calculated execute and rollback actions for the package.
1015 //
1016 extern "C" HRESULT MsiEnginePlanAddPackage(
1017 __in BOOTSTRAPPER_DISPLAY display,
1018 __in BURN_USER_EXPERIENCE* pUserExperience,
1019 __in BURN_PACKAGE* pPackage,
1020 __in BURN_PLAN* pPlan,
1021 __in BURN_LOGGING* pLog,
1022 __in BURN_VARIABLES* pVariables
1023 )
1024 {
1025 HRESULT hr = S_OK;
1026 BURN_EXECUTE_ACTION* pAction = NULL;
1027 BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL;
1028 BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL;
1029
1030 if (pPackage->Msi.cFeatures)
1031 {
1032 // Allocate and populate array for feature actions.
1033 rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE);
1034 ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions.");
1035
1036 rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE);
1037 ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions.");
1038
1039 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1040 {
1041 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1042
1043 // calculate feature actions
1044 rgFeatureActions[i] = pFeature->execute;
1045 rgRollbackFeatureActions[i] = pFeature->rollback;
1046 }
1047 }
1048
1049 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
1050 ExitOnFailure(hr, "Failed to plan package dependency actions.");
1051
1052 if (pPackage->compatiblePackage.fRemove)
1053 {
1054 hr = PlanAppendExecuteAction(pPlan, &pAction);
1055 ExitOnFailure(hr, "Failed to append execute action.");
1056
1057 pAction->type = BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE;
1058 pAction->uninstallMsiCompatiblePackage.pParentPackage = pPackage;
1059 pAction->uninstallMsiCompatiblePackage.dwLoggingAttributes = pLog->dwAttributes;
1060
1061 LoggingSetCompatiblePackageVariable(pPackage, pLog, pVariables, &pAction->uninstallMsiCompatiblePackage.sczLogPath); // ignore errors.
1062 }
1063
1064 // add rollback action
1065 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
1066 {
1067 hr = PlanAppendRollbackAction(pPlan, &pAction);
1068 ExitOnFailure(hr, "Failed to append rollback action.");
1069
1070 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
1071 pAction->msiPackage.pPackage = pPackage;
1072 pAction->msiPackage.action = pPackage->rollback;
1073 pAction->msiPackage.rgFeatures = rgRollbackFeatureActions;
1074 rgRollbackFeatureActions = NULL;
1075
1076 hr = MsiEnginePlanPackageOptions(display, pUserExperience, pPackage->sczId, FALSE, pAction->msiPackage.action,
1077 &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler, &pAction->msiPackage.fileVersioning);
1078 ExitOnFailure(hr, "Failed to get msi ui options.");
1079
1080 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
1081 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
1082
1083 // Plan a checkpoint between rollback and execute so that we always attempt
1084 // rollback in the case that the MSI was not able to rollback itself (e.g.
1085 // user pushes cancel after InstallFinalize).
1086 hr = PlanExecuteCheckpoint(pPlan);
1087 ExitOnFailure(hr, "Failed to append execute checkpoint.");
1088 }
1089
1090 // add execute action
1091 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
1092 {
1093 hr = PlanAppendExecuteAction(pPlan, &pAction);
1094 ExitOnFailure(hr, "Failed to append execute action.");
1095
1096 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
1097 pAction->msiPackage.pPackage = pPackage;
1098 pAction->msiPackage.action = pPackage->execute;
1099 pAction->msiPackage.rgFeatures = rgFeatureActions;
1100 rgFeatureActions = NULL;
1101
1102 hr = MsiEnginePlanPackageOptions(display, pUserExperience, pPackage->sczId, TRUE, pAction->msiPackage.action,
1103 &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler, &pAction->msiPackage.fileVersioning);
1104 ExitOnFailure(hr, "Failed to get msi ui options.");
1105
1106 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
1107 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
1108 }
1109
1110 LExit:
1111 ReleaseMem(rgFeatureActions);
1112 ReleaseMem(rgRollbackFeatureActions);
1113
1114 return hr;
1115 }
1116
1117 extern "C" HRESULT MsiEngineBeginTransaction(
1118 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1119 )
1120 {
1121 HRESULT hr = S_OK;
1122 MSIHANDLE hTransactionHandle = NULL;
1123 HANDLE hChangeOfOwnerEvent = NULL;
1124
1125 LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_BEGIN, pRollbackBoundary->sczId);
1126
1127 hr = WiuBeginTransaction(pRollbackBoundary->sczId, 0, &hTransactionHandle, &hChangeOfOwnerEvent, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath);
1128
1129 if (HRESULT_FROM_WIN32(ERROR_ROLLBACK_DISABLED) == hr)
1130 {
1131 LogId(REPORT_ERROR, MSG_MSI_TRANSACTIONS_DISABLED);
1132 }
1133
1134 ExitOnFailure(hr, "Failed to begin an MSI transaction");
1135
1136 LExit:
1137 // Assume that MsiEndTransaction cleans up the handles.
1138
1139 return hr;
1140 }
1141
1142 extern "C" HRESULT MsiEngineCommitTransaction(
1143 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
1144 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1145 )
1146 {
1147 HRESULT hr = S_OK;
1148 WIU_RESTART restart = WIU_RESTART_NONE;
1149
1150 LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_COMMIT, pRollbackBoundary->sczId);
1151
1152 hr = WiuEndTransaction(MSITRANSACTIONSTATE_COMMIT, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath, &restart);
1153 ExitOnFailure(hr, "Failed to commit the MSI transaction");
1154
1155 LExit:
1156 switch (restart)
1157 {
1158 case WIU_RESTART_NONE:
1159 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1160 break;
1161
1162 case WIU_RESTART_REQUIRED:
1163 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1164 break;
1165
1166 case WIU_RESTART_INITIATED:
1167 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1168 break;
1169 }
1170
1171 return hr;
1172 }
1173
1174 extern "C" HRESULT MsiEngineRollbackTransaction(
1175 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
1176 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1177 )
1178 {
1179 HRESULT hr = S_OK;
1180 WIU_RESTART restart = WIU_RESTART_NONE;
1181
1182 LogId(REPORT_WARNING, MSG_MSI_TRANSACTION_ROLLBACK, pRollbackBoundary->sczId);
1183
1184 hr = WiuEndTransaction(MSITRANSACTIONSTATE_ROLLBACK, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath, &restart);
1185 ExitOnFailure(hr, "Failed to rollback the MSI transaction");
1186
1187 LExit:
1188 switch (restart)
1189 {
1190 case WIU_RESTART_NONE:
1191 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1192 break;
1193
1194 case WIU_RESTART_REQUIRED:
1195 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1196 break;
1197
1198 case WIU_RESTART_INITIATED:
1199 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1200 break;
1201 }
1202
1203 return hr;
1204 }
1205
1206 extern "C" HRESULT MsiEngineExecutePackage(
1207 __in_opt HWND hwndParent,
1208 __in BURN_EXECUTE_ACTION* pExecuteAction,
1209 __in BURN_CACHE* pCache,
1210 __in BURN_VARIABLES* pVariables,
1211 __in BOOL fRollback,
1212 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
1213 __in LPVOID pvContext,
1214 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1215 )
1216 {
1217 HRESULT hr = S_OK;
1218 WIU_MSI_EXECUTE_CONTEXT context = { };
1219 WIU_RESTART restart = WIU_RESTART_NONE;
1220
1221 LPWSTR sczInstalledVersion = NULL;
1222 LPWSTR sczCachedDirectory = NULL;
1223 LPWSTR sczMsiPath = NULL;
1224 LPWSTR sczProperties = NULL;
1225 LPWSTR sczObfuscatedProperties = NULL;
1226 BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage;
1227 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
1228
1229 // During rollback, if the package is already in the rollback state we expect don't
1230 // touch it again.
1231 if (fRollback)
1232 {
1233 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action)
1234 {
1235 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1236 if (FAILED(hr)) // package not present.
1237 {
1238 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT));
1239
1240 hr = S_OK;
1241 ExitFunction();
1242 }
1243 }
1244 else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action)
1245 {
1246 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1247 if (SUCCEEDED(hr)) // package present.
1248 {
1249 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT));
1250
1251 hr = S_OK;
1252 ExitFunction();
1253 }
1254
1255 hr = S_OK;
1256 }
1257 }
1258
1259 // Default to "verbose" logging and set extra debug mode only if explicitly required.
1260 DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
1261
1262 if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
1263 {
1264 dwLogMode |= INSTALLLOGMODE_EXTRADEBUG;
1265 }
1266
1267 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action)
1268 {
1269 // get cached MSI path
1270 hr = CacheGetCompletedPath(pCache, pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory);
1271 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId);
1272
1273 // Best effort to set the execute package cache folder variable.
1274 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
1275
1276 hr = PathConcatRelativeToFullyQualifiedBase(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsiPath);
1277 ExitOnFailure(hr, "Failed to build MSI path.");
1278 }
1279
1280 // Best effort to set the execute package action variable.
1281 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE);
1282
1283 // Wire up the external UI handler and logging.
1284 if (pExecuteAction->msiPackage.fDisableExternalUiHandler)
1285 {
1286 hr = WiuInitializeInternalUI(pExecuteAction->msiPackage.uiLevel, hwndParent, &context);
1287 ExitOnFailure(hr, "Failed to initialize internal UI for MSI package.");
1288 }
1289 else
1290 {
1291 hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context);
1292 ExitOnFailure(hr, "Failed to initialize external UI handler.");
1293 }
1294
1295 if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath)
1296 {
1297 hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, INSTALLLOGATTRIBUTES_APPEND);
1298 ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pPackage->sczId, pExecuteAction->msiPackage.sczLogPath);
1299 }
1300
1301 // set up properties
1302 hr = MsiEngineConcatPackageProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE);
1303 ExitOnFailure(hr, "Failed to add properties to argument string.");
1304
1305 hr = MsiEngineConcatPackageProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
1306 ExitOnFailure(hr, "Failed to add obfuscated properties to argument string.");
1307
1308 // add feature action properties
1309 hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties);
1310 ExitOnFailure(hr, "Failed to add feature action properties to argument string.");
1311
1312 hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties);
1313 ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string.");
1314
1315 // add slipstream patch properties
1316 hr = ConcatPatchProperty(pCache, pPackage, fRollback, &sczProperties);
1317 ExitOnFailure(hr, "Failed to add patch properties to argument string.");
1318
1319 hr = ConcatPatchProperty(pCache, pPackage, fRollback, &sczObfuscatedProperties);
1320 ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string.");
1321
1322 hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, 0 != pPackage->Msi.cFeatures, &sczProperties);
1323 ExitOnFailure(hr, "Failed to add action property to argument string.");
1324
1325 hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, 0 != pPackage->Msi.cFeatures, &sczObfuscatedProperties);
1326 ExitOnFailure(hr, "Failed to add action property to obfuscated argument string.");
1327
1328 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L"");
1329
1330 //
1331 // Do the actual action.
1332 //
1333 switch (pExecuteAction->msiPackage.action)
1334 {
1335 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
1336 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1337 ExitOnFailure(hr, "Failed to install MSI package.");
1338
1339 RegisterSourceDirectory(pPackage, sczMsiPath);
1340 break;
1341
1342 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
1343 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1344 ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package.");
1345
1346 RegisterSourceDirectory(pPackage, sczMsiPath);
1347 break;
1348
1349 case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough;
1350 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
1351 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1352 ExitOnFailure(hr, "Failed to run maintenance mode for MSI package.");
1353 break;
1354
1355 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1356 hr = WiuConfigureProductEx(pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart);
1357 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1358 {
1359 LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId);
1360 hr = S_OK;
1361 }
1362 ExitOnFailure(hr, "Failed to uninstall MSI package.");
1363 break;
1364 }
1365
1366 LExit:
1367 WiuUninitializeExternalUI(&context);
1368
1369 StrSecureZeroFreeString(sczProperties);
1370 ReleaseStr(sczObfuscatedProperties);
1371 ReleaseStr(sczMsiPath);
1372 ReleaseStr(sczCachedDirectory);
1373 ReleaseStr(sczInstalledVersion);
1374
1375 switch (restart)
1376 {
1377 case WIU_RESTART_NONE:
1378 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1379 break;
1380
1381 case WIU_RESTART_REQUIRED:
1382 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1383 break;
1384
1385 case WIU_RESTART_INITIATED:
1386 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1387 break;
1388 }
1389
1390 // Best effort to clear the execute package cache folder and action variables.
1391 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
1392 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
1393
1394 return hr;
1395 }
1396
1397 extern "C" HRESULT MsiEngineUninstallCompatiblePackage(
1398 __in_opt HWND hwndParent,
1399 __in BURN_EXECUTE_ACTION* pExecuteAction,
1400 __in BURN_CACHE* /*pCache*/,
1401 __in BURN_VARIABLES* /*pVariables*/,
1402 __in BOOL fRollback,
1403 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
1404 __in LPVOID pvContext,
1405 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1406 )
1407 {
1408 HRESULT hr = S_OK;
1409 WIU_MSI_EXECUTE_CONTEXT context = { };
1410 WIU_RESTART restart = WIU_RESTART_NONE;
1411 LPWSTR sczProperties = NULL;
1412 BOOTSTRAPPER_ACTION_STATE action = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
1413 BURN_MSI_PROPERTY burnMsiProperty = BURN_MSI_PROPERTY_NONE;
1414 BOOTSTRAPPER_MSI_FILE_VERSIONING fileVersioning = BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER;
1415 BURN_PACKAGE* pParentPackage = pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage;
1416 BURN_COMPATIBLE_PROVIDER_ENTRY* pCompatibleEntry = &pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage->compatiblePackage.compatibleEntry;
1417
1418 // Default to "verbose" logging and set extra debug mode only if explicitly required.
1419 DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
1420
1421 if (pExecuteAction->uninstallMsiCompatiblePackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
1422 {
1423 dwLogMode |= INSTALLLOGMODE_EXTRADEBUG;
1424 }
1425
1426 hr = WiuInitializeExternalUI(pfnMessageHandler, INSTALLUILEVEL_NONE, hwndParent, pvContext, fRollback, &context);
1427 ExitOnFailure(hr, "Failed to initialize external UI handler.");
1428
1429 if (pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath && *pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath)
1430 {
1431 hr = WiuEnableLog(dwLogMode, pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath, INSTALLLOGATTRIBUTES_APPEND);
1432 ExitOnFailure(hr, "Failed to enable logging for compatible package: %ls to: %ls", pCompatibleEntry->sczId, pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath);
1433 }
1434
1435 hr = MsiEngineConcatBurnProperties(action, burnMsiProperty, fileVersioning, TRUE, FALSE, &sczProperties);
1436 ExitOnFailure(hr, "Failed to add action property to argument string.");
1437
1438 LogId(REPORT_STANDARD, MSG_APPLYING_ORPHAN_COMPATIBLE_PACKAGE, LoggingRollbackOrExecute(fRollback), pCompatibleEntry->sczId, pParentPackage->sczId, LoggingActionStateToString(action), sczProperties ? sczProperties : L"");
1439
1440 hr = WiuConfigureProductEx(pCompatibleEntry->sczId, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart);
1441 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1442 {
1443 LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pCompatibleEntry->sczId);
1444 hr = S_OK;
1445 }
1446 ExitOnFailure(hr, "Failed to uninstall compatible MSI package.");
1447
1448 LExit:
1449 WiuUninitializeExternalUI(&context);
1450
1451 StrSecureZeroFreeString(sczProperties);
1452
1453 switch (restart)
1454 {
1455 case WIU_RESTART_NONE:
1456 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1457 break;
1458
1459 case WIU_RESTART_REQUIRED:
1460 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1461 break;
1462
1463 case WIU_RESTART_INITIATED:
1464 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1465 break;
1466 }
1467
1468 return hr;
1469 }
1470
1471 extern "C" HRESULT MsiEngineConcatBurnProperties(
1472 __in BOOTSTRAPPER_ACTION_STATE action,
1473 __in BURN_MSI_PROPERTY actionMsiProperty,
1474 __in BOOTSTRAPPER_MSI_FILE_VERSIONING fileVersioning,
1475 __in BOOL fMsiPackage,
1476 __in BOOL fFeatureSelectionEnabled,
1477 __deref_out_z LPWSTR* psczProperties
1478 )
1479 {
1480 HRESULT hr = S_OK;
1481 LPCWSTR wzPropertyName = NULL;
1482 LPCWSTR wzReinstallModeOptions = NULL;
1483 LPCWSTR wzFileVersioning = L"";
1484 BOOL fReinstallAll = FALSE;
1485 BOOL fIgnoreDependencies = FALSE;
1486
1487 switch (action)
1488 {
1489 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
1490 wzReinstallModeOptions = L"mus";
1491 break;
1492 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
1493 if (fMsiPackage)
1494 {
1495 // If feature selection is not enabled,
1496 // then reinstall the existing features to ensure they get updated.
1497 fReinstallAll = !fFeatureSelectionEnabled;
1498
1499 wzReinstallModeOptions = L"vmus";
1500 }
1501 break;
1502 case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough;
1503 case BOOTSTRAPPER_ACTION_STATE_MODIFY:
1504 if (fMsiPackage)
1505 {
1506 fReinstallAll = BOOTSTRAPPER_ACTION_STATE_MODIFY != action && !fFeatureSelectionEnabled;
1507 wzReinstallModeOptions = L"cmus";
1508 fIgnoreDependencies = TRUE;
1509 }
1510 else
1511 {
1512 wzReinstallModeOptions = L"mus";
1513 }
1514 break;
1515 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1516 fIgnoreDependencies = TRUE;
1517 break;
1518 }
1519
1520 switch (actionMsiProperty)
1521 {
1522 case BURN_MSI_PROPERTY_INSTALL:
1523 wzPropertyName = BURNMSIINSTALL_PROPERTY_NAME;
1524 break;
1525 case BURN_MSI_PROPERTY_MODIFY:
1526 wzPropertyName = BURNMSIMODIFY_PROPERTY_NAME;
1527 break;
1528 case BURN_MSI_PROPERTY_REPAIR:
1529 wzPropertyName = BURNMSIREPAIR_PROPERTY_NAME;
1530 break;
1531 case BURN_MSI_PROPERTY_UNINSTALL:
1532 wzPropertyName = BURNMSIUNINSTALL_PROPERTY_NAME;
1533 break;
1534 }
1535
1536 if (wzPropertyName)
1537 {
1538 hr = StrAllocConcatFormattedSecure(psczProperties, L" %ls=1", wzPropertyName);
1539 ExitOnFailure(hr, "Failed to add burn action property.");
1540 }
1541
1542 if (fReinstallAll)
1543 {
1544 hr = StrAllocConcatSecure(psczProperties, L" REINSTALL=ALL", 0);
1545 ExitOnFailure(hr, "Failed to add reinstall all property.");
1546 }
1547
1548 if (wzReinstallModeOptions)
1549 {
1550 switch (fileVersioning)
1551 {
1552 case BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER:
1553 wzFileVersioning = L"o";
1554 break;
1555 case BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER_OR_EQUAL:
1556 wzFileVersioning = L"e";
1557 break;
1558 case BOOTSTRAPPER_MSI_FILE_VERSIONING_ALL:
1559 wzFileVersioning = L"a";
1560 break;
1561 }
1562
1563 hr = StrAllocConcatFormattedSecure(psczProperties, L" REINSTALLMODE=\"%ls%ls\"", wzReinstallModeOptions, wzFileVersioning);
1564 ExitOnFailure(hr, "Failed to add reinstall mode.");
1565 }
1566
1567 hr = StrAllocConcatSecure(psczProperties, L" REBOOT=ReallySuppress", 0);
1568 ExitOnFailure(hr, "Failed to add reboot suppression property.");
1569
1570 if (fIgnoreDependencies)
1571 {
1572 // Ignore all dependencies, since the Burn engine already performed the check.
1573 hr = StrAllocConcatFormattedSecure(psczProperties, L" %ls=ALL", DEPENDENCY_IGNOREDEPENDENCIES);
1574 ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
1575 }
1576
1577 LExit:
1578 return hr;
1579 }
1580
1581 extern "C" HRESULT MsiEngineConcatPackageProperties(
1582 __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties,
1583 __in DWORD cProperties,
1584 __in BURN_VARIABLES* pVariables,
1585 __in BOOL fRollback,
1586 __deref_out_z LPWSTR* psczProperties,
1587 __in BOOL fObfuscateHiddenVariables
1588 )
1589 {
1590 HRESULT hr = S_OK;
1591 LPWSTR sczValue = NULL;
1592 LPWSTR sczEscapedValue = NULL;
1593 LPWSTR sczProperty = NULL;
1594
1595 for (DWORD i = 0; i < cProperties; ++i)
1596 {
1597 BURN_MSIPROPERTY* pProperty = &rgProperties[i];
1598
1599 if (pProperty->sczCondition && *pProperty->sczCondition)
1600 {
1601 BOOL fCondition = FALSE;
1602
1603 hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition);
1604 if (FAILED(hr) || !fCondition)
1605 {
1606 LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition));
1607 continue;
1608 }
1609 }
1610
1611 // format property value
1612 if (fObfuscateHiddenVariables)
1613 {
1614 hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL);
1615 }
1616 else
1617 {
1618 hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL);
1619 ExitOnFailure(hr, "Failed to format property value.");
1620 }
1621 ExitOnFailure(hr, "Failed to format property value.");
1622
1623 // escape property value
1624 hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables);
1625 ExitOnFailure(hr, "Failed to escape string.");
1626
1627 // build part
1628 hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue);
1629 ExitOnFailure(hr, "Failed to format property string part.");
1630
1631 // append to property string
1632 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0);
1633 ExitOnFailure(hr, "Failed to append property string part.");
1634 }
1635
1636 LExit:
1637 StrSecureZeroFreeString(sczValue);
1638 StrSecureZeroFreeString(sczEscapedValue);
1639 StrSecureZeroFreeString(sczProperty);
1640 return hr;
1641 }
1642
1643 extern "C" HRESULT MsiEnginePlanPackageOptions(
1644 __in BOOTSTRAPPER_DISPLAY display,
1645 __in BURN_USER_EXPERIENCE* pUserExperience,
1646 __in LPCWSTR wzPackageId,
1647 __in BOOL fExecute,
1648 __in BOOTSTRAPPER_ACTION_STATE actionState,
1649 __out BURN_MSI_PROPERTY* pActionMsiProperty,
1650 __out INSTALLUILEVEL* pUiLevel,
1651 __out BOOL* pfDisableExternalUiHandler,
1652 __out BOOTSTRAPPER_MSI_FILE_VERSIONING* pFileVersioning
1653 )
1654 {
1655 *pUiLevel = INSTALLUILEVEL_NONE;
1656 *pfDisableExternalUiHandler = FALSE;
1657 *pFileVersioning = BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER;
1658
1659 if (BOOTSTRAPPER_DISPLAY_FULL == display ||
1660 BOOTSTRAPPER_DISPLAY_PASSIVE == display)
1661 {
1662 *pUiLevel = static_cast<INSTALLUILEVEL>(*pUiLevel | INSTALLUILEVEL_SOURCERESONLY);
1663 }
1664
1665 switch (actionState)
1666 {
1667 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1668 *pActionMsiProperty = BURN_MSI_PROPERTY_UNINSTALL;
1669 break;
1670 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
1671 *pActionMsiProperty = BURN_MSI_PROPERTY_REPAIR;
1672 *pFileVersioning = BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER_OR_EQUAL;
1673 break;
1674 case BOOTSTRAPPER_ACTION_STATE_MODIFY:
1675 *pActionMsiProperty = BURN_MSI_PROPERTY_MODIFY;
1676 break;
1677 default:
1678 *pActionMsiProperty = BURN_MSI_PROPERTY_INSTALL;
1679 break;
1680 }
1681
1682 return BACallbackOnPlanMsiPackage(pUserExperience, wzPackageId, fExecute, actionState, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler, pFileVersioning);
1683 }
1684
1685 extern "C" void MsiEngineUpdateInstallRegistrationState(
1686 __in BURN_EXECUTE_ACTION* pAction,
1687 __in BOOL fRollback,
1688 __in HRESULT hrExecute,
1689 __in BOOL fInsideMsiTransaction
1690 )
1691 {
1692 BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
1693 BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage;
1694
1695 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
1696 {
1697 ExitFunction();
1698 }
1699
1700 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action)
1701 {
1702 newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
1703 }
1704 else
1705 {
1706 newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
1707 }
1708
1709 if (fInsideMsiTransaction)
1710 {
1711 pPackage->transactionRegistrationState = newState;
1712 }
1713 else
1714 {
1715 pPackage->installRegistrationState = newState;
1716 }
1717
1718 if (BURN_PACKAGE_REGISTRATION_STATE_ABSENT == newState)
1719 {
1720 for (DWORD i = 0; i < pPackage->Msi.cChainedPatches; ++i)
1721 {
1722 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + i;
1723 BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex;
1724
1725 if (fInsideMsiTransaction)
1726 {
1727 pTargetProduct->transactionRegistrationState = newState;
1728 }
1729 else
1730 {
1731 pTargetProduct->registrationState = newState;
1732 }
1733 }
1734 }
1735 else
1736 {
1737 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
1738 {
1739 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i;
1740 BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute;
1741
1742 if (BOOTSTRAPPER_ACTION_STATE_INSTALL > patchExecuteAction)
1743 {
1744 continue;
1745 }
1746
1747 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex;
1748 BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex;
1749
1750 if (fInsideMsiTransaction)
1751 {
1752 pTargetProduct->transactionRegistrationState = newState;
1753 }
1754 else
1755 {
1756 pTargetProduct->registrationState = newState;
1757 }
1758 }
1759 }
1760
1761 LExit:
1762 return;
1763 }
1764
1765
1766 // internal helper functions
1767
1768 static HRESULT ParseRelatedMsiFromXml(
1769 __in IXMLDOMNode* pixnRelatedMsi,
1770 __in BURN_RELATED_MSI* pRelatedMsi
1771 )
1772 {
1773 HRESULT hr = S_OK;
1774 IXMLDOMNodeList* pixnNodes = NULL;
1775 IXMLDOMNode* pixnNode = NULL;
1776 DWORD cNodes = 0;
1777 LPWSTR scz = NULL;
1778
1779 // @Id
1780 hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode);
1781 ExitOnFailure(hr, "Failed to get @Id.");
1782
1783 // @MinVersion
1784 hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz);
1785 if (E_NOTFOUND != hr)
1786 {
1787 ExitOnFailure(hr, "Failed to get @MinVersion.");
1788
1789 hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMinVersion);
1790 ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz);
1791
1792 if (pRelatedMsi->pMinVersion->fInvalid)
1793 {
1794 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
1795 }
1796
1797 // flag that we have a min version
1798 pRelatedMsi->fMinProvided = TRUE;
1799
1800 // @MinInclusive
1801 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive);
1802 ExitOnFailure(hr, "Failed to get @MinInclusive.");
1803 }
1804
1805 // @MaxVersion
1806 hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz);
1807 if (E_NOTFOUND != hr)
1808 {
1809 ExitOnFailure(hr, "Failed to get @MaxVersion.");
1810
1811 hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMaxVersion);
1812 ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz);
1813
1814 if (pRelatedMsi->pMaxVersion->fInvalid)
1815 {
1816 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
1817 }
1818
1819 // flag that we have a max version
1820 pRelatedMsi->fMaxProvided = TRUE;
1821
1822 // @MaxInclusive
1823 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive);
1824 ExitOnFailure(hr, "Failed to get @MaxInclusive.");
1825 }
1826
1827 // @OnlyDetect
1828 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect);
1829 ExitOnFailure(hr, "Failed to get @OnlyDetect.");
1830
1831 // select language nodes
1832 hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes);
1833 ExitOnFailure(hr, "Failed to select language nodes.");
1834
1835 // get language node count
1836 hr = pixnNodes->get_length((long*)&cNodes);
1837 ExitOnFailure(hr, "Failed to get language node count.");
1838
1839 if (cNodes)
1840 {
1841 // @LangInclusive
1842 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive);
1843 ExitOnFailure(hr, "Failed to get @LangInclusive.");
1844
1845 // allocate memory for language IDs
1846 pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE);
1847 ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs.");
1848
1849 pRelatedMsi->cLanguages = cNodes;
1850
1851 // parse language elements
1852 for (DWORD i = 0; i < cNodes; ++i)
1853 {
1854 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
1855 ExitOnFailure(hr, "Failed to get next node.");
1856
1857 // @Id
1858 hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]);
1859 ExitOnFailure(hr, "Failed to get Language/@Id.");
1860
1861 // prepare next iteration
1862 ReleaseNullObject(pixnNode);
1863 }
1864 }
1865
1866 hr = S_OK;
1867
1868 LExit:
1869 ReleaseObject(pixnNodes);
1870 ReleaseObject(pixnNode);
1871 ReleaseStr(scz);
1872
1873 return hr;
1874 }
1875
1876 static HRESULT EvaluateActionStateConditions(
1877 __in BURN_VARIABLES* pVariables,
1878 __in_z_opt LPCWSTR sczAddLocalCondition,
1879 __in_z_opt LPCWSTR sczAddSourceCondition,
1880 __in_z_opt LPCWSTR sczAdvertiseCondition,
1881 __out BOOTSTRAPPER_FEATURE_STATE* pState
1882 )
1883 {
1884 HRESULT hr = S_OK;
1885 BOOL fCondition = FALSE;
1886
1887 // if no condition was set, return no feature state
1888 if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition)
1889 {
1890 *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1891 ExitFunction();
1892 }
1893
1894 if (sczAddLocalCondition)
1895 {
1896 hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition);
1897 ExitOnFailure(hr, "Failed to evaluate add local condition.");
1898
1899 if (fCondition)
1900 {
1901 *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
1902 ExitFunction();
1903 }
1904 }
1905
1906 if (sczAddSourceCondition)
1907 {
1908 hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition);
1909 ExitOnFailure(hr, "Failed to evaluate add source condition.");
1910
1911 if (fCondition)
1912 {
1913 *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE;
1914 ExitFunction();
1915 }
1916 }
1917
1918 if (sczAdvertiseCondition)
1919 {
1920 hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition);
1921 ExitOnFailure(hr, "Failed to evaluate advertise condition.");
1922
1923 if (fCondition)
1924 {
1925 *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED;
1926 ExitFunction();
1927 }
1928 }
1929
1930 // if no condition was true, set to absent
1931 *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
1932
1933 LExit:
1934 return hr;
1935 }
1936
1937 static HRESULT CalculateFeatureAction(
1938 __in BOOTSTRAPPER_FEATURE_STATE currentState,
1939 __in BOOTSTRAPPER_FEATURE_STATE requestedState,
1940 __in BOOL fRepair,
1941 __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction,
1942 __inout BOOL* pfDelta
1943 )
1944 {
1945 HRESULT hr = S_OK;
1946
1947 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1948 switch (requestedState)
1949 {
1950 case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN:
1951 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1952 break;
1953
1954 case BOOTSTRAPPER_FEATURE_STATE_ABSENT:
1955 if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState)
1956 {
1957 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE;
1958 }
1959 break;
1960
1961 case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED:
1962 if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState)
1963 {
1964 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE;
1965 }
1966 else if (fRepair)
1967 {
1968 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1969 }
1970 break;
1971
1972 case BOOTSTRAPPER_FEATURE_STATE_LOCAL:
1973 if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState)
1974 {
1975 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL;
1976 }
1977 else if (fRepair)
1978 {
1979 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1980 }
1981 break;
1982
1983 case BOOTSTRAPPER_FEATURE_STATE_SOURCE:
1984 if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState)
1985 {
1986 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE;
1987 }
1988 else if (fRepair)
1989 {
1990 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1991 }
1992 break;
1993
1994 default:
1995 hr = E_UNEXPECTED;
1996 ExitOnRootFailure(hr, "Invalid state value.");
1997 }
1998
1999 if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction)
2000 {
2001 *pfDelta = TRUE;
2002 }
2003
2004 LExit:
2005 return hr;
2006 }
2007
2008 static HRESULT EscapePropertyArgumentString(
2009 __in LPCWSTR wzProperty,
2010 __inout_z LPWSTR* psczEscapedValue,
2011 __in BOOL fZeroOnRealloc
2012 )
2013 {
2014 HRESULT hr = S_OK;
2015 DWORD cch = 0;
2016 DWORD cchEscape = 0;
2017 LPCWSTR wzSource = NULL;
2018 LPWSTR wzTarget = NULL;
2019
2020 // count characters to escape
2021 wzSource = wzProperty;
2022 while (*wzSource)
2023 {
2024 ++cch;
2025 if (L'\"' == *wzSource)
2026 {
2027 ++cchEscape;
2028 }
2029 ++wzSource;
2030 }
2031
2032 // allocate target buffer
2033 hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator
2034 ExitOnFailure(hr, "Failed to allocate string buffer.");
2035
2036 // write to target buffer
2037 wzSource = wzProperty;
2038 wzTarget = *psczEscapedValue;
2039 while (*wzSource)
2040 {
2041 *wzTarget = *wzSource;
2042 if (L'\"' == *wzTarget)
2043 {
2044 ++wzTarget;
2045 *wzTarget = L'\"';
2046 }
2047
2048 ++wzSource;
2049 ++wzTarget;
2050 }
2051
2052 *wzTarget = L'\0'; // add null terminator
2053
2054 LExit:
2055 return hr;
2056 }
2057
2058 static HRESULT ConcatFeatureActionProperties(
2059 __in BURN_PACKAGE* pPackage,
2060 __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions,
2061 __inout_z LPWSTR* psczArguments
2062 )
2063 {
2064 HRESULT hr = S_OK;
2065 LPWSTR scz = NULL;
2066 LPWSTR sczAddLocal = NULL;
2067 LPWSTR sczAddSource = NULL;
2068 LPWSTR sczAddDefault = NULL;
2069 LPWSTR sczReinstall = NULL;
2070 LPWSTR sczAdvertise = NULL;
2071 LPWSTR sczRemove = NULL;
2072
2073 // features
2074 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
2075 {
2076 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
2077
2078 switch (rgFeatureActions[i])
2079 {
2080 case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL:
2081 if (sczAddLocal)
2082 {
2083 hr = StrAllocConcat(&sczAddLocal, L",", 0);
2084 ExitOnFailure(hr, "Failed to concat separator.");
2085 }
2086 hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0);
2087 ExitOnFailure(hr, "Failed to concat feature.");
2088 break;
2089
2090 case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE:
2091 if (sczAddSource)
2092 {
2093 hr = StrAllocConcat(&sczAddSource, L",", 0);
2094 ExitOnFailure(hr, "Failed to concat separator.");
2095 }
2096 hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0);
2097 ExitOnFailure(hr, "Failed to concat feature.");
2098 break;
2099
2100 case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT:
2101 if (sczAddDefault)
2102 {
2103 hr = StrAllocConcat(&sczAddDefault, L",", 0);
2104 ExitOnFailure(hr, "Failed to concat separator.");
2105 }
2106 hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0);
2107 ExitOnFailure(hr, "Failed to concat feature.");
2108 break;
2109
2110 case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL:
2111 if (sczReinstall)
2112 {
2113 hr = StrAllocConcat(&sczReinstall, L",", 0);
2114 ExitOnFailure(hr, "Failed to concat separator.");
2115 }
2116 hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0);
2117 ExitOnFailure(hr, "Failed to concat feature.");
2118 break;
2119
2120 case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE:
2121 if (sczAdvertise)
2122 {
2123 hr = StrAllocConcat(&sczAdvertise, L",", 0);
2124 ExitOnFailure(hr, "Failed to concat separator.");
2125 }
2126 hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0);
2127 ExitOnFailure(hr, "Failed to concat feature.");
2128 break;
2129
2130 case BOOTSTRAPPER_FEATURE_ACTION_REMOVE:
2131 if (sczRemove)
2132 {
2133 hr = StrAllocConcat(&sczRemove, L",", 0);
2134 ExitOnFailure(hr, "Failed to concat separator.");
2135 }
2136 hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0);
2137 ExitOnFailure(hr, "Failed to concat feature.");
2138 break;
2139 }
2140 }
2141
2142 if (sczAddLocal)
2143 {
2144 hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0);
2145 ExitOnFailure(hr, "Failed to format ADDLOCAL string.");
2146
2147 hr = StrAllocConcatSecure(psczArguments, scz, 0);
2148 ExitOnFailure(hr, "Failed to concat argument string.");
2149 }
2150
2151 if (sczAddSource)
2152 {
2153 hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0);
2154 ExitOnFailure(hr, "Failed to format ADDSOURCE string.");
2155
2156 hr = StrAllocConcatSecure(psczArguments, scz, 0);
2157 ExitOnFailure(hr, "Failed to concat argument string.");
2158 }
2159
2160 if (sczAddDefault)
2161 {
2162 hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0);
2163 ExitOnFailure(hr, "Failed to format ADDDEFAULT string.");
2164
2165 hr = StrAllocConcatSecure(psczArguments, scz, 0);
2166 ExitOnFailure(hr, "Failed to concat argument string.");
2167 }
2168
2169 if (sczReinstall)
2170 {
2171 hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0);
2172 ExitOnFailure(hr, "Failed to format REINSTALL string.");
2173
2174 hr = StrAllocConcatSecure(psczArguments, scz, 0);
2175 ExitOnFailure(hr, "Failed to concat argument string.");
2176 }
2177
2178 if (sczAdvertise)
2179 {
2180 hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0);
2181 ExitOnFailure(hr, "Failed to format ADVERTISE string.");
2182
2183 hr = StrAllocConcatSecure(psczArguments, scz, 0);
2184 ExitOnFailure(hr, "Failed to concat argument string.");
2185 }
2186
2187 if (sczRemove)
2188 {
2189 hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0);
2190 ExitOnFailure(hr, "Failed to format REMOVE string.");
2191
2192 hr = StrAllocConcatSecure(psczArguments, scz, 0);
2193 ExitOnFailure(hr, "Failed to concat argument string.");
2194 }
2195
2196 LExit:
2197 ReleaseStr(scz);
2198 ReleaseStr(sczAddLocal);
2199 ReleaseStr(sczAddSource);
2200 ReleaseStr(sczAddDefault);
2201 ReleaseStr(sczReinstall);
2202 ReleaseStr(sczAdvertise);
2203 ReleaseStr(sczRemove);
2204
2205 return hr;
2206 }
2207
2208 static HRESULT ConcatPatchProperty(
2209 __in BURN_CACHE* pCache,
2210 __in BURN_PACKAGE* pPackage,
2211 __in BOOL fRollback,
2212 __inout_z LPWSTR* psczArguments
2213 )
2214 {
2215 HRESULT hr = S_OK;
2216 LPWSTR sczCachedDirectory = NULL;
2217 LPWSTR sczMspPath = NULL;
2218 LPWSTR sczPatches = NULL;
2219
2220 // If there are slipstream patch actions, build up their patch action.
2221 if (pPackage->Msi.cSlipstreamMspPackages)
2222 {
2223 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
2224 {
2225 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i;
2226 BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage;
2227 BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload;
2228 BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute;
2229
2230 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction)
2231 {
2232 hr = CacheGetCompletedPath(pCache, pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
2233 ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);
2234
2235 hr = PathConcatRelativeToFullyQualifiedBase(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath);
2236 ExitOnFailure(hr, "Failed to build MSP path.");
2237
2238 if (!sczPatches)
2239 {
2240 hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0);
2241 ExitOnFailure(hr, "Failed to prefix with PATCH property.");
2242 }
2243 else
2244 {
2245 hr = StrAllocConcat(&sczPatches, L";", 0);
2246 ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
2247 }
2248
2249 hr = StrAllocConcat(&sczPatches, sczMspPath, 0);
2250 ExitOnFailure(hr, "Failed to append patch path.");
2251 }
2252 }
2253
2254 if (sczPatches)
2255 {
2256 hr = StrAllocConcat(&sczPatches, L"\"", 0);
2257 ExitOnFailure(hr, "Failed to close the quoted PATCH property.");
2258
2259 hr = StrAllocConcatSecure(psczArguments, sczPatches, 0);
2260 ExitOnFailure(hr, "Failed to append PATCH property.");
2261 }
2262 }
2263
2264 LExit:
2265 ReleaseStr(sczMspPath);
2266 ReleaseStr(sczCachedDirectory);
2267 ReleaseStr(sczPatches);
2268 return hr;
2269 }
2270
2271 static void RegisterSourceDirectory(
2272 __in BURN_PACKAGE* pPackage,
2273 __in_z LPCWSTR wzMsiPath
2274 )
2275 {
2276 HRESULT hr = S_OK;
2277 LPWSTR sczMsiDirectory = NULL;
2278 MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED;
2279
2280 hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory);
2281 ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath);
2282
2283 hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1);
2284 if (FAILED(hr))
2285 {
2286 LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr);
2287 ExitFunction();
2288 }
2289
2290 LExit:
2291 ReleaseStr(sczMsiDirectory);
2292
2293 return;
2294 }