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