aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Hoover <jacob.hoover@greenheck.com>2021-06-11 17:05:06 -0500
committerSean Hall <r.sean.hall@gmail.com>2021-07-18 14:41:21 -0500
commitf3c96bcab560cb09355e9366eac3f4195479d95d (patch)
tree1585c1f2af7e3582e14663c29c033702e910d12f
parent5b2b06c9bffb4e6f17409cec41bc0b4b8dab4c90 (diff)
downloadwix-f3c96bcab560cb09355e9366eac3f4195479d95d.tar.gz
wix-f3c96bcab560cb09355e9366eac3f4195479d95d.tar.bz2
wix-f3c96bcab560cb09355e9366eac3f4195479d95d.zip
Allow access to persisted variables from related bundles.
Implements #3704
-rw-r--r--src/burn/engine/registration.cpp92
-rw-r--r--src/burn/test/BurnUnitTest/RegistrationTest.cpp178
-rw-r--r--src/burn/test/BurnUnitTest/precomp.h1
-rw-r--r--src/libs/dutil/WixToolset.DUtil/butil.cpp105
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/butil.h11
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/regutil.h8
-rw-r--r--src/libs/dutil/WixToolset.DUtil/regutil.cpp47
7 files changed, 420 insertions, 22 deletions
diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp
index eed1fee2..4088004d 100644
--- a/src/burn/engine/registration.cpp
+++ b/src/burn/engine/registration.cpp
@@ -32,6 +32,7 @@ const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine";
32const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; 32const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor";
33const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; 33const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor";
34const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; 34const LPCWSTR SWIDTAG_FOLDER = L"swidtag";
35const LPCWSTR REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
35 36
36// internal function declarations 37// internal function declarations
37 38
@@ -909,6 +910,7 @@ extern "C" HRESULT RegistrationSessionEnd(
909{ 910{
910 HRESULT hr = S_OK; 911 HRESULT hr = S_OK;
911 LPWSTR sczRebootRequiredKey = NULL; 912 LPWSTR sczRebootRequiredKey = NULL;
913 LPWSTR sczVariableKey = NULL;
912 HKEY hkRebootRequired = NULL; 914 HKEY hkRebootRequired = NULL;
913 HKEY hkRegistration = NULL; 915 HKEY hkRegistration = NULL;
914 916
@@ -956,6 +958,17 @@ extern "C" HRESULT RegistrationSessionEnd(
956 958
957 RemoveSoftwareTags(pVariables, &pRegistration->softwareTags); 959 RemoveSoftwareTags(pVariables, &pRegistration->softwareTags);
958 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
959 // Delete registration key. 972 // Delete registration key.
960 hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE); 973 hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE);
961 if (E_FILENOTFOUND != hr) 974 if (E_FILENOTFOUND != hr)
@@ -985,6 +998,7 @@ extern "C" HRESULT RegistrationSessionEnd(
985LExit: 998LExit:
986 ReleaseRegKey(hkRegistration); 999 ReleaseRegKey(hkRegistration);
987 ReleaseRegKey(hkRebootRequired); 1000 ReleaseRegKey(hkRebootRequired);
1001 ReleaseStr(sczVariableKey);
988 ReleaseStr(sczRebootRequiredKey); 1002 ReleaseStr(sczRebootRequiredKey);
989 1003
990 return hr; 1004 return hr;
@@ -1001,6 +1015,15 @@ extern "C" HRESULT RegistrationSaveState(
1001 ) 1015 )
1002{ 1016{
1003 HRESULT hr = S_OK; 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
1004 1027
1005 // write data to file 1028 // write data to file
1006 hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL); 1029 hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL);
@@ -1011,7 +1034,76 @@ extern "C" HRESULT RegistrationSaveState(
1011 } 1034 }
1012 ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile); 1035 ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile);
1013 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 }
1014LExit: 1100LExit:
1101 VariablesUninitialize(&variables);
1102 ReleaseStr(sczValueName);
1103 ReleaseStr(sczVariableValue);
1104 ReleaseStr(sczVariableKey);
1105 ReleaseRegKey(hkRegistration);
1106
1015 return hr; 1107 return hr;
1016} 1108}
1017 1109
diff --git a/src/burn/test/BurnUnitTest/RegistrationTest.cpp b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
index 96bdb2bf..298d4631 100644
--- a/src/burn/test/BurnUnitTest/RegistrationTest.cpp
+++ b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
@@ -8,9 +8,12 @@
8#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU" 8#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU"
9#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" 9#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
10#define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce" 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}"
11 13
12#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}" 14#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}"
13#define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY 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"
14 17
15 18
16static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( 19static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW(
@@ -496,30 +499,153 @@ namespace Bootstrapper
496 } 499 }
497 } 500 }
498 501
499 [Fact(Skip = "Currently fails")] 502 [Fact]
500 void ResumeTest() 503 void DUtilButilTest()
501 { 504 {
502 HRESULT hr = S_OK; 505 HRESULT hr = S_OK;
503 IXMLDOMElement* pixeBundle = NULL; 506 IXMLDOMElement* pixeBundle = NULL;
504 LPWSTR sczCurrentProcess = NULL; 507 LPWSTR sczCurrentProcess = NULL;
508 LPWSTR sczValue = NULL;
509 LPWSTR sczRelatedBundleId = NULL;
510 DWORD dwRelatedBundleIndex = 0;
505 BURN_VARIABLES variables = { }; 511 BURN_VARIABLES variables = { };
506 BURN_USER_EXPERIENCE userExperience = { }; 512 BURN_USER_EXPERIENCE userExperience = { };
507 BOOTSTRAPPER_COMMAND command = { }; 513 BOOTSTRAPPER_COMMAND command = { };
508 BURN_REGISTRATION registration = { }; 514 BURN_REGISTRATION registration = { };
509 BURN_LOGGING logging = { }; 515 BURN_LOGGING logging = { };
510 BURN_PACKAGES packages = { }; 516 BURN_PACKAGES packages = { };
511 BYTE rgbData[256] = { };
512 BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
513 BYTE* pbBuffer = NULL; 517 BYTE* pbBuffer = NULL;
514 SIZE_T cbBuffer = 0; 518 SIZE_T cbBuffer = 0;
519
515 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); 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}"));
516 try 521 try
517 { 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))
519 { 579 {
520 rgbData[i] = (BYTE)i; 580 Directory::CreateDirectory(cacheDirectory);
521 } 581 }
522 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 {
523 // set mock API's 649 // set mock API's
524 RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); 650 RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
525 651
@@ -535,6 +661,10 @@ namespace Bootstrapper
535 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'>" 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'>"
536 L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />" 662 L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
537 L" </Registration>" 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' />"
538 L"</Bundle>"; 668 L"</Bundle>";
539 669
540 // load XML document 670 // load XML document
@@ -543,6 +673,9 @@ namespace Bootstrapper
543 hr = VariableInitialize(&variables); 673 hr = VariableInitialize(&variables);
544 TestThrowOnFailure(hr, L"Failed to initialize variables."); 674 TestThrowOnFailure(hr, L"Failed to initialize variables.");
545 675
676 hr = VariablesParseFromXml(&variables, pixeBundle);
677 TestThrowOnFailure(hr, L"Failed to parse variables from XML.");
678
546 hr = UserExperienceParseFromXml(&userExperience, pixeBundle); 679 hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
547 TestThrowOnFailure(hr, L"Failed to parse UX from XML."); 680 TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
548 681
@@ -565,9 +698,33 @@ namespace Bootstrapper
565 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 698 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
566 TestThrowOnFailure(hr, L"Failed to register bundle."); 699 TestThrowOnFailure(hr, L"Failed to register bundle.");
567 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);
569 TestThrowOnFailure(hr, L"Failed to save state."); 714 TestThrowOnFailure(hr, L"Failed to save state.");
570 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
571 // read interrupted resume type 728 // read interrupted resume type
572 hr = RegistrationDetectResumeType(&registration, &resumeType); 729 hr = RegistrationDetectResumeType(&registration, &resumeType);
573 TestThrowOnFailure(hr, L"Failed to read interrupted resume type."); 730 TestThrowOnFailure(hr, L"Failed to read interrupted resume type.");
@@ -591,8 +748,11 @@ namespace Bootstrapper
591 hr = RegistrationLoadState(&registration, &pbBuffer, &cbBuffer); 748 hr = RegistrationLoadState(&registration, &pbBuffer, &cbBuffer);
592 TestThrowOnFailure(hr, L"Failed to load state."); 749 TestThrowOnFailure(hr, L"Failed to load state.");
593 750
594 Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer); 751 hr = VariableDeserialize(&variables, TRUE, pbBuffer, cbBuffer, &piBuffer);
595 Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData))); 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)));
596 756
597 // write active resume mode 757 // write active resume mode
598 hr = RegistrationSessionResume(&registration, &variables, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS); 758 hr = RegistrationSessionResume(&registration, &variables, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h
index d2b57d61..78c44d39 100644
--- a/src/burn/test/BurnUnitTest/precomp.h
+++ b/src/burn/test/BurnUnitTest/precomp.h
@@ -30,6 +30,7 @@
30#include <xmlutil.h> 30#include <xmlutil.h>
31#include <dictutil.h> 31#include <dictutil.h>
32#include <deputil.h> 32#include <deputil.h>
33#include <butil.h>
33 34
34#include "BootstrapperEngine.h" 35#include "BootstrapperEngine.h"
35#include "BootstrapperApplication.h" 36#include "BootstrapperApplication.h"
diff --git a/src/libs/dutil/WixToolset.DUtil/butil.cpp b/src/libs/dutil/WixToolset.DUtil/butil.cpp
index e04b52e9..cda2a658 100644
--- a/src/libs/dutil/WixToolset.DUtil/butil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/butil.cpp
@@ -20,6 +20,7 @@
20const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; 20const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
21const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; 21const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
22const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; 22const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
23const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
23 24
24// Forward declarations. 25// Forward declarations.
25/******************************************************************** 26/********************************************************************
@@ -29,10 +30,13 @@ NOTE: caller is responsible for closing key
29********************************************************************/ 30********************************************************************/
30static HRESULT OpenBundleKey( 31static HRESULT OpenBundleKey(
31 __in_z LPCWSTR wzBundleId, 32 __in_z LPCWSTR wzBundleId,
32 __in BUNDLE_INSTALL_CONTEXT context, 33 __in BUNDLE_INSTALL_CONTEXT context,
33 __inout HKEY *key); 34 __in_opt LPCWSTR szSubKey,
34 35 __inout HKEY* key);
35 36
37/********************************************************************
38BundleGetBundleInfo - Read the registration data for a gven bundle
39********************************************************************/
36extern "C" HRESULT DAPI BundleGetBundleInfo( 40extern "C" HRESULT DAPI BundleGetBundleInfo(
37 __in_z LPCWSTR wzBundleId, 41 __in_z LPCWSTR wzBundleId,
38 __in_z LPCWSTR wzAttribute, 42 __in_z LPCWSTR wzAttribute,
@@ -43,7 +47,6 @@ extern "C" HRESULT DAPI BundleGetBundleInfo(
43 Assert(wzBundleId && wzAttribute); 47 Assert(wzBundleId && wzAttribute);
44 48
45 HRESULT hr = S_OK; 49 HRESULT hr = S_OK;
46 BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE;
47 LPWSTR sczValue = NULL; 50 LPWSTR sczValue = NULL;
48 HKEY hkBundle = NULL; 51 HKEY hkBundle = NULL;
49 DWORD cchSource = 0; 52 DWORD cchSource = 0;
@@ -55,8 +58,8 @@ extern "C" HRESULT DAPI BundleGetBundleInfo(
55 ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); 58 ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
56 } 59 }
57 60
58 if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) && 61 if (FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_MACHINE, NULL, &hkBundle)) &&
59 FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle))) 62 FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_USER, NULL, &hkBundle)))
60 { 63 {
61 ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); 64 ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path.");
62 } 65 }
@@ -108,7 +111,10 @@ LExit:
108 return hr; 111 return hr;
109} 112}
110 113
111HRESULT DAPI BundleEnumRelatedBundle( 114/********************************************************************
115********************************************************************/
116
117extern "C" HRESULT DAPI BundleEnumRelatedBundle(
112 __in_z LPCWSTR wzUpgradeCode, 118 __in_z LPCWSTR wzUpgradeCode,
113 __in BUNDLE_INSTALL_CONTEXT context, 119 __in BUNDLE_INSTALL_CONTEXT context,
114 __inout PDWORD pdwStartIndex, 120 __inout PDWORD pdwStartIndex,
@@ -231,11 +237,80 @@ LExit:
231 return hr; 237 return hr;
232} 238}
233 239
240/********************************************************************
241BundleGetBundleVariable - Queries the bundle installation metadata for a given variable,
242the caller is expected to free the memory returned vis psczValue
243RETURNS:
244S_OK
245 Success, if the variable had a value, it's returned in psczValue
246E_INVALIDARG
247 An invalid parameter was passed to the function.
248HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT)
249 The bundle is not installed
250HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY)
251 The variable is unrecognized
252E_NOTIMPL:
253 Tried to read a bundle variable for a type which has not been implemented
254
255All other returns are unexpected returns from other dutil methods.
256********************************************************************/
257
258extern "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
300LExit:
301 ReleaseRegKey(hkBundle);
234 302
303 return hr;
304
305}
306/********************************************************************
307*
308********************************************************************/
235HRESULT OpenBundleKey( 309HRESULT OpenBundleKey(
236 __in_z LPCWSTR wzBundleId, 310 __in_z LPCWSTR wzBundleId,
237 __in BUNDLE_INSTALL_CONTEXT context, 311 __in BUNDLE_INSTALL_CONTEXT context,
238 __inout HKEY *key) 312 __in_opt LPCWSTR szSubKey,
313 __inout HKEY* key)
239{ 314{
240 Assert(key && wzBundleId); 315 Assert(key && wzBundleId);
241 AssertSz(NULL == *key, "*key should be null"); 316 AssertSz(NULL == *key, "*key should be null");
@@ -244,9 +319,16 @@ HRESULT OpenBundleKey(
244 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 319 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
245 LPWSTR sczKeypath = NULL; 320 LPWSTR sczKeypath = NULL;
246 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 }
248 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); 330 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
249 331
250 hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key); 332 hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key);
251 ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); 333 ButilExitOnFailure(hr, "Failed to open bundle uninstall key path.");
252 334
@@ -255,3 +337,4 @@ LExit:
255 337
256 return hr; 338 return hr;
257} 339}
340
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/butil.h b/src/libs/dutil/WixToolset.DUtil/inc/butil.h
index d1ec73bc..d910c113 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/butil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/butil.h
@@ -6,11 +6,11 @@
6extern "C" { 6extern "C" {
7#endif 7#endif
8 8
9enum BUNDLE_INSTALL_CONTEXT 9typedef enum BUNDLE_INSTALL_CONTEXT
10{ 10{
11 BUNDLE_INSTALL_CONTEXT_MACHINE, 11 BUNDLE_INSTALL_CONTEXT_MACHINE,
12 BUNDLE_INSTALL_CONTEXT_USER, 12 BUNDLE_INSTALL_CONTEXT_USER,
13}; 13} BUNDLE_INSTALL_CONTEXT;
14 14
15 15
16/******************************************************************** 16/********************************************************************
@@ -55,6 +55,13 @@ HRESULT DAPI BundleEnumRelatedBundle(
55 __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf 55 __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf
56 ); 56 );
57 57
58HRESULT DAPI BundleGetBundleVariable(
59 __in_z LPCWSTR wzBundleId,
60 __in_z LPCWSTR wzVariable,
61 __deref_out_z LPWSTR* psczValue
62);
63
64
58#ifdef __cplusplus 65#ifdef __cplusplus
59} 66}
60#endif 67#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
index 75284940..fcf13054 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
@@ -178,6 +178,10 @@ HRESULT DAPI RegReadVersion(
178 __in_z_opt LPCWSTR wzName, 178 __in_z_opt LPCWSTR wzName,
179 __out DWORD64* pdw64Version 179 __out DWORD64* pdw64Version
180 ); 180 );
181HRESULT DAPI RegReadNone(
182 __in HKEY hk,
183 __in_z_opt LPCWSTR wzName
184);
181HRESULT DAPI RegReadNumber( 185HRESULT DAPI RegReadNumber(
182 __in HKEY hk, 186 __in HKEY hk,
183 __in_z_opt LPCWSTR wzName, 187 __in_z_opt LPCWSTR wzName,
@@ -211,6 +215,10 @@ HRESULT DAPI RegWriteStringFormatted(
211 __in __format_string LPCWSTR szFormat, 215 __in __format_string LPCWSTR szFormat,
212 ... 216 ...
213 ); 217 );
218HRESULT DAPI RegWriteNone(
219 __in HKEY hk,
220 __in_z_opt LPCWSTR wzName
221);
214HRESULT DAPI RegWriteNumber( 222HRESULT DAPI RegWriteNumber(
215 __in HKEY hk, 223 __in HKEY hk,
216 __in_z_opt LPCWSTR wzName, 224 __in_z_opt LPCWSTR wzName,
diff --git a/src/libs/dutil/WixToolset.DUtil/regutil.cpp b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
index cb617932..458d8586 100644
--- a/src/libs/dutil/WixToolset.DUtil/regutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
@@ -672,6 +672,34 @@ LExit:
672 return hr; 672 return hr;
673} 673}
674 674
675/********************************************************************
676 RegReadNone - reads a NONE registry key value.
677
678*********************************************************************/
679extern "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
700LExit:
701 return hr;
702}
675 703
676/******************************************************************** 704/********************************************************************
677 RegReadNumber - reads a DWORD registry key value as a number. 705 RegReadNumber - reads a DWORD registry key value as a number.
@@ -886,6 +914,25 @@ LExit:
886} 914}
887 915
888/******************************************************************** 916/********************************************************************
917 RegWriteNone - writes a registry key value as none.
918
919*********************************************************************/
920extern "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
931LExit:
932 return hr;
933}
934
935/********************************************************************
889 RegWriteNumber - writes a registry key value as a number. 936 RegWriteNumber - writes a registry key value as a number.
890 937
891*********************************************************************/ 938*********************************************************************/