@joebigelow / wix-1 / commits / f3c96bca

Allow access to persisted variables from related bundles.

Implements #3704

Jacob Hoover committed Jun 11, 2021 at 17:05 UTC f3c96bcab560cb09355e9366eac3f4195479d95d
7 files changed +420 -22
src/burn/engine/registration.cpp
+92
@@ -32,6 +32,7 @@ const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine";
32 const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor";
33 const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor";
34 const LPCWSTR SWIDTAG_FOLDER = L"swidtag";
35 +const LPCWSTR REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
36
37 // internal function declarations
38
@@ -909,6 +910,7 @@ extern "C" HRESULT RegistrationSessionEnd(
910 {
911 HRESULT hr = S_OK;
912 LPWSTR sczRebootRequiredKey = NULL;
913 + LPWSTR sczVariableKey = NULL;
914 HKEY hkRebootRequired = NULL;
915 HKEY hkRegistration = NULL;
916
@@ -956,6 +958,17 @@ extern "C" HRESULT RegistrationSessionEnd(
958
959 RemoveSoftwareTags(pVariables, &pRegistration->softwareTags);
960
961 + // build variable registry key path
962 + hr = StrAllocFormatted(&sczVariableKey, L"%s\\%s", pRegistration->sczRegistrationKey, REGISTRY_BUNDLE_VARIABLE_KEY);
963 + ExitOnFailure(hr, "Failed to build variable registry key path.");
964 +
965 + // Delete registration variable key.
966 + hr = RegDelete(pRegistration->hkRoot, sczVariableKey, REG_KEY_DEFAULT, FALSE);
967 + if (E_FILENOTFOUND != hr)
968 + {
969 + ExitOnFailure(hr, "Failed to delete registration variable key: %ls", sczVariableKey);
970 + }
971 +
972 // Delete registration key.
973 hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE);
974 if (E_FILENOTFOUND != hr)
@@ -985,6 +998,7 @@ extern "C" HRESULT RegistrationSessionEnd(
998 LExit:
999 ReleaseRegKey(hkRegistration);
1000 ReleaseRegKey(hkRebootRequired);
1001 + ReleaseStr(sczVariableKey);
1002 ReleaseStr(sczRebootRequiredKey);
1003
1004 return hr;
@@ -1001,6 +1015,15 @@ extern "C" HRESULT RegistrationSaveState(
1015 )
1016 {
1017 HRESULT hr = S_OK;
1018 + BURN_VARIABLES variables = { };
1019 + SIZE_T iBuffer_Unused = 0;
1020 + HKEY hkRegistration = NULL;
1021 + LPWSTR sczVariableKey = NULL;
1022 + LPWSTR sczVariableValue = NULL;
1023 + LPWSTR sczValueName = NULL;
1024 + DWORD dwType = 0;
1025 + DWORD dwNumberOfExistingValues = 0;
1026 +
1027
1028 // write data to file
1029 hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL);
@@ -1011,7 +1034,76 @@ extern "C" HRESULT RegistrationSaveState(
1034 }
1035 ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile);
1036
1037 + ::InitializeCriticalSection(&variables.csAccess);
1038 +
1039 + hr = VariableDeserialize(&variables, TRUE, pbBuffer, cbBuffer, &iBuffer_Unused);
1040 + ExitOnFailure(hr, "Failed to read variables.");
1041 +
1042 + // build variable registry key path
1043 + hr = StrAllocFormatted(&sczVariableKey, L"%s\\%s", pRegistration->sczRegistrationKey, REGISTRY_BUNDLE_VARIABLE_KEY);
1044 + ExitOnFailure(hr, "Failed to build variable registry key path.");
1045 +
1046 + // open registration variable key
1047 + hr = RegCreate(pRegistration->hkRoot, sczVariableKey, KEY_WRITE | KEY_QUERY_VALUE, &hkRegistration);
1048 + ExitOnFailure(hr, "Failed to create registration variable key.");
1049 +
1050 + hr = RegQueryInfoKey(hkRegistration, 0, 0, 0, 0, 0, 0, &dwNumberOfExistingValues, 0, 0, 0, 0);
1051 + ExitOnFailure(hr, "Failed to query registration variable count.");
1052 +
1053 + for (DWORD i = dwNumberOfExistingValues; i >= 0; --i)
1054 + {
1055 + hr = RegValueEnum(hkRegistration, i, &sczValueName, &dwType);
1056 +
1057 + if (E_NOMOREITEMS == hr)
1058 + {
1059 + hr = S_OK;
1060 + break;
1061 + }
1062 +
1063 + ExitOnFailure(hr, "Failed to enumerate value %u", i);
1064 +
1065 + hr = RegDeleteValue(hkRegistration, sczValueName);
1066 + ExitOnFailure(hr, "Failed to delete registration variable value.");
1067 + }
1068 +
1069 + // Write variables.
1070 + for (DWORD i = 0; i < variables.cVariables; ++i)
1071 + {
1072 + BURN_VARIABLE* pVariable = &variables.rgVariables[i];
1073 +
1074 + // Write variable value.
1075 + switch (pVariable->Value.Type)
1076 + {
1077 + case BURN_VARIANT_TYPE_NONE:
1078 + hr = RegWriteNone(hkRegistration, pVariable->sczName);
1079 + ExitOnFailure(hr, "Failed to set variable value.");
1080 + break;
1081 + case BURN_VARIANT_TYPE_NUMERIC: __fallthrough;
1082 + case BURN_VARIANT_TYPE_VERSION: __fallthrough;
1083 + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
1084 + case BURN_VARIANT_TYPE_STRING:
1085 + hr = BVariantGetString(&pVariable->Value, &sczVariableValue);
1086 + ExitOnFailure(hr, "Failed to get variable value.");
1087 +
1088 + hr = RegWriteString(hkRegistration, pVariable->sczName, sczVariableValue);
1089 + ExitOnFailure(hr, "Failed to set variable value.");
1090 +
1091 + ReleaseNullStrSecure(sczVariableValue);
1092 +
1093 + break;
1094 + default:
1095 + hr = E_INVALIDARG;
1096 + ExitOnFailure(hr, "Unsupported variable type.");
1097 + }
1098 +
1099 + }
1100 LExit:
1101 + VariablesUninitialize(&variables);
1102 + ReleaseStr(sczValueName);
1103 + ReleaseStr(sczVariableValue);
1104 + ReleaseStr(sczVariableKey);
1105 + ReleaseRegKey(hkRegistration);
1106 +
1107 return hr;
1108 }
1109
src/burn/test/BurnUnitTest/RegistrationTest.cpp
+169 -9
@@ -8,9 +8,12 @@
8 #define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU"
9 #define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
10 #define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"
11 +#define TEST_BUNDLE_ID L"{D54F896D-1952-43e6-9C67-B5652240618C}"
12 +#define TEST_BUNDLE_UPGRADE_CODE L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}"
13
14 #define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}"
15 #define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY
16 +#define TEST_VARIABLE_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}\\variables"
17
18
19 static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW(
@@ -496,30 +499,153 @@ namespace Bootstrapper
499 }
500 }
501
499 - [Fact(Skip = "Currently fails")]
500 - void ResumeTest()
502 + [Fact]
503 + void DUtilButilTest()
504 {
505 HRESULT hr = S_OK;
506 IXMLDOMElement* pixeBundle = NULL;
507 LPWSTR sczCurrentProcess = NULL;
508 + LPWSTR sczValue = NULL;
509 + LPWSTR sczRelatedBundleId = NULL;
510 + DWORD dwRelatedBundleIndex = 0;
511 BURN_VARIABLES variables = { };
512 BURN_USER_EXPERIENCE userExperience = { };
513 BOOTSTRAPPER_COMMAND command = { };
514 BURN_REGISTRATION registration = { };
515 BURN_LOGGING logging = { };
516 BURN_PACKAGES packages = { };
511 - BYTE rgbData[256] = { };
512 - BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
517 BYTE* pbBuffer = NULL;
518 SIZE_T cbBuffer = 0;
519 +
520 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
521 try
522 {
518 - for (DWORD i = 0; i < 256; ++i)
523 + // set mock API's
524 + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
525 +
526 + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
527 +
528 + logging.sczPath = L"BurnUnitTest.txt";
529 +
530 + LPCWSTR wzDocument =
531 + L"<Bundle>"
532 + L" <UX>"
533 + L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
534 + L" </UX>"
535 + L" <RelatedBundle Id='" TEST_BUNDLE_UPGRADE_CODE "' Action='Upgrade' />"
536 + L" <Registration Id='" TEST_BUNDLE_ID "' Tag='foo' ProviderKey='" TEST_BUNDLE_ID "' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
537 + L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
538 + L" </Registration>"
539 + L" <Variable Id='MyBurnVariable1' Type='numeric' Value='0' Hidden='no' Persisted='yes' />"
540 + L" <Variable Id='MyBurnVariable2' Type='string' Value='foo' Hidden='no' Persisted='yes' />"
541 + L" <Variable Id='MyBurnVariable3' Type='version' Value='v1.1-alpha' Hidden='no' Persisted='yes' />"
542 + L" <Variable Id='MyBurnVariable4' Type='string' Value='foo' Hidden='no' Persisted='no' />"
543 + L" <CommandLine Variables='upperCase' />"
544 + L"</Bundle>";
545 +
546 + // load XML document
547 + LoadBundleXmlHelper(wzDocument, &pixeBundle);
548 +
549 + hr = VariableInitialize(&variables);
550 + TestThrowOnFailure(hr, L"Failed to initialize variables.");
551 +
552 + hr = VariablesParseFromXml(&variables, pixeBundle);
553 + TestThrowOnFailure(hr, L"Failed to parse variables from XML.");
554 +
555 + hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
556 + TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
557 +
558 + hr = RegistrationParseFromXml(&registration, pixeBundle);
559 + TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
560 +
561 + hr = PlanSetResumeCommand(&registration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
562 + TestThrowOnFailure(hr, L"Failed to set registration resume command.");
563 +
564 + hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
565 + TestThrowOnFailure(hr, L"Failed to get current process path.");
566 +
567 + // begin session
568 + hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
569 + TestThrowOnFailure(hr, L"Failed to register bundle.");
570 +
571 + VariableSetNumericHelper(&variables, L"MyBurnVariable1", 42);
572 + VariableSetStringHelper(&variables, L"MyBurnVariable2", L"bar", FALSE);
573 + VariableSetVersionHelper(&variables, L"MyBurnVariable3", L"v1.0-beta");
574 +
575 + hr = VariableSerialize(&variables, TRUE, &pbBuffer, &cbBuffer);
576 + TestThrowOnFailure(hr, "Failed to serialize variables.");
577 +
578 + if (!Directory::Exists(cacheDirectory))
579 {
520 - rgbData[i] = (BYTE)i;
580 + Directory::CreateDirectory(cacheDirectory);
581 }
582
583 + hr = RegistrationSaveState(&registration, pbBuffer, cbBuffer);
584 + TestThrowOnFailure(hr, L"Failed to save state.");
585 +
586 + ReleaseNullBuffer(pbBuffer);
587 + cbBuffer = 0;
588 + // Verify the variables exist
589 + Assert::Equal<String^>(gcnew String(L"42"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable1"), nullptr));
590 + Assert::Equal<String^>(gcnew String(L"bar"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable2"), nullptr));
591 + Assert::Equal<String^>(gcnew String(L"1.0-beta"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable3"), nullptr));
592 + Assert::Empty((System::Collections::IEnumerable ^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"WixBundleForcedRestartPackage"), nullptr));
593 +
594 + hr = StrAlloc(&sczRelatedBundleId, MAX_GUID_CHARS + 1);
595 +
596 + // Verify we can find ourself via the UpgradeCode
597 + hr = BundleEnumRelatedBundle(TEST_BUNDLE_UPGRADE_CODE, BUNDLE_INSTALL_CONTEXT_USER, &dwRelatedBundleIndex, sczRelatedBundleId);
598 + TestThrowOnFailure(hr, L"Failed to enumerate related bundle.");
599 + Assert::Equal<String^>(gcnew String(TEST_BUNDLE_ID), gcnew String(sczRelatedBundleId));
600 +
601 + // Verify we can read the bundle variables via the API
602 + hr = BundleGetBundleVariable(TEST_BUNDLE_ID, L"MyBurnVariable1", &sczValue);
603 + TestThrowOnFailure(hr, L"Failed to read MyBurnVariable1.");
604 + Assert::Equal<String^>(gcnew String(L"42"), gcnew String(sczValue));
605 +
606 + // end session
607 + hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
608 + TestThrowOnFailure(hr, L"Failed to unregister bundle.");
609 + }
610 + finally
611 + {
612 + ReleaseStr(sczRelatedBundleId);
613 + ReleaseStr(sczCurrentProcess);
614 + ReleaseObject(pixeBundle);
615 + UserExperienceUninitialize(&userExperience);
616 + RegistrationUninitialize(&registration);
617 + VariablesUninitialize(&variables);
618 +
619 + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
620 + if (Directory::Exists(cacheDirectory))
621 + {
622 + Directory::Delete(cacheDirectory, true);
623 + }
624 +
625 + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
626 + }
627 + }
628 +
629 + [Fact]//(Skip = "Currently fails")]
630 + void ResumeTest()
631 + {
632 + HRESULT hr = S_OK;
633 + IXMLDOMElement* pixeBundle = NULL;
634 + LPWSTR sczCurrentProcess = NULL;
635 + LPWSTR sczValue = NULL;
636 + BURN_VARIABLES variables = { };
637 + BURN_USER_EXPERIENCE userExperience = { };
638 + BOOTSTRAPPER_COMMAND command = { };
639 + BURN_REGISTRATION registration = { };
640 + BURN_LOGGING logging = { };
641 + BURN_PACKAGES packages = { };
642 + BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
643 + BYTE* pbBuffer = NULL;
644 + SIZE_T cbBuffer = 0;
645 + SIZE_T piBuffer = 0;
646 + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
647 + try
648 + {
649 // set mock API's
650 RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
651
@@ -535,6 +661,10 @@ namespace Bootstrapper
661 L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
662 L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
663 L" </Registration>"
664 + L" <Variable Id='MyBurnVariable1' Type='numeric' Value='0' Hidden='no' Persisted='yes' />"
665 + L" <Variable Id='MyBurnVariable2' Type='string' Value='foo' Hidden='no' Persisted='yes' />"
666 + L" <Variable Id='MyBurnVariable3' Type='version' Value='v1.1-alpha' Hidden='no' Persisted='yes' />"
667 + L" <CommandLine Variables='upperCase' />"
668 L"</Bundle>";
669
670 // load XML document
@@ -543,6 +673,9 @@ namespace Bootstrapper
673 hr = VariableInitialize(&variables);
674 TestThrowOnFailure(hr, L"Failed to initialize variables.");
675
676 + hr = VariablesParseFromXml(&variables, pixeBundle);
677 + TestThrowOnFailure(hr, L"Failed to parse variables from XML.");
678 +
679 hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
680 TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
681
@@ -565,9 +698,33 @@ namespace Bootstrapper
698 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
699 TestThrowOnFailure(hr, L"Failed to register bundle.");
700
568 - hr = RegistrationSaveState(&registration, rgbData, sizeof(rgbData));
701 + VariableSetNumericHelper(&variables, L"MyBurnVariable1", 42);
702 + VariableSetStringHelper(&variables, L"MyBurnVariable2", L"bar", FALSE);
703 + VariableSetVersionHelper(&variables, L"MyBurnVariable3", L"v1.0-beta");
704 +
705 + hr = VariableSerialize(&variables, TRUE, &pbBuffer, &cbBuffer);
706 + TestThrowOnFailure(hr, "Failed to serialize variables.");
707 +
708 + if (!Directory::Exists(cacheDirectory))
709 + {
710 + Directory::CreateDirectory(cacheDirectory);
711 + }
712 +
713 + hr = RegistrationSaveState(&registration, pbBuffer, cbBuffer);
714 TestThrowOnFailure(hr, L"Failed to save state.");
715
716 + ReleaseNullBuffer(pbBuffer);
717 + cbBuffer = 0;
718 + // Verify the variables exist
719 + Assert::Equal<String^>(gcnew String(L"42"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable1"), nullptr));
720 + Assert::Equal<String^>(gcnew String(L"bar"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable2"), nullptr));
721 + Assert::Equal<String^>(gcnew String(L"1.0-beta"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable3"), nullptr));
722 + Assert::Empty((System::Collections::IEnumerable^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"WixBundleForcedRestartPackage"), nullptr));
723 +
724 + hr = BundleGetBundleVariable(TEST_BUNDLE_ID, L"MyBurnVariable1", &sczValue);
725 + TestThrowOnFailure(hr, L"Failed to read MyBurnVariable1.");
726 + Assert::Equal<String^>(gcnew String(L"42"), gcnew String(sczValue));
727 +
728 // read interrupted resume type
729 hr = RegistrationDetectResumeType(&registration, &resumeType);
730 TestThrowOnFailure(hr, L"Failed to read interrupted resume type.");
@@ -591,8 +748,11 @@ namespace Bootstrapper
748 hr = RegistrationLoadState(&registration, &pbBuffer, &cbBuffer);
749 TestThrowOnFailure(hr, L"Failed to load state.");
750
594 - Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer);
595 - Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData)));
751 + hr = VariableDeserialize(&variables, TRUE, pbBuffer, cbBuffer, &piBuffer);
752 + TestThrowOnFailure(hr, L"Failed to deserialize variables.");
753 +
754 + //Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer);
755 + //Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData)));
756
757 // write active resume mode
758 hr = RegistrationSessionResume(&registration, &variables, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
src/burn/test/BurnUnitTest/precomp.h
+1
@@ -30,6 +30,7 @@
30 #include <xmlutil.h>
31 #include <dictutil.h>
32 #include <deputil.h>
33 +#include <butil.h>
34
35 #include "BootstrapperEngine.h"
36 #include "BootstrapperApplication.h"
src/libs/dutil/WixToolset.DUtil/butil.cpp
+94 -11
@@ -20,6 +20,7 @@
20 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
21 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
22 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
23 +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
24
25 // Forward declarations.
26 /********************************************************************
@@ -29,10 +30,13 @@ NOTE: caller is responsible for closing key
30 ********************************************************************/
31 static HRESULT OpenBundleKey(
32 __in_z LPCWSTR wzBundleId,
32 - __in BUNDLE_INSTALL_CONTEXT context,
33 - __inout HKEY *key);
34 -
33 + __in BUNDLE_INSTALL_CONTEXT context,
34 + __in_opt LPCWSTR szSubKey,
35 + __inout HKEY* key);
36
37 +/********************************************************************
38 +BundleGetBundleInfo - Read the registration data for a gven bundle
39 +********************************************************************/
40 extern "C" HRESULT DAPI BundleGetBundleInfo(
41 __in_z LPCWSTR wzBundleId,
42 __in_z LPCWSTR wzAttribute,
@@ -43,7 +47,6 @@ extern "C" HRESULT DAPI BundleGetBundleInfo(
47 Assert(wzBundleId && wzAttribute);
48
49 HRESULT hr = S_OK;
46 - BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE;
50 LPWSTR sczValue = NULL;
51 HKEY hkBundle = NULL;
52 DWORD cchSource = 0;
@@ -55,8 +58,8 @@ extern "C" HRESULT DAPI BundleGetBundleInfo(
58 ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
59 }
60
58 - if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) &&
59 - FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle)))
61 + if (FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_MACHINE, NULL, &hkBundle)) &&
62 + FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_USER, NULL, &hkBundle)))
63 {
64 ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path.");
65 }
@@ -108,7 +111,10 @@ LExit:
111 return hr;
112 }
113
111 -HRESULT DAPI BundleEnumRelatedBundle(
114 +/********************************************************************
115 +********************************************************************/
116 +
117 +extern "C" HRESULT DAPI BundleEnumRelatedBundle(
118 __in_z LPCWSTR wzUpgradeCode,
119 __in BUNDLE_INSTALL_CONTEXT context,
120 __inout PDWORD pdwStartIndex,
@@ -231,11 +237,80 @@ LExit:
237 return hr;
238 }
239
240 +/********************************************************************
241 +BundleGetBundleVariable - Queries the bundle installation metadata for a given variable,
242 +the caller is expected to free the memory returned vis psczValue
243 +RETURNS:
244 +S_OK
245 + Success, if the variable had a value, it's returned in psczValue
246 +E_INVALIDARG
247 + An invalid parameter was passed to the function.
248 +HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT)
249 + The bundle is not installed
250 +HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY)
251 + The variable is unrecognized
252 +E_NOTIMPL:
253 + Tried to read a bundle variable for a type which has not been implemented
254 +
255 +All other returns are unexpected returns from other dutil methods.
256 +********************************************************************/
257 +
258 +extern "C" HRESULT DAPI BundleGetBundleVariable(
259 + __in_z LPCWSTR wzBundleId,
260 + __in_z LPCWSTR wzVariable,
261 + __deref_out_z LPWSTR * psczValue
262 +)
263 +{
264 + Assert(wzBundleId && wzVariable);
265 +
266 + HRESULT hr = S_OK;
267 + BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE;
268 + HKEY hkBundle = NULL;
269 + DWORD dwType = 0;
270 +
271 + if (!wzBundleId || !wzVariable || !psczValue)
272 + {
273 + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
274 + }
275 +
276 + if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY, &hkBundle)) &&
277 + FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY, &hkBundle)))
278 + {
279 + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key variable path.");
280 + }
281 +
282 + // If the bundle doesn't have the shared variable defined, return ERROR_UNKNOWN_PROPERTY
283 + hr = RegGetType(hkBundle, wzVariable, &dwType);
284 + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle variable.");
285 +
286 + switch (dwType)
287 + {
288 + case REG_SZ:
289 + hr = RegReadString(hkBundle, wzVariable, psczValue);
290 + ButilExitOnFailure(hr, "Failed to read string shared variable.");
291 + break;
292 + case REG_NONE:
293 + hr = S_OK;
294 + break;
295 + default:
296 + ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle variable of type 0x%x not implemented.", dwType);
297 +
298 + }
299 +
300 +LExit:
301 + ReleaseRegKey(hkBundle);
302
303 + return hr;
304 +
305 +}
306 +/********************************************************************
307 +*
308 +********************************************************************/
309 HRESULT OpenBundleKey(
310 __in_z LPCWSTR wzBundleId,
237 - __in BUNDLE_INSTALL_CONTEXT context,
238 - __inout HKEY *key)
311 + __in BUNDLE_INSTALL_CONTEXT context,
312 + __in_opt LPCWSTR szSubKey,
313 + __inout HKEY* key)
314 {
315 Assert(key && wzBundleId);
316 AssertSz(NULL == *key, "*key should be null");
@@ -244,9 +319,16 @@ HRESULT OpenBundleKey(
319 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
320 LPWSTR sczKeypath = NULL;
321
247 - hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId);
322 + if (szSubKey)
323 + {
324 + hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId, szSubKey);
325 + }
326 + else
327 + {
328 + hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId);
329 + }
330 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
249 -
331 +
332 hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key);
333 ButilExitOnFailure(hr, "Failed to open bundle uninstall key path.");
334
@@ -255,3 +337,4 @@ LExit:
337
338 return hr;
339 }
340 +
src/libs/dutil/WixToolset.DUtil/inc/butil.h
+9 -2
@@ -6,11 +6,11 @@
6 extern "C" {
7 #endif
8
9 -enum BUNDLE_INSTALL_CONTEXT
9 +typedef enum BUNDLE_INSTALL_CONTEXT
10 {
11 BUNDLE_INSTALL_CONTEXT_MACHINE,
12 BUNDLE_INSTALL_CONTEXT_USER,
13 -};
13 +} BUNDLE_INSTALL_CONTEXT;
14
15
16 /********************************************************************
@@ -55,6 +55,13 @@ HRESULT DAPI BundleEnumRelatedBundle(
55 __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf
56 );
57
58 +HRESULT DAPI BundleGetBundleVariable(
59 + __in_z LPCWSTR wzBundleId,
60 + __in_z LPCWSTR wzVariable,
61 + __deref_out_z LPWSTR* psczValue
62 +);
63 +
64 +
65 #ifdef __cplusplus
66 }
67 #endif
src/libs/dutil/WixToolset.DUtil/inc/regutil.h
+8
@@ -178,6 +178,10 @@ HRESULT DAPI RegReadVersion(
178 __in_z_opt LPCWSTR wzName,
179 __out DWORD64* pdw64Version
180 );
181 +HRESULT DAPI RegReadNone(
182 + __in HKEY hk,
183 + __in_z_opt LPCWSTR wzName
184 +);
185 HRESULT DAPI RegReadNumber(
186 __in HKEY hk,
187 __in_z_opt LPCWSTR wzName,
@@ -211,6 +215,10 @@ HRESULT DAPI RegWriteStringFormatted(
215 __in __format_string LPCWSTR szFormat,
216 ...
217 );
218 +HRESULT DAPI RegWriteNone(
219 + __in HKEY hk,
220 + __in_z_opt LPCWSTR wzName
221 +);
222 HRESULT DAPI RegWriteNumber(
223 __in HKEY hk,
224 __in_z_opt LPCWSTR wzName,
src/libs/dutil/WixToolset.DUtil/regutil.cpp
+47
@@ -672,6 +672,34 @@ LExit:
672 return hr;
673 }
674
675 +/********************************************************************
676 + RegReadNone - reads a NONE registry key value.
677 +
678 +*********************************************************************/
679 +extern "C" HRESULT DAPI RegReadNone(
680 + __in HKEY hk,
681 + __in_z_opt LPCWSTR wzName)
682 +{
683 + HRESULT hr = S_OK;
684 + DWORD er = ERROR_SUCCESS;
685 + DWORD dwType = 0;
686 +
687 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, NULL);
688 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
689 + {
690 + ExitFunction1(hr = E_FILENOTFOUND);
691 + }
692 + RegExitOnWin32Error(er, hr, "Failed to query registry key value.");
693 +
694 + if (REG_NONE != dwType)
695 + {
696 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
697 + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
698 + }
699 +
700 +LExit:
701 + return hr;
702 +}
703
704 /********************************************************************
705 RegReadNumber - reads a DWORD registry key value as a number.
@@ -885,6 +913,25 @@ LExit:
913 return hr;
914 }
915
916 +/********************************************************************
917 + RegWriteNone - writes a registry key value as none.
918 +
919 +*********************************************************************/
920 +extern "C" HRESULT DAPI RegWriteNone(
921 + __in HKEY hk,
922 + __in_z_opt LPCWSTR wzName
923 +)
924 +{
925 + HRESULT hr = S_OK;
926 + DWORD er = ERROR_SUCCESS;
927 +
928 + er = vpfnRegSetValueExW(hk, wzName, 0, REG_NONE, NULL, NULL);
929 + RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName);
930 +
931 +LExit:
932 + return hr;
933 +}
934 +
935 /********************************************************************
936 RegWriteNumber - writes a registry key value as a number.
937