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(®istration, pixeBundle);
559
+ TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
560
+
561
+ hr = PlanSetResumeCommand(®istration, 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, ®istration, &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(®istration, 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(®istration, &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(®istration);
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, ®istration, &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(®istration, 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(®istration, 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(®istration, &resumeType);
730
TestThrowOnFailure(hr, L"Failed to read interrupted resume type.");
@@ -591,8 +748,11 @@ namespace Bootstrapper
748
hr = RegistrationLoadState(®istration, &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(®istration, &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