aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/butil.cpp480
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/butil.h49
2 files changed, 469 insertions, 60 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/butil.cpp b/src/libs/dutil/WixToolset.DUtil/butil.cpp
index 2f45da56..ac322ae7 100644
--- a/src/libs/dutil/WixToolset.DUtil/butil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/butil.cpp
@@ -19,6 +19,9 @@
19// constants 19// constants
20// From engine/registration.h 20// From engine/registration.h
21const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; 21const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
22const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode";
23const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode";
24const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode";
22const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; 25const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
23const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; 26const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
24const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY = L"variables"; 27const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
@@ -30,7 +33,41 @@ enum INTERNAL_BUNDLE_STATUS
30 INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY, 33 INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY,
31}; 34};
32 35
36typedef 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
33// Forward declarations. 56// Forward declarations.
57static HRESULT QueryRelatedBundlesForScopeAndBitness(
58 __in BUNDLE_QUERY_CONTEXT* pQueryContext
59 );
60static 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 );
66static HRESULT DetermineRelationType(
67 __in BUNDLE_QUERY_CONTEXT* pQueryContext,
68 __in HKEY hkBundleId,
69 __out BUNDLE_RELATION_TYPE* pRelationType
70 );
34/******************************************************************** 71/********************************************************************
35LocateAndQueryBundleValue - Locates the requested key for the bundle, 72LocateAndQueryBundleValue - Locates the requested key for the bundle,
36 then queries the registry type for requested value. 73 then queries the registry type for requested value.
@@ -160,11 +197,13 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
160 HKEY hkBundle = NULL; 197 HKEY hkBundle = NULL;
161 LPWSTR sczUninstallSubKey = NULL; 198 LPWSTR sczUninstallSubKey = NULL;
162 LPWSTR sczUninstallSubKeyPath = NULL; 199 LPWSTR sczUninstallSubKeyPath = NULL;
163 LPWSTR sczValue = NULL;
164 DWORD dwType = 0;
165 LPWSTR* rgsczBundleUpgradeCodes = NULL;
166 DWORD cBundleUpgradeCodes = 0;
167 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 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;
168 207
169 if (!wzUpgradeCode || !pdwStartIndex) 208 if (!wzUpgradeCode || !pdwStartIndex)
170 { 209 {
@@ -185,60 +224,12 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
185 hr = RegOpenEx(hkRoot, sczUninstallSubKeyPath, KEY_READ, kbKeyBitness, &hkBundle); 224 hr = RegOpenEx(hkRoot, sczUninstallSubKeyPath, KEY_READ, kbKeyBitness, &hkBundle);
186 ButilExitOnFailure(hr, "Failed to open uninstall key path."); 225 ButilExitOnFailure(hr, "Failed to open uninstall key path.");
187 226
188 // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ 227 hr = DetermineRelationType(&queryContext, hkBundle, &relationType);
189 hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType); 228 if (SUCCEEDED(hr) && BUNDLE_RELATION_UPGRADE == relationType)
190 if (FAILED(hr))
191 { 229 {
192 ReleaseRegKey(hkBundle); 230 fUpgradeCodeFound = TRUE;
193 ReleaseNullStr(sczUninstallSubKey); 231 *pdwStartIndex = dwIndex;
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.");
218 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 {
242 if (psczBundleId) 233 if (psczBundleId)
243 { 234 {
244 *psczBundleId = sczUninstallSubKey; 235 *psczBundleId = sczUninstallSubKey;
@@ -250,17 +241,13 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
250 241
251 // Cleanup before next iteration 242 // Cleanup before next iteration
252 ReleaseRegKey(hkBundle); 243 ReleaseRegKey(hkBundle);
253 ReleaseNullStr(sczUninstallSubKey);
254 ReleaseNullStr(sczUninstallSubKeyPath);
255 } 244 }
256 245
257LExit: 246LExit:
258 ReleaseStr(sczValue);
259 ReleaseStr(sczUninstallSubKey); 247 ReleaseStr(sczUninstallSubKey);
260 ReleaseStr(sczUninstallSubKeyPath); 248 ReleaseStr(sczUninstallSubKeyPath);
261 ReleaseRegKey(hkBundle); 249 ReleaseRegKey(hkBundle);
262 ReleaseRegKey(hkUninstall); 250 ReleaseRegKey(hkUninstall);
263 ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
264 251
265 return FAILED(hr) ? hr : fUpgradeCodeFound ? S_OK : S_FALSE; 252 return FAILED(hr) ? hr : fUpgradeCodeFound ? S_OK : S_FALSE;
266} 253}
@@ -370,6 +357,379 @@ LExit:
370 return hr; 357 return hr;
371} 358}
372 359
360DAPI_(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
399LExit:
400 return hr;
401}
402
403static 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
439LExit:
440 ReleaseStr(sczRelatedBundleId);
441 ReleaseRegKey(hkUninstallKey);
442
443 return hr;
444}
445
446static 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
475LExit:
476 ReleaseRegKey(hkBundleId);
477
478 return hr;
479}
480
481static 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
715LExit:
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
373static HRESULT LocateAndQueryBundleValue( 733static HRESULT LocateAndQueryBundleValue(
374 __in_z LPCWSTR wzBundleId, 734 __in_z LPCWSTR wzBundleId,
375 __in_opt LPCWSTR wzSubKey, 735 __in_opt LPCWSTR wzSubKey,
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/butil.h b/src/libs/dutil/WixToolset.DUtil/inc/butil.h
index 9c2010ee..0d3eefe3 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/butil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/butil.h
@@ -12,6 +12,37 @@ typedef enum _BUNDLE_INSTALL_CONTEXT
12 BUNDLE_INSTALL_CONTEXT_USER, 12 BUNDLE_INSTALL_CONTEXT_USER,
13} BUNDLE_INSTALL_CONTEXT; 13} BUNDLE_INSTALL_CONTEXT;
14 14
15typedef enum _BUNDLE_QUERY_CALLBACK_RESULT
16{
17 BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE,
18 BUNDLE_QUERY_CALLBACK_RESULT_CANCEL,
19} BUNDLE_QUERY_CALLBACK_RESULT;
20
21typedef 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
32typedef 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
41typedef 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
15 46
16/******************************************************************** 47/********************************************************************
17BundleGetBundleInfo - Queries the bundle installation metadata for a given property, 48BundleGetBundleInfo - Queries the bundle installation metadata for a given property,
@@ -155,6 +186,24 @@ HRESULT DAPI BundleGetBundleVariableFixed(
155 __inout SIZE_T* pcchValue 186 __inout SIZE_T* pcchValue
156 ); 187 );
157 188
189/********************************************************************
190BundleQueryRelatedBundles - 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********************************************************************/
193HRESULT 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
158 207
159#ifdef __cplusplus 208#ifdef __cplusplus
160} 209}