@joebigelow / wix / commits / befcd209

Refactor related bundle enumeration into butil.

Related to #3693

Sean Hall committed Feb 28, 2022 at 18:42 UTC befcd209d62a25020f46a688002b259c59e4dc3b
10 files changed +731 -405
src/burn/engine/precomp.h
+1
@@ -55,6 +55,7 @@
55 #include <atomutil.h>
56 #include <apuputil.h>
57 #include <dpiutil.h>
58 +#include <butil.h>
59
60 #include "BootstrapperEngine.h"
61 #include "BootstrapperApplication.h"
src/burn/engine/relatedbundle.cpp
+53 -337
@@ -2,6 +2,12 @@
2
3 #include "precomp.h"
4
5 +typedef struct _BUNDLE_QUERY_CONTEXT
6 +{
7 + BURN_REGISTRATION* pRegistration;
8 + BURN_RELATED_BUNDLES* pRelatedBundles;
9 +} BUNDLE_QUERY_CONTEXT;
10 +
11 // internal function declarations
12
13 static __callback int __cdecl CompareRelatedBundles(
@@ -9,25 +15,15 @@ static __callback int __cdecl CompareRelatedBundles(
15 __in const void* pvLeft,
16 __in const void* pvRight
17 );
12 -static HRESULT InitializeForScopeAndBitness(
13 - __in BOOL fPerMachine,
14 - __in REG_KEY_BITNESS regBitness,
15 - __in BURN_REGISTRATION* pRegistration,
16 - __in BURN_RELATED_BUNDLES* pRelatedBundles
18 +static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
19 + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
20 + __in_opt LPVOID pvContext
21 );
22 static HRESULT LoadIfRelatedBundle(
19 - __in BOOL fPerMachine,
20 - __in REG_KEY_BITNESS regBitness,
21 - __in HKEY hkUninstallKey,
22 - __in_z LPCWSTR sczRelatedBundleId,
23 + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
24 __in BURN_REGISTRATION* pRegistration,
25 __in BURN_RELATED_BUNDLES* pRelatedBundles
26 );
26 -static HRESULT DetermineRelationType(
27 - __in HKEY hkBundleId,
28 - __in BURN_REGISTRATION* pRegistration,
29 - __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
30 - );
27 static HRESULT LoadRelatedBundleFromKey(
28 __in_z LPCWSTR wzRelatedBundleId,
29 __in HKEY hkBundleId,
@@ -46,12 +42,25 @@ extern "C" HRESULT RelatedBundlesInitializeForScope(
42 )
43 {
44 HRESULT hr = S_OK;
49 -
50 - hr = InitializeForScopeAndBitness(fPerMachine, REG_KEY_32BIT, pRegistration, pRelatedBundles);
51 - ExitOnFailure(hr, "Failed to open 32-bit uninstall registry key.");
52 -
53 - hr = InitializeForScopeAndBitness(fPerMachine, REG_KEY_64BIT, pRegistration, pRelatedBundles);
54 - ExitOnFailure(hr, "Failed to open 64-bit uninstall registry key.");
45 + BUNDLE_INSTALL_CONTEXT installContext = fPerMachine ? BUNDLE_INSTALL_CONTEXT_MACHINE : BUNDLE_INSTALL_CONTEXT_USER;
46 + BUNDLE_QUERY_CONTEXT queryContext = { };
47 +
48 + queryContext.pRegistration = pRegistration;
49 + queryContext.pRelatedBundles = pRelatedBundles;
50 +
51 + hr = BundleQueryRelatedBundles(
52 + installContext,
53 + const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes),
54 + pRegistration->cDetectCodes,
55 + const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes),
56 + pRegistration->cUpgradeCodes,
57 + const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes),
58 + pRegistration->cAddonCodes,
59 + const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes),
60 + pRegistration->cPatchCodes,
61 + QueryRelatedBundlesCallback,
62 + &queryContext);
63 + ExitOnFailure(hr, "Failed to initialize related bundles for scope.");
64
65 LExit:
66 return hr;
@@ -166,346 +175,53 @@ static __callback int __cdecl CompareRelatedBundles(
175 return ret;
176 }
177
169 -static HRESULT InitializeForScopeAndBitness(
170 - __in BOOL fPerMachine,
171 - __in REG_KEY_BITNESS regBitness,
172 - __in BURN_REGISTRATION * pRegistration,
173 - __in BURN_RELATED_BUNDLES * pRelatedBundles
174 -)
178 +static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
179 + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
180 + __in_opt LPVOID pvContext
181 + )
182 {
183 HRESULT hr = S_OK;
177 - HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
178 - HKEY hkUninstallKey = NULL;
179 - LPWSTR sczRelatedBundleId = NULL;
184 + BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
185 + BUNDLE_QUERY_CONTEXT* pContext = reinterpret_cast<BUNDLE_QUERY_CONTEXT*>(pvContext);
186
181 - hr = RegOpenEx(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, regBitness, &hkUninstallKey);
182 - if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
183 - {
184 - ExitFunction1(hr = S_OK);
185 - }
186 - ExitOnFailure(hr, "Failed to open uninstall registry key.");
187 -
188 - for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
189 - {
190 - hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
191 - if (E_NOMOREITEMS == hr)
192 - {
193 - hr = S_OK;
194 - break;
195 - }
196 - ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
197 -
198 - // If we did not find our bundle id, try to load the subkey as a related bundle.
199 - if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1))
200 - {
201 - // Ignore failures here since we'll often find products that aren't actually
202 - // related bundles (or even bundles at all).
203 - HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, regBitness, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles);
204 - UNREFERENCED_PARAMETER(hrRelatedBundle);
205 - }
206 - }
187 + hr = LoadIfRelatedBundle(pBundle, pContext->pRegistration, pContext->pRelatedBundles);
188 + ExitOnFailure(hr, "Failed to load related bundle: %ls", pBundle->wzBundleId);
189
190 LExit:
209 - ReleaseStr(sczRelatedBundleId);
210 - ReleaseRegKey(hkUninstallKey);
211 -
212 - return hr;
191 + return result;
192 }
193
194 static HRESULT LoadIfRelatedBundle(
216 - __in BOOL fPerMachine,
217 - __in REG_KEY_BITNESS regBitness,
218 - __in HKEY hkUninstallKey,
219 - __in_z LPCWSTR sczRelatedBundleId,
195 + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
196 __in BURN_REGISTRATION* pRegistration,
197 __in BURN_RELATED_BUNDLES* pRelatedBundles
198 )
199 {
200 HRESULT hr = S_OK;
225 - HKEY hkBundleId = NULL;
226 - BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE;
227 -
228 - hr = RegOpenEx(hkUninstallKey, sczRelatedBundleId, KEY_READ, regBitness, &hkBundleId);
229 - ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId);
230 -
231 - hr = DetermineRelationType(hkBundleId, pRegistration, &relationType);
232 - if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType)
233 - {
234 - // Must not be a related bundle.
235 - hr = E_NOTFOUND;
236 - }
237 - else // load the related bundle.
238 - {
239 - hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
240 - ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
241 -
242 - BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
243 -
244 - hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle);
245 - ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId);
246 -
247 - hr = DependencyDetectRelatedBundle(pRelatedBundle, pRegistration);
248 - ExitOnFailure(hr, "Failed to detect dependencies for related bundle.");
249 -
250 - ++pRelatedBundles->cRelatedBundles;
251 - }
252 -
253 -LExit:
254 - ReleaseRegKey(hkBundleId);
255 -
256 - return hr;
257 -}
258 -
259 -static HRESULT DetermineRelationType(
260 - __in HKEY hkBundleId,
261 - __in BURN_REGISTRATION* pRegistration,
262 - __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
263 - )
264 -{
265 - HRESULT hr = S_OK;
266 - LPWSTR* rgsczUpgradeCodes = NULL;
267 - DWORD cUpgradeCodes = 0;
268 - STRINGDICT_HANDLE sdUpgradeCodes = NULL;
269 - LPWSTR* rgsczAddonCodes = NULL;
270 - DWORD cAddonCodes = 0;
271 - STRINGDICT_HANDLE sdAddonCodes = NULL;
272 - LPWSTR* rgsczDetectCodes = NULL;
273 - DWORD cDetectCodes = 0;
274 - STRINGDICT_HANDLE sdDetectCodes = NULL;
275 - LPWSTR* rgsczPatchCodes = NULL;
276 - DWORD cPatchCodes = 0;
277 - STRINGDICT_HANDLE sdPatchCodes = NULL;
278 -
279 - *pRelationType = BOOTSTRAPPER_RELATION_NONE;
280 -
281 - // All remaining operations should treat all related bundles as non-vital.
282 - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
283 - if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
284 - {
285 - TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
286 -
287 - rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
288 - ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
289 -
290 - hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
291 - if (SUCCEEDED(hr))
292 - {
293 - cUpgradeCodes = 1;
294 - }
295 - }
296 -
297 - // Compare upgrade codes.
298 - if (SUCCEEDED(hr))
299 - {
300 - hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
301 - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
302 -
303 - // Upgrade relationship: when their upgrade codes match our upgrade codes.
304 - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
305 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
306 - {
307 - hr = S_OK;
308 - }
309 - else
310 - {
311 - ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
312 -
313 - *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE;
314 - ExitFunction();
315 - }
316 -
317 - // Detect relationship: when their upgrade codes match our detect codes.
318 - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
319 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
320 - {
321 - hr = S_OK;
322 - }
323 - else
324 - {
325 - ExitOnFailure(hr, "Failed to do array search for detect code match.");
326 -
327 - *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
328 - ExitFunction();
329 - }
330 -
331 - // Dependent relationship: when their upgrade codes match our addon codes.
332 - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
333 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
334 - {
335 - hr = S_OK;
336 - }
337 - else
338 - {
339 - ExitOnFailure(hr, "Failed to do array search for addon code match.");
340 -
341 - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
342 - ExitFunction();
343 - }
344 -
345 - // Dependent relationship: when their upgrade codes match our patch codes.
346 - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
347 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
348 - {
349 - hr = S_OK;
350 - }
351 - else
352 - {
353 - ExitOnFailure(hr, "Failed to do array search for addon code match.");
354 -
355 - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
356 - ExitFunction();
357 - }
358 -
359 - ReleaseNullDict(sdUpgradeCodes);
360 - ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
361 - }
362 -
363 - // Compare addon codes.
364 - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
365 - if (SUCCEEDED(hr))
366 - {
367 - hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
368 - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
369 -
370 - // Addon relationship: when their addon codes match our detect codes.
371 - hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
372 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
373 - {
374 - hr = S_OK;
375 - }
376 - else
377 - {
378 - ExitOnFailure(hr, "Failed to do array search for addon code match.");
379 -
380 - *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
381 - ExitFunction();
382 - }
383 -
384 - // Addon relationship: when their addon codes match our upgrade codes.
385 - hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
386 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
387 - {
388 - hr = S_OK;
389 - }
390 - else
391 - {
392 - ExitOnFailure(hr, "Failed to do array search for addon code match.");
393 -
394 - *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
395 - ExitFunction();
396 - }
397 -
398 - ReleaseNullDict(sdAddonCodes);
399 - ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
400 - }
201 + BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext;
202 + BOOTSTRAPPER_RELATION_TYPE relationType = (BOOTSTRAPPER_RELATION_TYPE)pBundle->relationType;
203 + BURN_RELATED_BUNDLE* pRelatedBundle = NULL;
204
402 - // Compare patch codes.
403 - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
404 - if (SUCCEEDED(hr))
205 + // If we found our bundle id, it's not a related bundle.
206 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pRegistration->sczId, -1))
207 {
406 - hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
407 - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
408 -
409 - // Patch relationship: when their patch codes match our detect codes.
410 - hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
411 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
412 - {
413 - hr = S_OK;
414 - }
415 - else
416 - {
417 - ExitOnFailure(hr, "Failed to do array search for patch code match.");
418 -
419 - *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
420 - ExitFunction();
421 - }
422 -
423 - // Patch relationship: when their patch codes match our upgrade codes.
424 - hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
425 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
426 - {
427 - hr = S_OK;
428 - }
429 - else
430 - {
431 - ExitOnFailure(hr, "Failed to do array search for patch code match.");
432 -
433 - *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
434 - ExitFunction();
435 - }
436 -
437 - ReleaseNullDict(sdPatchCodes);
438 - ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
208 + ExitFunction1(hr = S_FALSE);
209 }
210
441 - // Compare detect codes.
442 - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
443 - if (SUCCEEDED(hr))
444 - {
445 - hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
446 - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
211 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
212 + ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
213
448 - // Detect relationship: when their detect codes match our detect codes.
449 - hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
450 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
451 - {
452 - hr = S_OK;
453 - }
454 - else
455 - {
456 - ExitOnFailure(hr, "Failed to do array search for detect code match.");
214 + pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
215
458 - *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
459 - ExitFunction();
460 - }
216 + hr = LoadRelatedBundleFromKey(pBundle->wzBundleId, pBundle->hkBundle, fPerMachine, relationType, pRelatedBundle);
217 + ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", pBundle->wzBundleId);
218
462 - // Dependent relationship: when their detect codes match our addon codes.
463 - hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
464 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
465 - {
466 - hr = S_OK;
467 - }
468 - else
469 - {
470 - ExitOnFailure(hr, "Failed to do array search for addon code match.");
471 -
472 - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
473 - ExitFunction();
474 - }
475 -
476 - // Dependent relationship: when their detect codes match our patch codes.
477 - hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
478 - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
479 - {
480 - hr = S_OK;
481 - }
482 - else
483 - {
484 - ExitOnFailure(hr, "Failed to do array search for addon code match.");
219 + hr = DependencyDetectRelatedBundle(pRelatedBundle, pRegistration);
220 + ExitOnFailure(hr, "Failed to detect dependencies for related bundle.");
221
486 - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
487 - ExitFunction();
488 - }
489 -
490 - ReleaseNullDict(sdDetectCodes);
491 - ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
492 - }
222 + ++pRelatedBundles->cRelatedBundles;
223
224 LExit:
495 - if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType)
496 - {
497 - hr = E_NOTFOUND;
498 - }
499 -
500 - ReleaseDict(sdUpgradeCodes);
501 - ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
502 - ReleaseDict(sdAddonCodes);
503 - ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
504 - ReleaseDict(sdDetectCodes);
505 - ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
506 - ReleaseDict(sdPatchCodes);
507 - ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
508 -
225 return hr;
226 }
227
src/burn/test/BurnUnitTest/BurnTestException.h
+3 -8
@@ -13,19 +13,14 @@ namespace Test
13 namespace Bootstrapper
14 {
15 using namespace System;
16 + using namespace WixBuildTools::TestSupport;
17
17 - public ref struct BurnTestException : public System::Exception
18 + public ref struct BurnTestException : public SucceededException
19 {
20 public:
20 - BurnTestException(HRESULT error)
21 - {
22 - this->HResult = error;
23 - }
24 -
21 BurnTestException(HRESULT error, String^ message)
26 - : Exception(message)
22 + : SucceededException(error, message)
23 {
28 - this->HResult = error;
24 }
25
26 property Int32 ErrorCode
src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
+1
@@ -56,6 +56,7 @@
56 <DisableSpecificWarnings>4564;4691</DisableSpecificWarnings>
57 </ClCompile>
58 <ClCompile Include="RegistrationTest.cpp" />
59 + <ClCompile Include="RelatedBundleTest.cpp" />
60 <ClCompile Include="SearchTest.cpp" />
61 <ClCompile Include="TestRegistryFixture.cpp" />
62 <ClCompile Include="VariableHelpers.cpp" />
src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
+3
@@ -39,6 +39,9 @@
39 <ClCompile Include="RegistrationTest.cpp">
40 <Filter>Source Files</Filter>
41 </ClCompile>
42 + <ClCompile Include="RelatedBundleTest.cpp">
43 + <Filter>Source Files</Filter>
44 + </ClCompile>
45 <ClCompile Include="SearchTest.cpp">
46 <Filter>Source Files</Filter>
47 </ClCompile>
src/burn/test/BurnUnitTest/RelatedBundleTest.cpp new
+199
@@ -0,0 +1,199 @@
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 +namespace Microsoft
7 +{
8 +namespace Tools
9 +{
10 +namespace WindowsInstallerXml
11 +{
12 +namespace Test
13 +{
14 +namespace Bootstrapper
15 +{
16 + using namespace System;
17 + using namespace System::IO;
18 + using namespace Xunit;
19 + using namespace WixBuildTools::TestSupport;
20 +
21 + public ref class RelatedBundleTest : BurnUnitTest, IClassFixture<TestRegistryFixture^>
22 + {
23 + private:
24 + TestRegistryFixture^ testRegistry;
25 + public:
26 + RelatedBundleTest(BurnTestFixture^ fixture, TestRegistryFixture^ registryFixture) : BurnUnitTest(fixture)
27 + {
28 + this->testRegistry = registryFixture;
29 + }
30 +
31 + [Fact]
32 + void RelatedBundleDetectPerMachineTest()
33 + {
34 + HRESULT hr = S_OK;
35 + IXMLDOMElement* pixeBundle = NULL;
36 + BURN_REGISTRATION registration = { };
37 + BURN_RELATED_BUNDLES relatedBundles = { };
38 + BURN_CACHE cache = { };
39 + BURN_ENGINE_COMMAND internalCommand = { };
40 +
41 + try
42 + {
43 + this->testRegistry->SetUp();
44 + this->RegisterFakeBundles();
45 +
46 + LPCWSTR wzDocument =
47 + L"<Bundle>"
48 + L" <UX>"
49 + L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' />"
50 + L" </UX>"
51 + L" <RelatedBundle Id='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Action='Upgrade' />"
52 + L" <Registration Id='{D54F896D-1952-43E6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='yes'>"
53 + L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
54 + L" </Registration>"
55 + L"</Bundle>";
56 +
57 + // load XML document
58 + LoadBundleXmlHelper(wzDocument, &pixeBundle);
59 +
60 + hr = CacheInitialize(&cache, &internalCommand);
61 + TestThrowOnFailure(hr, L"Failed initialize cache.");
62 +
63 + hr = RegistrationParseFromXml(&registration, &cache, pixeBundle);
64 + TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
65 +
66 + RelatedBundlesInitializeForScope(registration.fPerMachine, &registration, &relatedBundles);
67 +
68 + Assert::Equal(1lu, relatedBundles.cRelatedBundles);
69 +
70 + BURN_RELATED_BUNDLE* pRelatedBundle = relatedBundles.rgRelatedBundles + 0;
71 + NativeAssert::StringEqual(L"{AD75BE46-B5D7-4208-BC8B-918553C72D83}", pRelatedBundle->package.sczId);
72 + //{E2355133-384C-4332-9B62-1FA950D707B7} should be missing because it causes an error while processing it. It's important that this doesn't cause initialization to fail.
73 + }
74 + finally
75 + {
76 + ReleaseObject(pixeBundle);
77 + RegistrationUninitialize(&registration);
78 +
79 + this->testRegistry->TearDown();
80 + }
81 + }
82 +
83 + [Fact]
84 + void RelatedBundleDetectPerUserTest()
85 + {
86 + HRESULT hr = S_OK;
87 + IXMLDOMElement* pixeBundle = NULL;
88 + BURN_REGISTRATION registration = { };
89 + BURN_RELATED_BUNDLES relatedBundles = { };
90 + BURN_CACHE cache = { };
91 + BURN_ENGINE_COMMAND internalCommand = { };
92 +
93 + try
94 + {
95 + this->testRegistry->SetUp();
96 + this->RegisterFakeBundles();
97 +
98 + LPCWSTR wzDocument =
99 + L"<Bundle>"
100 + L" <UX>"
101 + L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' />"
102 + L" </UX>"
103 + L" <RelatedBundle Id='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Action='Upgrade' />"
104 + L" <Registration Id='{3DB49D3D-1FB8-4147-A465-BBE8BFD0DAD0}' Tag='foo' ProviderKey='foo' Version='4.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
105 + L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='4.0.0.0' />"
106 + L" </Registration>"
107 + L"</Bundle>";
108 +
109 + // load XML document
110 + LoadBundleXmlHelper(wzDocument, &pixeBundle);
111 +
112 + hr = CacheInitialize(&cache, &internalCommand);
113 + TestThrowOnFailure(hr, L"Failed initialize cache.");
114 +
115 + hr = RegistrationParseFromXml(&registration, &cache, pixeBundle);
116 + TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
117 +
118 + RelatedBundlesInitializeForScope(registration.fPerMachine, &registration, &relatedBundles);
119 +
120 + Assert::Equal(1lu, relatedBundles.cRelatedBundles);
121 +
122 + BURN_RELATED_BUNDLE* pRelatedBundle = relatedBundles.rgRelatedBundles + 0;
123 + NativeAssert::StringEqual(L"{6DB5D48C-CD7D-40D2-BCBC-AF630E136761}", pRelatedBundle->package.sczId);
124 + //{42D16EBE-8B6B-4A9A-9AE9-5300F30011AA} should be missing because it causes an error while processing it. It's important that this doesn't cause initialization to fail.
125 + }
126 + finally
127 + {
128 + ReleaseObject(pixeBundle);
129 + RegistrationUninitialize(&registration);
130 +
131 + this->testRegistry->TearDown();
132 + }
133 + }
134 +
135 + void RegisterFakeBundles()
136 + {
137 + this->RegisterFakeBundle(L"{D54F896D-1952-43E6-9C67-B5652240618C}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"1.0.0.0", TRUE);
138 + this->RegisterFakeBundle(L"{E2355133-384C-4332-9B62-1FA950D707B7}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", L"", L"1.1.0.0", TRUE);
139 + this->RegisterFakeBundle(L"{AD75BE46-B5D7-4208-BC8B-918553C72D83}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"2.0.0.0", TRUE);
140 + this->RegisterFakeBundle(L"{6DB5D48C-CD7D-40D2-BCBC-AF630E136761}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"3.0.0.0", FALSE);
141 + this->RegisterFakeBundle(L"{42D16EBE-8B6B-4A9A-9AE9-5300F30011AA}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", L"", L"3.1.0.0", FALSE);
142 + this->RegisterFakeBundle(L"{3DB49D3D-1FB8-4147-A465-BBE8BFD0DAD0}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"4.0.0.0", FALSE);
143 + }
144 +
145 + void RegisterFakeBundle(LPCWSTR wzBundleId, LPCWSTR wzUpgradeCodes, LPCWSTR wzCachePath, LPCWSTR wzVersion, BOOL fPerMachine)
146 + {
147 + HRESULT hr = S_OK;
148 + LPWSTR* rgsczUpgradeCodes = NULL;
149 + DWORD cUpgradeCodes = 0;
150 + LPWSTR sczRegistrationKey = NULL;
151 + LPWSTR sczCachePath = NULL;
152 + HKEY hkRegistration = NULL;
153 + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
154 +
155 + try
156 + {
157 + hr = StrSplitAllocArray(&rgsczUpgradeCodes, reinterpret_cast<UINT*>(&cUpgradeCodes), wzUpgradeCodes, L";");
158 + NativeAssert::Succeeded(hr, "Failed to split upgrade codes.");
159 +
160 + hr = StrAllocFormatted(&sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId);
161 + NativeAssert::Succeeded(hr, "Failed to build uninstall registry key path.");
162 +
163 + if (!wzCachePath)
164 + {
165 + hr = StrAllocFormatted(&sczCachePath, L"%ls.exe", wzBundleId);
166 + NativeAssert::Succeeded(hr, "Failed to build cache path.");
167 +
168 + wzCachePath = sczCachePath;
169 + }
170 +
171 + hr = RegCreate(hkRoot, sczRegistrationKey, KEY_WRITE, &hkRegistration);
172 + NativeAssert::Succeeded(hr, "Failed to create registration key.");
173 +
174 + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, rgsczUpgradeCodes, cUpgradeCodes);
175 + NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE);
176 +
177 + if (wzCachePath && *wzCachePath)
178 + {
179 + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, wzCachePath);
180 + NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH);
181 + }
182 +
183 + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, wzVersion);
184 + NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION);
185 + }
186 + finally
187 + {
188 + ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
189 + ReleaseStr(sczRegistrationKey);
190 + ReleaseStr(sczCachePath);
191 + ReleaseRegKey(hkRegistration);
192 + }
193 + }
194 + };
195 +}
196 +}
197 +}
198 +}
199 +}
src/burn/test/BurnUnitTest/precomp.h
+1
@@ -53,6 +53,7 @@
53 #include "update.h"
54 #include "pseudobundle.h"
55 #include "registration.h"
56 +#include "relatedbundle.h"
57 #include "plan.h"
58 #include "pipe.h"
59 #include "logging.h"
src/internal/WixBuildTools.TestSupport/SucceededException.cs
+1
@@ -13,6 +13,7 @@ namespace WixBuildTools.TestSupport
13 "Message: {1}",
14 hr, userMessage))
15 {
16 + this.HResult = hr;
17 }
18 }
19 }
src/libs/dutil/WixToolset.DUtil/butil.cpp
+420 -60
@@ -19,6 +19,9 @@
19 // constants
20 // From engine/registration.h
21 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
22 +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode";
23 +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode";
24 +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode";
25 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
26 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
27 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
@@ -30,7 +33,41 @@ enum INTERNAL_BUNDLE_STATUS
33 INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY,
34 };
35
36 +typedef struct _BUNDLE_QUERY_CONTEXT
37 +{
38 + BUNDLE_INSTALL_CONTEXT installContext;
39 + REG_KEY_BITNESS regBitness;
40 + PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK pfnCallback;
41 + LPVOID pvContext;
42 +
43 + LPCWSTR* rgwzDetectCodes;
44 + DWORD cDetectCodes;
45 +
46 + LPCWSTR* rgwzUpgradeCodes;
47 + DWORD cUpgradeCodes;
48 +
49 + LPCWSTR* rgwzAddonCodes;
50 + DWORD cAddonCodes;
51 +
52 + LPCWSTR* rgwzPatchCodes;
53 + DWORD cPatchCodes;
54 +} BUNDLE_QUERY_CONTEXT;
55 +
56 // Forward declarations.
57 +static HRESULT QueryRelatedBundlesForScopeAndBitness(
58 + __in BUNDLE_QUERY_CONTEXT* pQueryContext
59 + );
60 +static HRESULT QueryPotentialRelatedBundle(
61 + __in BUNDLE_QUERY_CONTEXT* pQueryContext,
62 + __in HKEY hkUninstallKey,
63 + __in_z LPCWSTR wzRelatedBundleId,
64 + __inout BUNDLE_QUERY_CALLBACK_RESULT* pResult
65 + );
66 +static HRESULT DetermineRelationType(
67 + __in BUNDLE_QUERY_CONTEXT* pQueryContext,
68 + __in HKEY hkBundleId,
69 + __out BUNDLE_RELATION_TYPE* pRelationType
70 + );
71 /********************************************************************
72 LocateAndQueryBundleValue - Locates the requested key for the bundle,
73 then queries the registry type for requested value.
@@ -160,11 +197,13 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
197 HKEY hkBundle = NULL;
198 LPWSTR sczUninstallSubKey = NULL;
199 LPWSTR sczUninstallSubKeyPath = NULL;
163 - LPWSTR sczValue = NULL;
164 - DWORD dwType = 0;
165 - LPWSTR* rgsczBundleUpgradeCodes = NULL;
166 - DWORD cBundleUpgradeCodes = 0;
200 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
201 + BUNDLE_QUERY_CONTEXT queryContext = { };
202 + BUNDLE_RELATION_TYPE relationType = BUNDLE_RELATION_NONE;
203 +
204 + queryContext.installContext = context;
205 + queryContext.rgwzUpgradeCodes = &wzUpgradeCode;
206 + queryContext.cUpgradeCodes = 1;
207
208 if (!wzUpgradeCode || !pdwStartIndex)
209 {
@@ -185,60 +224,12 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
224 hr = RegOpenEx(hkRoot, sczUninstallSubKeyPath, KEY_READ, kbKeyBitness, &hkBundle);
225 ButilExitOnFailure(hr, "Failed to open uninstall key path.");
226
188 - // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ
189 - hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType);
190 - if (FAILED(hr))
227 + hr = DetermineRelationType(&queryContext, hkBundle, &relationType);
228 + if (SUCCEEDED(hr) && BUNDLE_RELATION_UPGRADE == relationType)
229 {
192 - ReleaseRegKey(hkBundle);
193 - ReleaseNullStr(sczUninstallSubKey);
194 - ReleaseNullStr(sczUninstallSubKeyPath);
195 - // Not a bundle
196 - continue;
197 - }
198 -
199 - switch (dwType)
200 - {
201 - case REG_SZ:
202 - hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue);
203 - ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property.");
204 -
205 - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1))
206 - {
207 - *pdwStartIndex = dwIndex;
208 - fUpgradeCodeFound = TRUE;
209 - break;
210 - }
211 -
212 - ReleaseNullStr(sczValue);
213 -
214 - break;
215 - case REG_MULTI_SZ:
216 - hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes);
217 - ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property.");
230 + fUpgradeCodeFound = TRUE;
231 + *pdwStartIndex = dwIndex;
232
219 - for (DWORD i = 0; i < cBundleUpgradeCodes; i++)
220 - {
221 - LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i];
222 - if (wzBundleUpgradeCode && *wzBundleUpgradeCode)
223 - {
224 - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1))
225 - {
226 - *pdwStartIndex = dwIndex;
227 - fUpgradeCodeFound = TRUE;
228 - break;
229 - }
230 - }
231 - }
232 - ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
233 -
234 - break;
235 -
236 - default:
237 - ButilExitWithRootFailure(hr, E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType);
238 - }
239 -
240 - if (fUpgradeCodeFound)
241 - {
233 if (psczBundleId)
234 {
235 *psczBundleId = sczUninstallSubKey;
@@ -250,17 +241,13 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
241
242 // Cleanup before next iteration
243 ReleaseRegKey(hkBundle);
253 - ReleaseNullStr(sczUninstallSubKey);
254 - ReleaseNullStr(sczUninstallSubKeyPath);
244 }
245
246 LExit:
258 - ReleaseStr(sczValue);
247 ReleaseStr(sczUninstallSubKey);
248 ReleaseStr(sczUninstallSubKeyPath);
249 ReleaseRegKey(hkBundle);
250 ReleaseRegKey(hkUninstall);
263 - ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
251
252 return FAILED(hr) ? hr : fUpgradeCodeFound ? S_OK : S_FALSE;
253 }
@@ -370,6 +357,379 @@ LExit:
357 return hr;
358 }
359
360 +DAPI_(HRESULT) BundleQueryRelatedBundles(
361 + __in BUNDLE_INSTALL_CONTEXT installContext,
362 + __in_z_opt LPCWSTR* rgwzDetectCodes,
363 + __in DWORD cDetectCodes,
364 + __in_z_opt LPCWSTR* rgwzUpgradeCodes,
365 + __in DWORD cUpgradeCodes,
366 + __in_z_opt LPCWSTR* rgwzAddonCodes,
367 + __in DWORD cAddonCodes,
368 + __in_z_opt LPCWSTR* rgwzPatchCodes,
369 + __in DWORD cPatchCodes,
370 + __in PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK pfnCallback,
371 + __in_opt LPVOID pvContext
372 + )
373 +{
374 + HRESULT hr = S_OK;
375 + BUNDLE_QUERY_CONTEXT queryContext = { };
376 +
377 + queryContext.installContext = installContext;
378 + queryContext.rgwzDetectCodes = rgwzDetectCodes;
379 + queryContext.cDetectCodes = cDetectCodes;
380 + queryContext.rgwzUpgradeCodes = rgwzUpgradeCodes;
381 + queryContext.cUpgradeCodes = cUpgradeCodes;
382 + queryContext.rgwzAddonCodes = rgwzAddonCodes;
383 + queryContext.cAddonCodes = cAddonCodes;
384 + queryContext.rgwzPatchCodes = rgwzPatchCodes;
385 + queryContext.cPatchCodes = cPatchCodes;
386 + queryContext.pfnCallback = pfnCallback;
387 + queryContext.pvContext = pvContext;
388 +
389 + queryContext.regBitness = REG_KEY_32BIT;
390 +
391 + hr = QueryRelatedBundlesForScopeAndBitness(&queryContext);
392 + ExitOnFailure(hr, "Failed to query 32-bit related bundles.");
393 +
394 + queryContext.regBitness = REG_KEY_64BIT;
395 +
396 + hr = QueryRelatedBundlesForScopeAndBitness(&queryContext);
397 + ExitOnFailure(hr, "Failed to query 64-bit related bundles.");
398 +
399 +LExit:
400 + return hr;
401 +}
402 +
403 +static HRESULT QueryRelatedBundlesForScopeAndBitness(
404 + __in BUNDLE_QUERY_CONTEXT* pQueryContext
405 + )
406 +{
407 + HRESULT hr = S_OK;
408 + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == pQueryContext->installContext ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
409 + HKEY hkUninstallKey = NULL;
410 + LPWSTR sczRelatedBundleId = NULL;
411 + BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
412 +
413 + hr = RegOpenEx(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, pQueryContext->regBitness, &hkUninstallKey);
414 + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
415 + {
416 + ExitFunction1(hr = S_OK);
417 + }
418 + ExitOnFailure(hr, "Failed to open uninstall registry key.");
419 +
420 + for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
421 + {
422 + hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
423 + if (E_NOMOREITEMS == hr)
424 + {
425 + hr = S_OK;
426 + break;
427 + }
428 + ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
429 +
430 + // Ignore failures here since we'll often find products that aren't actually
431 + // related bundles (or even bundles at all).
432 + HRESULT hrRelatedBundle = QueryPotentialRelatedBundle(pQueryContext, hkUninstallKey, sczRelatedBundleId, &result);
433 + if (SUCCEEDED(hrRelatedBundle) && BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE != result)
434 + {
435 + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
436 + }
437 + }
438 +
439 +LExit:
440 + ReleaseStr(sczRelatedBundleId);
441 + ReleaseRegKey(hkUninstallKey);
442 +
443 + return hr;
444 +}
445 +
446 +static HRESULT QueryPotentialRelatedBundle(
447 + __in BUNDLE_QUERY_CONTEXT* pQueryContext,
448 + __in HKEY hkUninstallKey,
449 + __in_z LPCWSTR wzRelatedBundleId,
450 + __inout BUNDLE_QUERY_CALLBACK_RESULT* pResult
451 + )
452 +{
453 + HRESULT hr = S_OK;
454 + HKEY hkBundleId = NULL;
455 + BUNDLE_RELATION_TYPE relationType = BUNDLE_RELATION_NONE;
456 + BUNDLE_QUERY_RELATED_BUNDLE_RESULT bundle = { };
457 +
458 + hr = RegOpenEx(hkUninstallKey, wzRelatedBundleId, KEY_READ, pQueryContext->regBitness, &hkBundleId);
459 + ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", wzRelatedBundleId);
460 +
461 + hr = DetermineRelationType(pQueryContext, hkBundleId, &relationType);
462 + if (FAILED(hr))
463 + {
464 + ExitFunction();
465 + }
466 +
467 + bundle.installContext = pQueryContext->installContext;
468 + bundle.regBitness = pQueryContext->regBitness;
469 + bundle.wzBundleId = wzRelatedBundleId;
470 + bundle.relationType = relationType;
471 + bundle.hkBundle = hkBundleId;
472 +
473 + *pResult = pQueryContext->pfnCallback(&bundle, pQueryContext->pvContext);
474 +
475 +LExit:
476 + ReleaseRegKey(hkBundleId);
477 +
478 + return hr;
479 +}
480 +
481 +static HRESULT DetermineRelationType(
482 + __in BUNDLE_QUERY_CONTEXT* pQueryContext,
483 + __in HKEY hkBundleId,
484 + __out BUNDLE_RELATION_TYPE* pRelationType
485 + )
486 +{
487 + HRESULT hr = S_OK;
488 + LPWSTR* rgsczUpgradeCodes = NULL;
489 + DWORD cUpgradeCodes = 0;
490 + STRINGDICT_HANDLE sdUpgradeCodes = NULL;
491 + LPWSTR* rgsczAddonCodes = NULL;
492 + DWORD cAddonCodes = 0;
493 + STRINGDICT_HANDLE sdAddonCodes = NULL;
494 + LPWSTR* rgsczDetectCodes = NULL;
495 + DWORD cDetectCodes = 0;
496 + STRINGDICT_HANDLE sdDetectCodes = NULL;
497 + LPWSTR* rgsczPatchCodes = NULL;
498 + DWORD cPatchCodes = 0;
499 + STRINGDICT_HANDLE sdPatchCodes = NULL;
500 +
501 + *pRelationType = BUNDLE_RELATION_NONE;
502 +
503 + hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
504 + if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
505 + {
506 + TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
507 +
508 + rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
509 + ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
510 +
511 + hr = RegReadString(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
512 + if (SUCCEEDED(hr))
513 + {
514 + cUpgradeCodes = 1;
515 + }
516 + }
517 +
518 + // Compare upgrade codes.
519 + if (SUCCEEDED(hr))
520 + {
521 + hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
522 + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
523 +
524 + // Upgrade relationship: when their upgrade codes match our upgrade codes.
525 + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
526 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
527 + {
528 + hr = S_OK;
529 + }
530 + else
531 + {
532 + ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
533 +
534 + *pRelationType = BUNDLE_RELATION_UPGRADE;
535 + ExitFunction();
536 + }
537 +
538 + // Detect relationship: when their upgrade codes match our detect codes.
539 + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
540 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
541 + {
542 + hr = S_OK;
543 + }
544 + else
545 + {
546 + ExitOnFailure(hr, "Failed to do array search for detect code match.");
547 +
548 + *pRelationType = BUNDLE_RELATION_DETECT;
549 + ExitFunction();
550 + }
551 +
552 + // Dependent relationship: when their upgrade codes match our addon codes.
553 + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzAddonCodes), pQueryContext->cAddonCodes);
554 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
555 + {
556 + hr = S_OK;
557 + }
558 + else
559 + {
560 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
561 +
562 + *pRelationType = BUNDLE_RELATION_DEPENDENT;
563 + ExitFunction();
564 + }
565 +
566 + // Dependent relationship: when their upgrade codes match our patch codes.
567 + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzPatchCodes), pQueryContext->cPatchCodes);
568 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
569 + {
570 + hr = S_OK;
571 + }
572 + else
573 + {
574 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
575 +
576 + *pRelationType = BUNDLE_RELATION_DEPENDENT;
577 + ExitFunction();
578 + }
579 +
580 + ReleaseNullDict(sdUpgradeCodes);
581 + ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
582 + }
583 +
584 + // Compare addon codes.
585 + hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
586 + if (SUCCEEDED(hr))
587 + {
588 + hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
589 + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
590 +
591 + // Addon relationship: when their addon codes match our detect codes.
592 + hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
593 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
594 + {
595 + hr = S_OK;
596 + }
597 + else
598 + {
599 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
600 +
601 + *pRelationType = BUNDLE_RELATION_ADDON;
602 + ExitFunction();
603 + }
604 +
605 + // Addon relationship: when their addon codes match our upgrade codes.
606 + hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
607 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
608 + {
609 + hr = S_OK;
610 + }
611 + else
612 + {
613 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
614 +
615 + *pRelationType = BUNDLE_RELATION_ADDON;
616 + ExitFunction();
617 + }
618 +
619 + ReleaseNullDict(sdAddonCodes);
620 + ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
621 + }
622 +
623 + // Compare patch codes.
624 + hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
625 + if (SUCCEEDED(hr))
626 + {
627 + hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
628 + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
629 +
630 + // Patch relationship: when their patch codes match our detect codes.
631 + hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
632 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
633 + {
634 + hr = S_OK;
635 + }
636 + else
637 + {
638 + ExitOnFailure(hr, "Failed to do array search for patch code match.");
639 +
640 + *pRelationType = BUNDLE_RELATION_PATCH;
641 + ExitFunction();
642 + }
643 +
644 + // Patch relationship: when their patch codes match our upgrade codes.
645 + hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
646 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
647 + {
648 + hr = S_OK;
649 + }
650 + else
651 + {
652 + ExitOnFailure(hr, "Failed to do array search for patch code match.");
653 +
654 + *pRelationType = BUNDLE_RELATION_PATCH;
655 + ExitFunction();
656 + }
657 +
658 + ReleaseNullDict(sdPatchCodes);
659 + ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
660 + }
661 +
662 + // Compare detect codes.
663 + hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
664 + if (SUCCEEDED(hr))
665 + {
666 + hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
667 + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
668 +
669 + // Detect relationship: when their detect codes match our detect codes.
670 + hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
671 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
672 + {
673 + hr = S_OK;
674 + }
675 + else
676 + {
677 + ExitOnFailure(hr, "Failed to do array search for detect code match.");
678 +
679 + *pRelationType = BUNDLE_RELATION_DETECT;
680 + ExitFunction();
681 + }
682 +
683 + // Dependent relationship: when their detect codes match our addon codes.
684 + hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzAddonCodes), pQueryContext->cAddonCodes);
685 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
686 + {
687 + hr = S_OK;
688 + }
689 + else
690 + {
691 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
692 +
693 + *pRelationType = BUNDLE_RELATION_DEPENDENT;
694 + ExitFunction();
695 + }
696 +
697 + // Dependent relationship: when their detect codes match our patch codes.
698 + hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzPatchCodes), pQueryContext->cPatchCodes);
699 + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
700 + {
701 + hr = S_OK;
702 + }
703 + else
704 + {
705 + ExitOnFailure(hr, "Failed to do array search for addon code match.");
706 +
707 + *pRelationType = BUNDLE_RELATION_DEPENDENT;
708 + ExitFunction();
709 + }
710 +
711 + ReleaseNullDict(sdDetectCodes);
712 + ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
713 + }
714 +
715 +LExit:
716 + if (SUCCEEDED(hr) && BUNDLE_RELATION_NONE == *pRelationType)
717 + {
718 + hr = E_NOTFOUND;
719 + }
720 +
721 + ReleaseDict(sdUpgradeCodes);
722 + ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
723 + ReleaseDict(sdAddonCodes);
724 + ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
725 + ReleaseDict(sdDetectCodes);
726 + ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
727 + ReleaseDict(sdPatchCodes);
728 + ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
729 +
730 + return hr;
731 +}
732 +
733 static HRESULT LocateAndQueryBundleValue(
734 __in_z LPCWSTR wzBundleId,
735 __in_opt LPCWSTR wzSubKey,
src/libs/dutil/WixToolset.DUtil/inc/butil.h
+49
@@ -12,6 +12,37 @@ typedef enum _BUNDLE_INSTALL_CONTEXT
12 BUNDLE_INSTALL_CONTEXT_USER,
13 } BUNDLE_INSTALL_CONTEXT;
14
15 +typedef enum _BUNDLE_QUERY_CALLBACK_RESULT
16 +{
17 + BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE,
18 + BUNDLE_QUERY_CALLBACK_RESULT_CANCEL,
19 +} BUNDLE_QUERY_CALLBACK_RESULT;
20 +
21 +typedef enum _BUNDLE_RELATION_TYPE
22 +{
23 + BUNDLE_RELATION_NONE,
24 + BUNDLE_RELATION_DETECT,
25 + BUNDLE_RELATION_UPGRADE,
26 + BUNDLE_RELATION_ADDON,
27 + BUNDLE_RELATION_PATCH,
28 + BUNDLE_RELATION_DEPENDENT,
29 + BUNDLE_RELATION_UPDATE,
30 +} BUNDLE_RELATION_TYPE;
31 +
32 +typedef struct _BUNDLE_QUERY_RELATED_BUNDLE_RESULT
33 +{
34 + LPCWSTR wzBundleId;
35 + BUNDLE_INSTALL_CONTEXT installContext;
36 + REG_KEY_BITNESS regBitness;
37 + HKEY hkBundle;
38 + BUNDLE_RELATION_TYPE relationType;
39 +} BUNDLE_QUERY_RELATED_BUNDLE_RESULT;
40 +
41 +typedef BUNDLE_QUERY_CALLBACK_RESULT(CALLBACK *PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK)(
42 + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
43 + __in_opt LPVOID pvContext
44 + );
45 +
46
47 /********************************************************************
48 BundleGetBundleInfo - Queries the bundle installation metadata for a given property,
@@ -155,6 +186,24 @@ HRESULT DAPI BundleGetBundleVariableFixed(
186 __inout SIZE_T* pcchValue
187 );
188
189 +/********************************************************************
190 +BundleQueryRelatedBundles - Queries the bundle installation metadata for installs with the given detect, upgrade, addon, and patch codes.
191 + Passes each related bundle to the callback function.
192 +********************************************************************/
193 +HRESULT BundleQueryRelatedBundles(
194 + __in BUNDLE_INSTALL_CONTEXT installContext,
195 + __in_z_opt LPCWSTR* rgwzDetectCodes,
196 + __in DWORD cDetectCodes,
197 + __in_z_opt LPCWSTR* rgwzUpgradeCodes,
198 + __in DWORD cUpgradeCodes,
199 + __in_z_opt LPCWSTR* rgwzAddonCodes,
200 + __in DWORD cAddonCodes,
201 + __in_z_opt LPCWSTR* rgwzPatchCodes,
202 + __in DWORD cPatchCodes,
203 + __in PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK pfnCallback,
204 + __in_opt LPVOID pvContext
205 + );
206 +
207
208 #ifdef __cplusplus
209 }