aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/bundlepackageengine.cpp
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-04-01 15:44:34 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-04-01 22:06:11 -0500
commit39b9a6112c2ff97f31f195749e2142538e47a2eb (patch)
tree8b2337b589fa5f52fabce89c99d3fca0ef1c8fc0 /src/burn/engine/bundlepackageengine.cpp
parent386a3578413ba16b3c0615d47870ee44a0e461f6 (diff)
downloadwix-39b9a6112c2ff97f31f195749e2142538e47a2eb.tar.gz
wix-39b9a6112c2ff97f31f195749e2142538e47a2eb.tar.bz2
wix-39b9a6112c2ff97f31f195749e2142538e47a2eb.zip
Detect related bundles for BundlePackages.
Diffstat (limited to 'src/burn/engine/bundlepackageengine.cpp')
-rw-r--r--src/burn/engine/bundlepackageengine.cpp277
1 files changed, 254 insertions, 23 deletions
diff --git a/src/burn/engine/bundlepackageengine.cpp b/src/burn/engine/bundlepackageengine.cpp
index f3badfc1..ef08d417 100644
--- a/src/burn/engine/bundlepackageengine.cpp
+++ b/src/burn/engine/bundlepackageengine.cpp
@@ -2,6 +2,18 @@
2 2
3#include "precomp.h" 3#include "precomp.h"
4 4
5typedef struct _BUNDLE_QUERY_CONTEXT
6{
7 BURN_PACKAGE* pPackage;
8 BURN_USER_EXPERIENCE* pUserExperience;
9 BOOL fSelfFound;
10 BOOL fNewerFound;
11} BUNDLE_QUERY_CONTEXT;
12
13static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
14 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
15 __in_opt LPVOID pvContext
16 );
5static HRESULT ExecuteBundle( 17static HRESULT ExecuteBundle(
6 __in BURN_CACHE* pCache, 18 __in BURN_CACHE* pCache,
7 __in BURN_VARIABLES* pVariables, 19 __in BURN_VARIABLES* pVariables,
@@ -35,6 +47,18 @@ extern "C" HRESULT BundlePackageEngineParsePackageFromXml(
35 hr = XmlGetAttributeEx(pixnBundlePackage, L"BundleId", &pPackage->Bundle.sczBundleId); 47 hr = XmlGetAttributeEx(pixnBundlePackage, L"BundleId", &pPackage->Bundle.sczBundleId);
36 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @BundleId."); 48 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @BundleId.");
37 49
50 // @Version
51 hr = XmlGetAttributeEx(pixnBundlePackage, L"Version", &scz);
52 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Version.");
53
54 hr = VerParseVersion(scz, 0, FALSE, &pPackage->Bundle.pVersion);
55 ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
56
57 if (pPackage->Bundle.pVersion->fInvalid)
58 {
59 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
60 }
61
38 // @InstallArguments 62 // @InstallArguments
39 hr = XmlGetAttributeEx(pixnBundlePackage, L"InstallArguments", &pPackage->Bundle.sczInstallArguments); 63 hr = XmlGetAttributeEx(pixnBundlePackage, L"InstallArguments", &pPackage->Bundle.sczInstallArguments);
40 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments."); 64 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments.");
@@ -55,6 +79,9 @@ extern "C" HRESULT BundlePackageEngineParsePackageFromXml(
55 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"Win64", &pPackage->Bundle.fWin64); 79 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"Win64", &pPackage->Bundle.fWin64);
56 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Win64."); 80 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Win64.");
57 81
82 hr = BundlePackageEngineParseRelatedCodes(pixnBundlePackage, &pPackage->Bundle.rgsczDetectCodes, &pPackage->Bundle.cDetectCodes, &pPackage->Bundle.rgsczUpgradeCodes, &pPackage->Bundle.cUpgradeCodes, &pPackage->Bundle.rgsczAddonCodes, &pPackage->Bundle.cAddonCodes, &pPackage->Bundle.rgsczPatchCodes, &pPackage->Bundle.cPatchCodes);
83 ExitOnFailure(hr, "Failed to parse related codes.");
84
58 hr = ExeEngineParseExitCodesFromXml(pixnBundlePackage, &pPackage->Bundle.rgExitCodes, &pPackage->Bundle.cExitCodes); 85 hr = ExeEngineParseExitCodesFromXml(pixnBundlePackage, &pPackage->Bundle.rgExitCodes, &pPackage->Bundle.cExitCodes);
59 ExitOnFailure(hr, "Failed to parse exit codes."); 86 ExitOnFailure(hr, "Failed to parse exit codes.");
60 87
@@ -70,11 +97,101 @@ LExit:
70 return hr; 97 return hr;
71} 98}
72 99
100
101extern "C" HRESULT BundlePackageEngineParseRelatedCodes(
102 __in IXMLDOMNode* pixnBundle,
103 __in LPWSTR** prgsczDetectCodes,
104 __in DWORD* pcDetectCodes,
105 __in LPWSTR** prgsczUpgradeCodes,
106 __in DWORD* pcUpgradeCodes,
107 __in LPWSTR** prgsczAddonCodes,
108 __in DWORD* pcAddonCodes,
109 __in LPWSTR** prgsczPatchCodes,
110 __in DWORD* pcPatchCodes
111 )
112{
113 HRESULT hr = S_OK;
114 IXMLDOMNodeList* pixnNodes = NULL;
115 IXMLDOMNode* pixnElement = NULL;
116 LPWSTR sczAction = NULL;
117 LPWSTR sczId = NULL;
118 DWORD cElements = 0;
119
120 hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes);
121 ExitOnFailure(hr, "Failed to get RelatedBundle nodes");
122
123 hr = pixnNodes->get_length((long*)&cElements);
124 ExitOnFailure(hr, "Failed to get RelatedBundle element count.");
125
126 for (DWORD i = 0; i < cElements; ++i)
127 {
128 hr = XmlNextElement(pixnNodes, &pixnElement, NULL);
129 ExitOnFailure(hr, "Failed to get next RelatedBundle element.");
130
131 hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction);
132 ExitOnFailure(hr, "Failed to get @Action.");
133
134 hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId);
135 ExitOnFailure(hr, "Failed to get @Id.");
136
137 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1))
138 {
139 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczDetectCodes), *pcDetectCodes, 1, sizeof(LPWSTR), 5);
140 ExitOnFailure(hr, "Failed to resize Detect code array");
141
142 *prgsczDetectCodes[*pcDetectCodes] = sczId;
143 sczId = NULL;
144 *pcDetectCodes += 1;
145 }
146 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1))
147 {
148 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczUpgradeCodes), *pcUpgradeCodes, 1, sizeof(LPWSTR), 5);
149 ExitOnFailure(hr, "Failed to resize Upgrade code array");
150
151 *prgsczUpgradeCodes[*pcUpgradeCodes] = sczId;
152 sczId = NULL;
153 *pcUpgradeCodes += 1;
154 }
155 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1))
156 {
157 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczAddonCodes), *pcAddonCodes, 1, sizeof(LPWSTR), 5);
158 ExitOnFailure(hr, "Failed to resize Addon code array");
159
160 *prgsczAddonCodes[*pcAddonCodes] = sczId;
161 sczId = NULL;
162 *pcAddonCodes += 1;
163 }
164 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1))
165 {
166 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczPatchCodes), *pcPatchCodes, 1, sizeof(LPWSTR), 5);
167 ExitOnFailure(hr, "Failed to resize Patch code array");
168
169 *prgsczPatchCodes[*pcPatchCodes] = sczId;
170 sczId = NULL;
171 *pcPatchCodes += 1;
172 }
173 else
174 {
175 hr = E_INVALIDARG;
176 ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction);
177 }
178 }
179
180LExit:
181 ReleaseObject(pixnNodes);
182 ReleaseObject(pixnElement);
183 ReleaseStr(sczAction);
184 ReleaseStr(sczId);
185
186 return hr;
187}
188
73extern "C" void BundlePackageEnginePackageUninitialize( 189extern "C" void BundlePackageEnginePackageUninitialize(
74 __in BURN_PACKAGE* pPackage 190 __in BURN_PACKAGE* pPackage
75 ) 191 )
76{ 192{
77 ReleaseStr(pPackage->Bundle.sczBundleId); 193 ReleaseStr(pPackage->Bundle.sczBundleId);
194 ReleaseVerutilVersion(pPackage->Bundle.pVersion);
78 ReleaseStr(pPackage->Bundle.sczRegistrationKey); 195 ReleaseStr(pPackage->Bundle.sczRegistrationKey);
79 ReleaseStr(pPackage->Bundle.sczInstallArguments); 196 ReleaseStr(pPackage->Bundle.sczInstallArguments);
80 ReleaseStr(pPackage->Bundle.sczRepairArguments); 197 ReleaseStr(pPackage->Bundle.sczRepairArguments);
@@ -92,45 +209,95 @@ extern "C" void BundlePackageEnginePackageUninitialize(
92 MemFree(pPackage->Bundle.rgCommandLineArguments); 209 MemFree(pPackage->Bundle.rgCommandLineArguments);
93 } 210 }
94 211
212 for (DWORD i = 0; i < pPackage->Bundle.cDetectCodes; ++i)
213 {
214 ReleaseStr(pPackage->Bundle.rgsczDetectCodes[i]);
215 }
216 ReleaseMem(pPackage->Bundle.rgsczDetectCodes);
217
218 for (DWORD i = 0; i < pPackage->Bundle.cUpgradeCodes; ++i)
219 {
220 ReleaseStr(pPackage->Bundle.rgsczUpgradeCodes[i]);
221 }
222 ReleaseMem(pPackage->Bundle.rgsczUpgradeCodes);
223
224 for (DWORD i = 0; i < pPackage->Bundle.cAddonCodes; ++i)
225 {
226 ReleaseStr(pPackage->Bundle.rgsczAddonCodes[i]);
227 }
228 ReleaseMem(pPackage->Bundle.rgsczAddonCodes);
229
230 for (DWORD i = 0; i < pPackage->Bundle.cPatchCodes; ++i)
231 {
232 ReleaseStr(pPackage->Bundle.rgsczPatchCodes[i]);
233 }
234 ReleaseMem(pPackage->Bundle.rgsczPatchCodes);
235
95 // clear struct 236 // clear struct
96 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle)); 237 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle));
97} 238}
98 239
99extern "C" HRESULT BundlePackageEngineDetectPackage( 240extern "C" HRESULT BundlePackageEngineDetectPackage(
100 __in BURN_PACKAGE* pPackage 241 __in BURN_PACKAGE* pPackage,
242 __in BURN_REGISTRATION* /*pRegistration*/,
243 __in BURN_USER_EXPERIENCE* pUserExperience
101 ) 244 )
102{ 245{
103 HRESULT hr = S_OK; 246 HRESULT hr = S_OK;
104 HKEY hkRegistration = NULL; 247 BUNDLE_QUERY_CONTEXT queryContext = { };
105 DWORD dwInstalled = 0; 248
106 BOOL fDetected = FALSE; 249 queryContext.pPackage = pPackage;
107 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 250 queryContext.pUserExperience = pUserExperience;
108 REG_KEY_BITNESS bitness = pPackage->Bundle.fWin64 ? REG_KEY_64BIT : REG_KEY_32BIT; 251
109 252 hr = BundleQueryRelatedBundles(
110 // TODO: detect all related bundles, so that the Obsolete state can be detected. 253 BUNDLE_INSTALL_CONTEXT_MACHINE,
111 hr = RegOpenEx(hkRoot, pPackage->Bundle.sczRegistrationKey, KEY_QUERY_VALUE, bitness, &hkRegistration); 254 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczDetectCodes),
112 if (SUCCEEDED(hr)) 255 pPackage->Bundle.cDetectCodes,
256 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczUpgradeCodes),
257 pPackage->Bundle.cUpgradeCodes,
258 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczAddonCodes),
259 pPackage->Bundle.cAddonCodes,
260 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczPatchCodes),
261 pPackage->Bundle.cPatchCodes,
262 QueryRelatedBundlesCallback,
263 &queryContext);
264 ExitOnFailure(hr, "Failed to query per-machine related bundle packages.");
265
266 hr = BundleQueryRelatedBundles(
267 BUNDLE_INSTALL_CONTEXT_USER,
268 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczDetectCodes),
269 pPackage->Bundle.cDetectCodes,
270 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczUpgradeCodes),
271 pPackage->Bundle.cUpgradeCodes,
272 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczAddonCodes),
273 pPackage->Bundle.cAddonCodes,
274 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczPatchCodes),
275 pPackage->Bundle.cPatchCodes,
276 QueryRelatedBundlesCallback,
277 &queryContext);
278 ExitOnFailure(hr, "Failed to query per-user related bundle packages.");
279
280 if (queryContext.fNewerFound)
113 { 281 {
114 hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); 282 pPackage->currentState = queryContext.fSelfFound ? BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED : BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
115 } 283 }
116 284 else
117 // Not finding the key or value is okay.
118 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
119 { 285 {
120 hr = S_OK; 286 pPackage->currentState = queryContext.fSelfFound ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
121 } 287 }
122 288
123 fDetected = (1 == dwInstalled);
124
125 // update detect state
126 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
127
128 if (pPackage->fCanAffectRegistration) 289 if (pPackage->fCanAffectRegistration)
129 { 290 {
130 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; 291 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
131 } 292 }
132 293
133 ReleaseRegKey(hkRegistration); 294 // TODO: The bundle is registering itself as a dependent when installed as a chain package, which prevents us from uninstalling it.
295 //hr = DependencyDetectChainPackage(pPackage, pRegistration);
296 //ExitOnFailure(hr, "Failed to detect dependencies for BUNDLE package.");
297
298 // TODO: uninstalling compatible Bundles like MsiEngine supports?
299
300LExit:
134 return hr; 301 return hr;
135} 302}
136 303
@@ -148,7 +315,8 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
148 // execute action 315 // execute action
149 switch (pPackage->currentState) 316 switch (pPackage->currentState)
150 { 317 {
151 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: 318 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
319 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
152 switch (pPackage->requested) 320 switch (pPackage->requested)
153 { 321 {
154 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: 322 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
@@ -173,6 +341,7 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
173 } 341 }
174 break; 342 break;
175 343
344 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
176 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: 345 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
177 switch (pPackage->requested) 346 switch (pPackage->requested)
178 { 347 {
@@ -200,7 +369,8 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
200 { 369 {
201 switch (pPackage->currentState) 370 switch (pPackage->currentState)
202 { 371 {
203 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: 372 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
373 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
204 switch (pPackage->requested) 374 switch (pPackage->requested)
205 { 375 {
206 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; 376 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
@@ -218,6 +388,7 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
218 } 388 }
219 break; 389 break;
220 390
391 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
221 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: 392 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
222 switch (pPackage->requested) 393 switch (pPackage->requested)
223 { 394 {
@@ -481,6 +652,66 @@ LExit:
481 return; 652 return;
482} 653}
483 654
655static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
656 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
657 __in_opt LPVOID pvContext
658 )
659{
660 HRESULT hr = S_OK;
661 BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
662 LPWSTR sczBundleVersion = NULL;
663 VERUTIL_VERSION* pVersion = NULL;
664 int nCompare = 0;
665 BUNDLE_QUERY_CONTEXT* pContext = reinterpret_cast<BUNDLE_QUERY_CONTEXT*>(pvContext);
666 BURN_PACKAGE* pPackage = pContext->pPackage;
667 BOOTSTRAPPER_RELATION_TYPE relationType = RelatedBundleConvertRelationType(pBundle->relationType);
668 BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext;
669
670 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pPackage->Bundle.sczBundleId, -1))
671 {
672 Assert(BOOTSTRAPPER_RELATION_UPGRADE == relationType);
673 Assert(pPackage->Bundle.fWin64 == (REG_KEY_64BIT == pBundle->regBitness));
674
675 pContext->fSelfFound = TRUE;
676 }
677
678 hr = RegReadString(pBundle->hkBundle, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion);
679 ExitOnFailure(hr, "Failed to read version from registry for related bundle package: %ls", pBundle->wzBundleId);
680
681 hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pVersion);
682 ExitOnFailure(hr, "Failed to parse related bundle package version: %ls", sczBundleVersion);
683
684 if (pVersion->fInvalid)
685 {
686 LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, pBundle->wzBundleId, sczBundleVersion);
687 }
688
689 if (BOOTSTRAPPER_RELATION_UPGRADE == relationType)
690 {
691 hr = VerCompareParsedVersions(pPackage->Bundle.pVersion, pVersion, &nCompare);
692 ExitOnFailure(hr, "Failed to compare related bundle package version: %ls", pVersion->sczVersion);
693
694 if (nCompare < 0)
695 {
696 pContext->fNewerFound = TRUE;
697 }
698 }
699
700 result = BUNDLE_QUERY_CALLBACK_RESULT_CANCEL;
701
702 // Pass to BA.
703 hr = UserExperienceOnDetectRelatedBundlePackage(pContext->pUserExperience, pPackage->sczId, pBundle->wzBundleId, relationType, fPerMachine, pVersion);
704 ExitOnRootFailure(hr, "BA aborted detect related BUNDLE package.");
705
706 result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
707
708LExit:
709 ReleaseVerutilVersion(pVersion);
710 ReleaseStr(sczBundleVersion);
711
712 return result;
713}
714
484static HRESULT ExecuteBundle( 715static HRESULT ExecuteBundle(
485 __in BURN_CACHE* pCache, 716 __in BURN_CACHE* pCache,
486 __in BURN_VARIABLES* pVariables, 717 __in BURN_VARIABLES* pVariables,