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