aboutsummaryrefslogtreecommitdiff
path: root/src/engine/relatedbundle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/relatedbundle.cpp')
-rw-r--r--src/engine/relatedbundle.cpp457
1 files changed, 457 insertions, 0 deletions
diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp
new file mode 100644
index 00000000..87794177
--- /dev/null
+++ b/src/engine/relatedbundle.cpp
@@ -0,0 +1,457 @@
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// internal function declarations
6
7static HRESULT LoadIfRelatedBundle(
8 __in BOOL fPerMachine,
9 __in HKEY hkUninstallKey,
10 __in_z LPCWSTR sczRelatedBundleId,
11 __in BURN_REGISTRATION* pRegistration,
12 __in BURN_RELATED_BUNDLES* pRelatedBundles
13 );
14static HRESULT DetermineRelationType(
15 __in HKEY hkBundleId,
16 __in BURN_REGISTRATION* pRegistration,
17 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
18 );
19static HRESULT LoadRelatedBundleFromKey(
20 __in_z LPCWSTR wzRelatedBundleId,
21 __in HKEY hkBundleId,
22 __in BOOL fPerMachine,
23 __in BOOTSTRAPPER_RELATION_TYPE relationType,
24 __inout BURN_RELATED_BUNDLE *pRelatedBundle
25 );
26
27
28// function definitions
29
30extern "C" HRESULT RelatedBundlesInitializeForScope(
31 __in BOOL fPerMachine,
32 __in BURN_REGISTRATION* pRegistration,
33 __in BURN_RELATED_BUNDLES* pRelatedBundles
34 )
35{
36 HRESULT hr = S_OK;
37 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
38 HKEY hkUninstallKey = NULL;
39 LPWSTR sczRelatedBundleId = NULL;
40
41 hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey);
42 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
43 {
44 ExitFunction1(hr = S_OK);
45 }
46 ExitOnFailure(hr, "Failed to open uninstall registry key.");
47
48 for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
49 {
50 hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
51 if (E_NOMOREITEMS == hr)
52 {
53 hr = S_OK;
54 break;
55 }
56 ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
57
58 // If we did not find our bundle id, try to load the subkey as a related bundle.
59 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1))
60 {
61 // Ignore failures here since we'll often find products that aren't actually
62 // related bundles (or even bundles at all).
63 HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles);
64 UNREFERENCED_PARAMETER(hrRelatedBundle);
65 }
66 }
67
68LExit:
69 ReleaseStr(sczRelatedBundleId);
70 ReleaseRegKey(hkUninstallKey);
71
72 return hr;
73}
74
75extern "C" void RelatedBundlesUninitialize(
76 __in BURN_RELATED_BUNDLES* pRelatedBundles
77 )
78{
79 if (pRelatedBundles->rgRelatedBundles)
80 {
81 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
82 {
83 PackageUninitialize(&pRelatedBundles->rgRelatedBundles[i].package);
84 ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag);
85 }
86
87 MemFree(pRelatedBundles->rgRelatedBundles);
88 }
89
90 memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES));
91}
92
93
94// internal helper functions
95
96static HRESULT LoadIfRelatedBundle(
97 __in BOOL fPerMachine,
98 __in HKEY hkUninstallKey,
99 __in_z LPCWSTR sczRelatedBundleId,
100 __in BURN_REGISTRATION* pRegistration,
101 __in BURN_RELATED_BUNDLES* pRelatedBundles
102 )
103{
104 HRESULT hr = S_OK;
105 HKEY hkBundleId = NULL;
106 BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE;
107
108 hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId);
109 ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId);
110
111 hr = DetermineRelationType(hkBundleId, pRegistration, &relationType);
112 if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType)
113 {
114 // Must not be a related bundle.
115 hr = E_NOTFOUND;
116 }
117 else // load the related bundle.
118 {
119 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
120 ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
121
122 BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
123
124 hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle);
125 ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId);
126
127 ++pRelatedBundles->cRelatedBundles;
128 }
129
130LExit:
131 ReleaseRegKey(hkBundleId);
132
133 return hr;
134}
135
136static HRESULT DetermineRelationType(
137 __in HKEY hkBundleId,
138 __in BURN_REGISTRATION* pRegistration,
139 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
140 )
141{
142 HRESULT hr = S_OK;
143 LPWSTR* rgsczUpgradeCodes = NULL;
144 DWORD cUpgradeCodes = 0;
145 STRINGDICT_HANDLE sdUpgradeCodes = NULL;
146 LPWSTR* rgsczAddonCodes = NULL;
147 DWORD cAddonCodes = 0;
148 STRINGDICT_HANDLE sdAddonCodes = NULL;
149 LPWSTR* rgsczDetectCodes = NULL;
150 DWORD cDetectCodes = 0;
151 STRINGDICT_HANDLE sdDetectCodes = NULL;
152 LPWSTR* rgsczPatchCodes = NULL;
153 DWORD cPatchCodes = 0;
154 STRINGDICT_HANDLE sdPatchCodes = NULL;
155
156 *pRelationType = BOOTSTRAPPER_RELATION_NONE;
157
158 // All remaining operations should treat all related bundles as non-vital.
159 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
160 if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
161 {
162 TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
163
164 rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
165 ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
166
167 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
168 if (SUCCEEDED(hr))
169 {
170 cUpgradeCodes = 1;
171 }
172 }
173
174 // Compare upgrade codes.
175 if (SUCCEEDED(hr))
176 {
177 hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
178 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
179
180 // Upgrade relationship: when their upgrade codes match our upgrade codes.
181 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
182 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
183 {
184 hr = S_OK;
185 }
186 else
187 {
188 ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
189
190 *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE;
191 ExitFunction();
192 }
193
194 // Detect relationship: when their upgrade codes match our detect codes.
195 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
196 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
197 {
198 hr = S_OK;
199 }
200 else
201 {
202 ExitOnFailure(hr, "Failed to do array search for detect code match.");
203
204 *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
205 ExitFunction();
206 }
207
208 // Dependent relationship: when their upgrade codes match our addon codes.
209 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
210 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
211 {
212 hr = S_OK;
213 }
214 else
215 {
216 ExitOnFailure(hr, "Failed to do array search for addon code match.");
217
218 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
219 ExitFunction();
220 }
221
222 // Dependent relationship: when their upgrade codes match our patch codes.
223 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
224 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
225 {
226 hr = S_OK;
227 }
228 else
229 {
230 ExitOnFailure(hr, "Failed to do array search for addon code match.");
231
232 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
233 ExitFunction();
234 }
235
236 ReleaseNullDict(sdUpgradeCodes);
237 ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
238 }
239
240 // Compare addon codes.
241 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
242 if (SUCCEEDED(hr))
243 {
244 hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
245 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
246
247 // Addon relationship: when their addon codes match our detect codes.
248 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
249 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
250 {
251 hr = S_OK;
252 }
253 else
254 {
255 ExitOnFailure(hr, "Failed to do array search for addon code match.");
256
257 *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
258 ExitFunction();
259 }
260
261 // Addon relationship: when their addon codes match our upgrade codes.
262 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
263 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
264 {
265 hr = S_OK;
266 }
267 else
268 {
269 ExitOnFailure(hr, "Failed to do array search for addon code match.");
270
271 *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
272 ExitFunction();
273 }
274
275 ReleaseNullDict(sdAddonCodes);
276 ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
277 }
278
279 // Compare patch codes.
280 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
281 if (SUCCEEDED(hr))
282 {
283 hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
284 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
285
286 // Patch relationship: when their patch codes match our detect codes.
287 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
288 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
289 {
290 hr = S_OK;
291 }
292 else
293 {
294 ExitOnFailure(hr, "Failed to do array search for patch code match.");
295
296 *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
297 ExitFunction();
298 }
299
300 // Patch relationship: when their patch codes match our upgrade codes.
301 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
302 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
303 {
304 hr = S_OK;
305 }
306 else
307 {
308 ExitOnFailure(hr, "Failed to do array search for patch code match.");
309
310 *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
311 ExitFunction();
312 }
313
314 ReleaseNullDict(sdPatchCodes);
315 ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
316 }
317
318 // Compare detect codes.
319 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
320 if (SUCCEEDED(hr))
321 {
322 hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
323 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
324
325 // Detect relationship: when their detect codes match our detect codes.
326 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
327 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
328 {
329 hr = S_OK;
330 }
331 else
332 {
333 ExitOnFailure(hr, "Failed to do array search for detect code match.");
334
335 *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
336 ExitFunction();
337 }
338
339 // Dependent relationship: when their detect codes match our addon codes.
340 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
341 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
342 {
343 hr = S_OK;
344 }
345 else
346 {
347 ExitOnFailure(hr, "Failed to do array search for addon code match.");
348
349 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
350 ExitFunction();
351 }
352
353 // Dependent relationship: when their detect codes match our patch codes.
354 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
355 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
356 {
357 hr = S_OK;
358 }
359 else
360 {
361 ExitOnFailure(hr, "Failed to do array search for addon code match.");
362
363 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
364 ExitFunction();
365 }
366
367 ReleaseNullDict(sdDetectCodes);
368 ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
369 }
370
371LExit:
372 if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType)
373 {
374 hr = E_NOTFOUND;
375 }
376
377 ReleaseDict(sdUpgradeCodes);
378 ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
379 ReleaseDict(sdAddonCodes);
380 ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
381 ReleaseDict(sdDetectCodes);
382 ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
383 ReleaseDict(sdPatchCodes);
384 ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
385
386 return hr;
387}
388
389static HRESULT LoadRelatedBundleFromKey(
390 __in_z LPCWSTR wzRelatedBundleId,
391 __in HKEY hkBundleId,
392 __in BOOL fPerMachine,
393 __in BOOTSTRAPPER_RELATION_TYPE relationType,
394 __inout BURN_RELATED_BUNDLE *pRelatedBundle
395 )
396{
397 HRESULT hr = S_OK;
398 DWORD64 qwEngineVersion = 0;
399 LPWSTR sczCachePath = NULL;
400 DWORD64 qwFileSize = 0;
401 BURN_DEPENDENCY_PROVIDER dependencyProvider = { };
402
403 hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion);
404 if (FAILED(hr))
405 {
406 qwEngineVersion = 0;
407 hr = S_OK;
408 }
409
410 hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &pRelatedBundle->qwVersion);
411 ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId);
412
413 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath);
414 ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId);
415
416 hr = FileSize(sczCachePath, reinterpret_cast<LONGLONG *>(&qwFileSize));
417 ExitOnFailure(hr, "Failed to get size of pseudo bundle: %ls", sczCachePath);
418
419 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey);
420 if (E_FILENOTFOUND != hr)
421 {
422 ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId);
423
424 dependencyProvider.fImported = TRUE;
425
426 hr = FileVersionToStringEx(pRelatedBundle->qwVersion, &dependencyProvider.sczVersion);
427 ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId);
428
429 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName);
430 if (E_FILENOTFOUND != hr)
431 {
432 ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId);
433 }
434 }
435
436 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag);
437 if (E_FILENOTFOUND == hr)
438 {
439 hr = S_OK;
440 }
441 ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId);
442
443 pRelatedBundle->relationType = relationType;
444
445 hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType,
446 BOOTSTRAPPER_PACKAGE_STATE_PRESENT, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE,
447 L"-quiet", L"-repair -quiet", L"-uninstall -quiet",
448 (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL,
449 NULL, 0);
450 ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId);
451
452LExit:
453 DependencyUninitialize(&dependencyProvider);
454 ReleaseStr(sczCachePath);
455
456 return hr;
457}