aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/butil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/butil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/butil.cpp127
1 files changed, 45 insertions, 82 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/butil.cpp b/src/libs/dutil/WixToolset.DUtil/butil.cpp
index 175903ad..47f206ea 100644
--- a/src/libs/dutil/WixToolset.DUtil/butil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/butil.cpp
@@ -15,6 +15,7 @@
15#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) 15#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__)
16#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) 16#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__)
17#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__) 17#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__)
18#define ButilExitOnPathFailure(x, b, s, ...) ExitOnPathFailureSource(DUTIL_SOURCE_BUTIL, x, b, s, __VA_ARGS__)
18 19
19// constants 20// constants
20// From engine/registration.h 21// From engine/registration.h
@@ -82,19 +83,6 @@ static HRESULT LocateAndQueryBundleValue(
82 __inout DWORD* pdwType, 83 __inout DWORD* pdwType,
83 __out INTERNAL_BUNDLE_STATUS* pStatus 84 __out INTERNAL_BUNDLE_STATUS* pStatus
84 ); 85 );
85
86/********************************************************************
87OpenBundleKey - Opens the bundle uninstallation key for a given bundle
88
89NOTE: caller is responsible for closing key
90********************************************************************/
91static HRESULT OpenBundleKey(
92 __in_z LPCWSTR wzBundleId,
93 __in BUNDLE_INSTALL_CONTEXT context,
94 __in_opt LPCWSTR wzSubKey,
95 __in REG_KEY_BITNESS kbKeyBitness,
96 __inout HKEY* phKey
97 );
98static HRESULT CopyStringToBuffer( 86static HRESULT CopyStringToBuffer(
99 __in_z LPWSTR wzValue, 87 __in_z LPWSTR wzValue,
100 __in_z_opt LPWSTR wzBuffer, 88 __in_z_opt LPWSTR wzBuffer,
@@ -389,12 +377,12 @@ DAPI_(HRESULT) BundleQueryRelatedBundles(
389 queryContext.regBitness = REG_KEY_32BIT; 377 queryContext.regBitness = REG_KEY_32BIT;
390 378
391 hr = QueryRelatedBundlesForScopeAndBitness(&queryContext); 379 hr = QueryRelatedBundlesForScopeAndBitness(&queryContext);
392 ExitOnFailure(hr, "Failed to query 32-bit related bundles."); 380 ButilExitOnFailure(hr, "Failed to query 32-bit related bundles.");
393 381
394 queryContext.regBitness = REG_KEY_64BIT; 382 queryContext.regBitness = REG_KEY_64BIT;
395 383
396 hr = QueryRelatedBundlesForScopeAndBitness(&queryContext); 384 hr = QueryRelatedBundlesForScopeAndBitness(&queryContext);
397 ExitOnFailure(hr, "Failed to query 64-bit related bundles."); 385 ButilExitOnFailure(hr, "Failed to query 64-bit related bundles.");
398 386
399LExit: 387LExit:
400 return hr; 388 return hr;
@@ -407,15 +395,17 @@ static HRESULT QueryRelatedBundlesForScopeAndBitness(
407 HRESULT hr = S_OK; 395 HRESULT hr = S_OK;
408 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == pQueryContext->installContext ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 396 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == pQueryContext->installContext ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
409 HKEY hkUninstallKey = NULL; 397 HKEY hkUninstallKey = NULL;
398 BOOL fExists = FALSE;
410 LPWSTR sczRelatedBundleId = NULL; 399 LPWSTR sczRelatedBundleId = NULL;
411 BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE; 400 BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
412 401
413 hr = RegOpenEx(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, pQueryContext->regBitness, &hkUninstallKey); 402 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) 403 ButilExitOnPathFailure(hr, fExists, "Failed to open uninstall registry key.");
404
405 if (!fExists)
415 { 406 {
416 ExitFunction1(hr = S_OK); 407 ExitFunction1(hr = S_OK);
417 } 408 }
418 ExitOnFailure(hr, "Failed to open uninstall registry key.");
419 409
420 for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex) 410 for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
421 { 411 {
@@ -425,7 +415,7 @@ static HRESULT QueryRelatedBundlesForScopeAndBitness(
425 hr = S_OK; 415 hr = S_OK;
426 break; 416 break;
427 } 417 }
428 ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles."); 418 ButilExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
429 419
430 // Ignore failures here since we'll often find products that aren't actually 420 // Ignore failures here since we'll often find products that aren't actually
431 // related bundles (or even bundles at all). 421 // related bundles (or even bundles at all).
@@ -456,7 +446,7 @@ static HRESULT QueryPotentialRelatedBundle(
456 BUNDLE_QUERY_RELATED_BUNDLE_RESULT bundle = { }; 446 BUNDLE_QUERY_RELATED_BUNDLE_RESULT bundle = { };
457 447
458 hr = RegOpenEx(hkUninstallKey, wzRelatedBundleId, KEY_READ, pQueryContext->regBitness, &hkBundleId); 448 hr = RegOpenEx(hkUninstallKey, wzRelatedBundleId, KEY_READ, pQueryContext->regBitness, &hkBundleId);
459 ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", wzRelatedBundleId); 449 ButilExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", wzRelatedBundleId);
460 450
461 hr = DetermineRelationType(pQueryContext, hkBundleId, &relationType); 451 hr = DetermineRelationType(pQueryContext, hkBundleId, &relationType);
462 if (FAILED(hr)) 452 if (FAILED(hr))
@@ -506,7 +496,7 @@ static HRESULT DetermineRelationType(
506 TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles."); 496 TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
507 497
508 rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE)); 498 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."); 499 ButilExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
510 500
511 hr = RegReadString(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]); 501 hr = RegReadString(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
512 if (SUCCEEDED(hr)) 502 if (SUCCEEDED(hr))
@@ -519,7 +509,7 @@ static HRESULT DetermineRelationType(
519 if (SUCCEEDED(hr)) 509 if (SUCCEEDED(hr))
520 { 510 {
521 hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE); 511 hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
522 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes"); 512 ButilExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
523 513
524 // Upgrade relationship: when their upgrade codes match our upgrade codes. 514 // Upgrade relationship: when their upgrade codes match our upgrade codes.
525 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes); 515 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
@@ -529,7 +519,7 @@ static HRESULT DetermineRelationType(
529 } 519 }
530 else 520 else
531 { 521 {
532 ExitOnFailure(hr, "Failed to do array search for upgrade code match."); 522 ButilExitOnFailure(hr, "Failed to do array search for upgrade code match.");
533 523
534 *pRelationType = BUNDLE_RELATION_UPGRADE; 524 *pRelationType = BUNDLE_RELATION_UPGRADE;
535 ExitFunction(); 525 ExitFunction();
@@ -543,7 +533,7 @@ static HRESULT DetermineRelationType(
543 } 533 }
544 else 534 else
545 { 535 {
546 ExitOnFailure(hr, "Failed to do array search for detect code match."); 536 ButilExitOnFailure(hr, "Failed to do array search for detect code match.");
547 537
548 *pRelationType = BUNDLE_RELATION_DETECT; 538 *pRelationType = BUNDLE_RELATION_DETECT;
549 ExitFunction(); 539 ExitFunction();
@@ -557,7 +547,7 @@ static HRESULT DetermineRelationType(
557 } 547 }
558 else 548 else
559 { 549 {
560 ExitOnFailure(hr, "Failed to do array search for addon code match."); 550 ButilExitOnFailure(hr, "Failed to do array search for addon code match.");
561 551
562 *pRelationType = BUNDLE_RELATION_DEPENDENT_ADDON; 552 *pRelationType = BUNDLE_RELATION_DEPENDENT_ADDON;
563 ExitFunction(); 553 ExitFunction();
@@ -571,7 +561,7 @@ static HRESULT DetermineRelationType(
571 } 561 }
572 else 562 else
573 { 563 {
574 ExitOnFailure(hr, "Failed to do array search for patch code match."); 564 ButilExitOnFailure(hr, "Failed to do array search for patch code match.");
575 565
576 *pRelationType = BUNDLE_RELATION_DEPENDENT_PATCH; 566 *pRelationType = BUNDLE_RELATION_DEPENDENT_PATCH;
577 ExitFunction(); 567 ExitFunction();
@@ -586,7 +576,7 @@ static HRESULT DetermineRelationType(
586 if (SUCCEEDED(hr)) 576 if (SUCCEEDED(hr))
587 { 577 {
588 hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE); 578 hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
589 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes"); 579 ButilExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
590 580
591 // Addon relationship: when their addon codes match our detect codes. 581 // Addon relationship: when their addon codes match our detect codes.
592 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes); 582 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
@@ -596,7 +586,7 @@ static HRESULT DetermineRelationType(
596 } 586 }
597 else 587 else
598 { 588 {
599 ExitOnFailure(hr, "Failed to do array search for addon code match."); 589 ButilExitOnFailure(hr, "Failed to do array search for addon code match.");
600 590
601 *pRelationType = BUNDLE_RELATION_ADDON; 591 *pRelationType = BUNDLE_RELATION_ADDON;
602 ExitFunction(); 592 ExitFunction();
@@ -610,7 +600,7 @@ static HRESULT DetermineRelationType(
610 } 600 }
611 else 601 else
612 { 602 {
613 ExitOnFailure(hr, "Failed to do array search for addon code match."); 603 ButilExitOnFailure(hr, "Failed to do array search for addon code match.");
614 604
615 *pRelationType = BUNDLE_RELATION_ADDON; 605 *pRelationType = BUNDLE_RELATION_ADDON;
616 ExitFunction(); 606 ExitFunction();
@@ -625,7 +615,7 @@ static HRESULT DetermineRelationType(
625 if (SUCCEEDED(hr)) 615 if (SUCCEEDED(hr))
626 { 616 {
627 hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE); 617 hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
628 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes"); 618 ButilExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
629 619
630 // Patch relationship: when their patch codes match our detect codes. 620 // Patch relationship: when their patch codes match our detect codes.
631 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes); 621 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
@@ -635,7 +625,7 @@ static HRESULT DetermineRelationType(
635 } 625 }
636 else 626 else
637 { 627 {
638 ExitOnFailure(hr, "Failed to do array search for patch code match."); 628 ButilExitOnFailure(hr, "Failed to do array search for patch code match.");
639 629
640 *pRelationType = BUNDLE_RELATION_PATCH; 630 *pRelationType = BUNDLE_RELATION_PATCH;
641 ExitFunction(); 631 ExitFunction();
@@ -649,7 +639,7 @@ static HRESULT DetermineRelationType(
649 } 639 }
650 else 640 else
651 { 641 {
652 ExitOnFailure(hr, "Failed to do array search for patch code match."); 642 ButilExitOnFailure(hr, "Failed to do array search for patch code match.");
653 643
654 *pRelationType = BUNDLE_RELATION_PATCH; 644 *pRelationType = BUNDLE_RELATION_PATCH;
655 ExitFunction(); 645 ExitFunction();
@@ -664,7 +654,7 @@ static HRESULT DetermineRelationType(
664 if (SUCCEEDED(hr)) 654 if (SUCCEEDED(hr))
665 { 655 {
666 hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE); 656 hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
667 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes"); 657 ButilExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
668 658
669 // Detect relationship: when their detect codes match our detect codes. 659 // Detect relationship: when their detect codes match our detect codes.
670 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes); 660 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
@@ -674,7 +664,7 @@ static HRESULT DetermineRelationType(
674 } 664 }
675 else 665 else
676 { 666 {
677 ExitOnFailure(hr, "Failed to do array search for detect code match."); 667 ButilExitOnFailure(hr, "Failed to do array search for detect code match.");
678 668
679 *pRelationType = BUNDLE_RELATION_DETECT; 669 *pRelationType = BUNDLE_RELATION_DETECT;
680 ExitFunction(); 670 ExitFunction();
@@ -688,7 +678,7 @@ static HRESULT DetermineRelationType(
688 } 678 }
689 else 679 else
690 { 680 {
691 ExitOnFailure(hr, "Failed to do array search for addon code match."); 681 ButilExitOnFailure(hr, "Failed to do array search for addon code match.");
692 682
693 *pRelationType = BUNDLE_RELATION_DEPENDENT_ADDON; 683 *pRelationType = BUNDLE_RELATION_DEPENDENT_ADDON;
694 ExitFunction(); 684 ExitFunction();
@@ -702,7 +692,7 @@ static HRESULT DetermineRelationType(
702 } 692 }
703 else 693 else
704 { 694 {
705 ExitOnFailure(hr, "Failed to do array search for patch code match."); 695 ButilExitOnFailure(hr, "Failed to do array search for patch code match.");
706 696
707 *pRelationType = BUNDLE_RELATION_DEPENDENT_PATCH; 697 *pRelationType = BUNDLE_RELATION_DEPENDENT_PATCH;
708 ExitFunction(); 698 ExitFunction();
@@ -740,54 +730,11 @@ static HRESULT LocateAndQueryBundleValue(
740 ) 730 )
741{ 731{
742 HRESULT hr = S_OK; 732 HRESULT hr = S_OK;
733 LPWSTR sczKeypath = NULL;
734 BOOL fExists = TRUE;
743 735
744 *pStatus = INTERNAL_BUNDLE_STATUS_SUCCESS; 736 *pStatus = INTERNAL_BUNDLE_STATUS_SUCCESS;
745 737
746 if (FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_MACHINE, wzSubKey, REG_KEY_32BIT, phKey)) &&
747 FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_MACHINE, wzSubKey, REG_KEY_64BIT, phKey)) &&
748 FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_USER, wzSubKey, REG_KEY_DEFAULT, phKey)))
749 {
750 if (E_FILENOTFOUND == hr)
751 {
752 *pStatus = INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE;
753 ExitFunction1(hr = S_OK);
754 }
755
756 ButilExitOnFailure(hr, "Failed to open bundle key.");
757 }
758
759 // If the bundle doesn't have the value defined, return ERROR_UNKNOWN_PROPERTY
760 hr = RegGetType(*phKey, wzValueName, pdwType);
761 if (FAILED(hr))
762 {
763 if (E_FILENOTFOUND == hr)
764 {
765 *pStatus = INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY;
766 ExitFunction1(hr = S_OK);
767 }
768
769 ButilExitOnFailure(hr, "Failed to read bundle value.");
770 }
771
772LExit:
773 return hr;
774}
775
776static HRESULT OpenBundleKey(
777 __in_z LPCWSTR wzBundleId,
778 __in BUNDLE_INSTALL_CONTEXT context,
779 __in_opt LPCWSTR wzSubKey,
780 __in REG_KEY_BITNESS kbKeyBitness,
781 __inout HKEY* phKey
782 )
783{
784 Assert(phKey && wzBundleId);
785 AssertSz(NULL == *phKey, "*key should be null");
786
787 HRESULT hr = S_OK;
788 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
789 LPWSTR sczKeypath = NULL;
790
791 if (wzSubKey) 738 if (wzSubKey)
792 { 739 {
793 hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId, wzSubKey); 740 hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId, wzSubKey);
@@ -798,8 +745,24 @@ static HRESULT OpenBundleKey(
798 } 745 }
799 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); 746 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
800 747
801 hr = RegOpenEx(hkRoot, sczKeypath, KEY_READ, kbKeyBitness, phKey); 748 if (FAILED(hr = RegOpenEx(HKEY_LOCAL_MACHINE, sczKeypath, KEY_READ, REG_KEY_32BIT, phKey)) &&
802 ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); 749 FAILED(hr = RegOpenEx(HKEY_LOCAL_MACHINE, sczKeypath, KEY_READ, REG_KEY_64BIT, phKey)) &&
750 FAILED(hr = RegOpenEx(HKEY_CURRENT_USER, sczKeypath, KEY_READ, REG_KEY_DEFAULT, phKey)))
751 {
752 ButilExitOnPathFailure(hr, fExists, "Failed to open bundle key.");
753
754 *pStatus = INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE;
755 ExitFunction1(hr = S_OK);
756 }
757
758 hr = RegGetType(*phKey, wzValueName, pdwType);
759 ButilExitOnPathFailure(hr, fExists, "Failed to read bundle value.");
760
761 if (!fExists)
762 {
763 *pStatus = INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY;
764 ExitFunction1(hr = S_OK);
765 }
803 766
804LExit: 767LExit:
805 ReleaseStr(sczKeypath); 768 ReleaseStr(sczKeypath);