diff options
-rw-r--r-- | src/burn/engine/search.cpp | 50 | ||||
-rw-r--r-- | src/burn/test/BurnUnitTest/TestRegistryFixture.cpp | 4 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/inc/regutil.h | 44 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/regutil.cpp | 420 | ||||
-rw-r--r-- | src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 1 | ||||
-rw-r--r-- | src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 3 | ||||
-rw-r--r-- | src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp | 2 | ||||
-rw-r--r-- | src/libs/dutil/test/DUtilUnitTest/RegUtilTest.cpp | 679 |
8 files changed, 1007 insertions, 196 deletions
diff --git a/src/burn/engine/search.cpp b/src/burn/engine/search.cpp index f521cdbd..b37dc9bd 100644 --- a/src/burn/engine/search.cpp +++ b/src/burn/engine/search.cpp | |||
@@ -955,15 +955,15 @@ static HRESULT RegistrySearchValue( | |||
955 | ) | 955 | ) |
956 | { | 956 | { |
957 | HRESULT hr = S_OK; | 957 | HRESULT hr = S_OK; |
958 | DWORD er = ERROR_SUCCESS; | ||
959 | LPWSTR sczKey = NULL; | 958 | LPWSTR sczKey = NULL; |
960 | LPWSTR sczValue = NULL; | 959 | LPWSTR sczValue = NULL; |
961 | HKEY hKey = NULL; | 960 | HKEY hKey = NULL; |
962 | DWORD dwType = 0; | 961 | DWORD dwType = 0; |
963 | DWORD cbData = 0; | 962 | SIZE_T cbData = 0; |
964 | LPBYTE pData = NULL; | 963 | LPBYTE pData = NULL; |
965 | DWORD cch = 0; | ||
966 | BURN_VARIANT value = { }; | 964 | BURN_VARIANT value = { }; |
965 | DWORD dwValue = 0; | ||
966 | LONGLONG llValue = 0; | ||
967 | 967 | ||
968 | // format key string | 968 | // format key string |
969 | hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); | 969 | hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); |
@@ -988,64 +988,38 @@ static HRESULT RegistrySearchValue( | |||
988 | ExitOnFailure(hr, "Failed to open registry key."); | 988 | ExitOnFailure(hr, "Failed to open registry key."); |
989 | 989 | ||
990 | // get value | 990 | // get value |
991 | er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData); | 991 | hr = RegReadValue(hKey, sczValue, pSearch->RegistrySearch.fExpandEnvironment, &pData, &cbData, &dwType); |
992 | if (ERROR_FILE_NOT_FOUND == er) | 992 | if (E_FILENOTFOUND == hr) |
993 | { | 993 | { |
994 | // What if there is a hidden variable in sczKey or sczValue? | 994 | // What if there is a hidden variable in sczKey or sczValue? |
995 | LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); | 995 | LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); |
996 | 996 | ||
997 | ExitFunction1(hr = S_OK); | 997 | ExitFunction1(hr = S_OK); |
998 | } | 998 | } |
999 | ExitOnWin32Error(er, hr, "Failed to query registry key value size."); | 999 | ExitOnFailure(hr, "Failed to query registry key value."); |
1000 | |||
1001 | pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ | ||
1002 | ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value."); | ||
1003 | |||
1004 | er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData); | ||
1005 | ExitOnWin32Error(er, hr, "Failed to query registry key value."); | ||
1006 | 1000 | ||
1007 | switch (dwType) | 1001 | switch (dwType) |
1008 | { | 1002 | { |
1009 | case REG_DWORD: | 1003 | case REG_DWORD: |
1010 | if (sizeof(LONG) != cbData) | 1004 | if (memcpy_s(&dwValue, sizeof(DWORD), pData, cbData)) |
1011 | { | 1005 | { |
1012 | ExitFunction1(hr = E_UNEXPECTED); | 1006 | ExitFunction1(hr = E_UNEXPECTED); |
1013 | } | 1007 | } |
1014 | hr = BVariantSetNumeric(&value, *((LONG*)pData)); | 1008 | hr = BVariantSetNumeric(&value, dwValue); |
1015 | break; | 1009 | break; |
1016 | case REG_QWORD: | 1010 | case REG_QWORD: |
1017 | if (sizeof(LONGLONG) != cbData) | 1011 | if (memcpy_s(&llValue, sizeof(LONGLONG), pData, cbData)) |
1018 | { | 1012 | { |
1019 | ExitFunction1(hr = E_UNEXPECTED); | 1013 | ExitFunction1(hr = E_UNEXPECTED); |
1020 | } | 1014 | } |
1021 | hr = BVariantSetNumeric(&value, *((LONGLONG*)pData)); | 1015 | hr = BVariantSetNumeric(&value, llValue); |
1022 | break; | 1016 | break; |
1023 | case REG_EXPAND_SZ: | 1017 | case REG_EXPAND_SZ: __fallthrough; |
1024 | if (pSearch->RegistrySearch.fExpandEnvironment) | ||
1025 | { | ||
1026 | hr = StrAlloc(&value.sczValue, cbData); | ||
1027 | ExitOnFailure(hr, "Failed to allocate string buffer."); | ||
1028 | value.Type = BURN_VARIANT_TYPE_STRING; | ||
1029 | |||
1030 | cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData); | ||
1031 | if (cch > cbData) | ||
1032 | { | ||
1033 | hr = StrAlloc(&value.sczValue, cch); | ||
1034 | ExitOnFailure(hr, "Failed to allocate string buffer."); | ||
1035 | |||
1036 | if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch)) | ||
1037 | { | ||
1038 | ExitWithLastError(hr, "Failed to get expand environment string."); | ||
1039 | } | ||
1040 | } | ||
1041 | break; | ||
1042 | } | ||
1043 | __fallthrough; | ||
1044 | case REG_SZ: | 1018 | case REG_SZ: |
1045 | hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE); | 1019 | hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE); |
1046 | break; | 1020 | break; |
1047 | default: | 1021 | default: |
1048 | ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); | 1022 | ExitWithRootFailure(hr, E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); |
1049 | } | 1023 | } |
1050 | ExitOnFailure(hr, "Failed to read registry value."); | 1024 | ExitOnFailure(hr, "Failed to read registry value."); |
1051 | 1025 | ||
diff --git a/src/burn/test/BurnUnitTest/TestRegistryFixture.cpp b/src/burn/test/BurnUnitTest/TestRegistryFixture.cpp index 1f3d7680..89704d09 100644 --- a/src/burn/test/BurnUnitTest/TestRegistryFixture.cpp +++ b/src/burn/test/BurnUnitTest/TestRegistryFixture.cpp | |||
@@ -186,7 +186,7 @@ namespace WixBuildTools | |||
186 | void TestRegistryFixture::SetUp() | 186 | void TestRegistryFixture::SetUp() |
187 | { | 187 | { |
188 | // set mock API's | 188 | // set mock API's |
189 | RegFunctionOverride(TestRegistryFixture_RegCreateKeyExW, TestRegistryFixture_RegOpenKeyExW, TestRegistryFixture_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); | 189 | RegFunctionOverride(TestRegistryFixture_RegCreateKeyExW, TestRegistryFixture_RegOpenKeyExW, TestRegistryFixture_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL, NULL); |
190 | 190 | ||
191 | Registry::CurrentUser->CreateSubKey(TEST_REGISTRY_FIXTURE_HKCU_PATH); | 191 | Registry::CurrentUser->CreateSubKey(TEST_REGISTRY_FIXTURE_HKCU_PATH); |
192 | Registry::CurrentUser->CreateSubKey(TEST_REGISTRY_FIXTURE_HKCU32_PATH); | 192 | Registry::CurrentUser->CreateSubKey(TEST_REGISTRY_FIXTURE_HKCU32_PATH); |
@@ -198,7 +198,7 @@ namespace WixBuildTools | |||
198 | { | 198 | { |
199 | Registry::CurrentUser->DeleteSubKeyTree(this->rootPath, false); | 199 | Registry::CurrentUser->DeleteSubKeyTree(this->rootPath, false); |
200 | 200 | ||
201 | RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); | 201 | RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); |
202 | } | 202 | } |
203 | 203 | ||
204 | String^ TestRegistryFixture::GetDirectHkcuPath(REG_KEY_BITNESS bitness, ... array<String^>^ paths) | 204 | String^ TestRegistryFixture::GetDirectHkcuPath(REG_KEY_BITNESS bitness, ... array<String^>^ paths) |
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h index 76d2d7cb..f5b40291 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h | |||
@@ -99,6 +99,15 @@ typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)( | |||
99 | __in HKEY hKey, | 99 | __in HKEY hKey, |
100 | __in_opt LPCWSTR lpValueName | 100 | __in_opt LPCWSTR lpValueName |
101 | ); | 101 | ); |
102 | typedef LSTATUS(APIENTRY *PFN_REGGETVALUEW)( | ||
103 | __in HKEY hkey, | ||
104 | __in_opt LPCWSTR lpSubKey, | ||
105 | __in_opt LPCWSTR lpValue, | ||
106 | __in DWORD dwFlags, | ||
107 | __out_opt LPDWORD pdwType, | ||
108 | __out_bcount_part_opt(*pcbData, *pcbData) __out_data_source(REGISTRY) PVOID pvData, | ||
109 | __inout_opt LPDWORD pcbData | ||
110 | ); | ||
102 | 111 | ||
103 | /******************************************************************** | 112 | /******************************************************************** |
104 | RegInitialize - initializes regutil | 113 | RegInitialize - initializes regutil |
@@ -126,10 +135,18 @@ void DAPI RegFunctionOverride( | |||
126 | __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, | 135 | __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, |
127 | __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, | 136 | __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, |
128 | __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, | 137 | __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, |
129 | __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW | 138 | __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW, |
139 | __in_opt PFN_REGGETVALUEW pfnRegGetValueW | ||
130 | ); | 140 | ); |
131 | 141 | ||
132 | /******************************************************************** | 142 | /******************************************************************** |
143 | RegFunctionForceFallback - ignore functions only available in newer versions of Windows. | ||
144 | Typically used for unit testing. | ||
145 | |||
146 | *********************************************************************/ | ||
147 | void DAPI RegFunctionForceFallback(); | ||
148 | |||
149 | /******************************************************************** | ||
133 | RegCreate - creates a registry key. | 150 | RegCreate - creates a registry key. |
134 | 151 | ||
135 | *********************************************************************/ | 152 | *********************************************************************/ |
@@ -220,6 +237,20 @@ HRESULT DAPI RegGetType( | |||
220 | ); | 237 | ); |
221 | 238 | ||
222 | /******************************************************************** | 239 | /******************************************************************** |
240 | RegReadBinary - reads a registry key value. | ||
241 | If fExpand is TRUE and the value is REG_EXPAND_SZ then it will be expanded. | ||
242 | NOTE: caller is responsible for freeing *ppbBuffer | ||
243 | *********************************************************************/ | ||
244 | HRESULT DAPI RegReadValue( | ||
245 | __in HKEY hk, | ||
246 | __in_z_opt LPCWSTR wzName, | ||
247 | __in BOOL fExpand, | ||
248 | __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, | ||
249 | __inout SIZE_T* pcbBuffer, | ||
250 | __out DWORD* pdwType | ||
251 | ); | ||
252 | |||
253 | /******************************************************************** | ||
223 | RegReadBinary - reads a registry key binary value. | 254 | RegReadBinary - reads a registry key binary value. |
224 | NOTE: caller is responsible for freeing *ppbBuffer | 255 | NOTE: caller is responsible for freeing *ppbBuffer |
225 | *********************************************************************/ | 256 | *********************************************************************/ |
@@ -241,6 +272,17 @@ HRESULT DAPI RegReadString( | |||
241 | ); | 272 | ); |
242 | 273 | ||
243 | /******************************************************************** | 274 | /******************************************************************** |
275 | RegReadUnexpandedString - reads a registry key value as a string without expanding it. | ||
276 | |||
277 | *********************************************************************/ | ||
278 | HRESULT DAPI RegReadUnexpandedString( | ||
279 | __in HKEY hk, | ||
280 | __in_z_opt LPCWSTR wzName, | ||
281 | __inout BOOL* pfNeedsExpansion, | ||
282 | __deref_out_z LPWSTR* psczValue | ||
283 | ); | ||
284 | |||
285 | /******************************************************************** | ||
244 | RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. | 286 | RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. |
245 | 287 | ||
246 | *********************************************************************/ | 288 | *********************************************************************/ |
diff --git a/src/libs/dutil/WixToolset.DUtil/regutil.cpp b/src/libs/dutil/WixToolset.DUtil/regutil.cpp index 64224d42..584966ed 100644 --- a/src/libs/dutil/WixToolset.DUtil/regutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/regutil.cpp | |||
@@ -9,6 +9,7 @@ | |||
9 | #define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) | 9 | #define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) |
10 | #define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) | 10 | #define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) |
11 | #define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) | 11 | #define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) |
12 | #define RegExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_REGUTIL, x, e, s, __VA_ARGS__) | ||
12 | #define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) | 13 | #define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) |
13 | #define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) | 14 | #define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) |
14 | #define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) | 15 | #define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__) |
@@ -28,10 +29,19 @@ static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW; | |||
28 | static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; | 29 | static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; |
29 | static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; | 30 | static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; |
30 | static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; | 31 | static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; |
32 | static PFN_REGGETVALUEW vpfnRegGetValueW = NULL; | ||
33 | static PFN_REGGETVALUEW vpfnRegGetValueWFromLibrary = NULL; | ||
31 | 34 | ||
32 | static HMODULE vhAdvApi32Dll = NULL; | 35 | static HMODULE vhAdvApi32Dll = NULL; |
33 | static BOOL vfRegInitialized = FALSE; | 36 | static BOOL vfRegInitialized = FALSE; |
34 | 37 | ||
38 | static HRESULT GetRegValue( | ||
39 | __in HKEY hk, | ||
40 | __in_z_opt LPCWSTR wzName, | ||
41 | __deref_out_bcount_opt(*pcbBuffer) BYTE* pbBuffer, | ||
42 | __inout SIZE_T* pcbBuffer, | ||
43 | __out DWORD* pdwType | ||
44 | ); | ||
35 | static HRESULT WriteStringToRegistry( | 45 | static HRESULT WriteStringToRegistry( |
36 | __in HKEY hk, | 46 | __in HKEY hk, |
37 | __in_z_opt LPCWSTR wzName, | 47 | __in_z_opt LPCWSTR wzName, |
@@ -46,14 +56,22 @@ DAPI_(HRESULT) RegInitialize() | |||
46 | hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); | 56 | hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); |
47 | RegExitOnFailure(hr, "Failed to load AdvApi32.dll"); | 57 | RegExitOnFailure(hr, "Failed to load AdvApi32.dll"); |
48 | 58 | ||
49 | // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW | 59 | // Ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW. |
50 | vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); | 60 | vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); |
51 | 61 | ||
52 | if (NULL == vpfnRegDeleteKeyExW) | 62 | // Ignore failures - if this doesn't exist, we'll fall back to RegQueryValueExW. |
63 | vpfnRegGetValueWFromLibrary = reinterpret_cast<PFN_REGGETVALUEW>(::GetProcAddress(vhAdvApi32Dll, "RegGetValueW")); | ||
64 | |||
65 | if (!vpfnRegDeleteKeyExW) | ||
53 | { | 66 | { |
54 | vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; | 67 | vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; |
55 | } | 68 | } |
56 | 69 | ||
70 | if (!vpfnRegGetValueW) | ||
71 | { | ||
72 | vpfnRegGetValueW = vpfnRegGetValueWFromLibrary; | ||
73 | } | ||
74 | |||
57 | vfRegInitialized = TRUE; | 75 | vfRegInitialized = TRUE; |
58 | 76 | ||
59 | LExit: | 77 | LExit: |
@@ -68,7 +86,9 @@ DAPI_(void) RegUninitialize() | |||
68 | ::FreeLibrary(vhAdvApi32Dll); | 86 | ::FreeLibrary(vhAdvApi32Dll); |
69 | vhAdvApi32Dll = NULL; | 87 | vhAdvApi32Dll = NULL; |
70 | vpfnRegDeleteKeyExWFromLibrary = NULL; | 88 | vpfnRegDeleteKeyExWFromLibrary = NULL; |
89 | vpfnRegGetValueWFromLibrary = NULL; | ||
71 | vpfnRegDeleteKeyExW = NULL; | 90 | vpfnRegDeleteKeyExW = NULL; |
91 | vpfnRegGetValueW = NULL; | ||
72 | } | 92 | } |
73 | 93 | ||
74 | vfRegInitialized = FALSE; | 94 | vfRegInitialized = FALSE; |
@@ -84,7 +104,8 @@ DAPI_(void) RegFunctionOverride( | |||
84 | __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, | 104 | __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, |
85 | __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, | 105 | __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, |
86 | __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, | 106 | __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, |
87 | __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW | 107 | __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW, |
108 | __in_opt PFN_REGGETVALUEW pfnRegGetValueW | ||
88 | ) | 109 | ) |
89 | { | 110 | { |
90 | vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; | 111 | vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; |
@@ -96,6 +117,14 @@ DAPI_(void) RegFunctionOverride( | |||
96 | vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; | 117 | vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; |
97 | vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; | 118 | vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; |
98 | vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; | 119 | vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; |
120 | vpfnRegGetValueW = pfnRegGetValueW ? pfnRegGetValueW : vpfnRegGetValueWFromLibrary; | ||
121 | } | ||
122 | |||
123 | |||
124 | DAPI_(void) RegFunctionForceFallback() | ||
125 | { | ||
126 | vpfnRegDeleteKeyExW = NULL; | ||
127 | vpfnRegGetValueW = NULL; | ||
99 | } | 128 | } |
100 | 129 | ||
101 | 130 | ||
@@ -358,54 +387,104 @@ LExit: | |||
358 | return hr; | 387 | return hr; |
359 | } | 388 | } |
360 | 389 | ||
361 | DAPI_(HRESULT) RegReadBinary( | 390 | DAPI_(HRESULT) RegReadValue( |
362 | __in HKEY hk, | 391 | __in HKEY hk, |
363 | __in_z_opt LPCWSTR wzName, | 392 | __in_z_opt LPCWSTR wzName, |
393 | __in BOOL fExpand, | ||
364 | __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, | 394 | __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, |
365 | __out SIZE_T *pcbBuffer | 395 | __inout SIZE_T* pcbBuffer, |
366 | ) | 396 | __out DWORD* pdwType |
397 | ) | ||
367 | { | 398 | { |
368 | HRESULT hr = S_OK; | 399 | HRESULT hr = S_OK; |
369 | LPBYTE pbBuffer = NULL; | 400 | DWORD dwAttempts = 0; |
370 | DWORD er = ERROR_SUCCESS; | 401 | LPWSTR sczExpand = NULL; |
371 | DWORD cb = 0; | ||
372 | DWORD dwType = 0; | ||
373 | |||
374 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb); | ||
375 | RegExitOnWin32Error(er, hr, "Failed to get size of registry value."); | ||
376 | 402 | ||
377 | // Zero-length binary values can exist | 403 | hr = GetRegValue(hk, wzName, *ppbBuffer, pcbBuffer, pdwType); |
378 | if (0 < cb) | 404 | if (E_FILENOTFOUND == hr) |
379 | { | 405 | { |
380 | pbBuffer = static_cast<LPBYTE>(MemAlloc(cb, FALSE)); | 406 | ExitFunction(); |
381 | RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); | 407 | } |
408 | else if (E_MOREDATA != hr) | ||
409 | { | ||
410 | RegExitOnFailure(hr, "Failed to get size of raw registry value."); | ||
382 | 411 | ||
383 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); | 412 | // Zero-length raw values can exist |
384 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | 413 | if (!*ppbBuffer && 0 < *pcbBuffer) |
385 | { | 414 | { |
386 | ExitFunction1(hr = E_FILENOTFOUND); | 415 | hr = E_MOREDATA; |
387 | } | 416 | } |
388 | RegExitOnWin32Error(er, hr, "Failed to read registry value."); | ||
389 | } | 417 | } |
390 | 418 | ||
391 | if (REG_BINARY == dwType) | 419 | while (E_MOREDATA == hr && dwAttempts < 10) |
392 | { | 420 | { |
393 | *ppbBuffer = pbBuffer; | 421 | ++dwAttempts; |
394 | pbBuffer = NULL; | 422 | |
395 | *pcbBuffer = cb; | 423 | if (*ppbBuffer) |
424 | { | ||
425 | *ppbBuffer = static_cast<LPBYTE>(MemReAlloc(*ppbBuffer, *pcbBuffer, FALSE)); | ||
426 | } | ||
427 | else | ||
428 | { | ||
429 | *ppbBuffer = static_cast<LPBYTE>(MemAlloc(*pcbBuffer, FALSE)); | ||
430 | } | ||
431 | RegExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for raw registry value."); | ||
432 | |||
433 | hr = GetRegValue(hk, wzName, *ppbBuffer, pcbBuffer, pdwType); | ||
434 | if (E_FILENOTFOUND == hr) | ||
435 | { | ||
436 | ExitFunction(); | ||
437 | } | ||
438 | else if (E_MOREDATA != hr) | ||
439 | { | ||
440 | RegExitOnFailure(hr, "Failed to read raw registry value."); | ||
441 | } | ||
396 | } | 442 | } |
397 | else | 443 | |
444 | if (fExpand && SUCCEEDED(hr) && REG_EXPAND_SZ == *pdwType) | ||
398 | { | 445 | { |
399 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | 446 | LPWSTR sczValue = reinterpret_cast<LPWSTR>(*ppbBuffer); |
400 | RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); | 447 | hr = PathExpand(&sczExpand, sczValue, PATH_EXPAND_ENVIRONMENT); |
448 | RegExitOnFailure(hr, "Failed to expand registry value: %ls", sczValue); | ||
449 | |||
450 | *ppbBuffer = reinterpret_cast<LPBYTE>(sczExpand); | ||
451 | *pcbBuffer = (lstrlenW(sczExpand) + 1) * sizeof(WCHAR); | ||
452 | sczExpand = NULL; | ||
453 | ReleaseMem(sczValue); | ||
401 | } | 454 | } |
402 | 455 | ||
403 | LExit: | 456 | LExit: |
404 | ReleaseMem(pbBuffer); | 457 | ReleaseStr(sczExpand); |
405 | 458 | ||
406 | return hr; | 459 | return hr; |
407 | } | 460 | } |
408 | 461 | ||
462 | DAPI_(HRESULT) RegReadBinary( | ||
463 | __in HKEY hk, | ||
464 | __in_z_opt LPCWSTR wzName, | ||
465 | __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, | ||
466 | __out SIZE_T* pcbBuffer | ||
467 | ) | ||
468 | { | ||
469 | HRESULT hr = S_OK; | ||
470 | DWORD dwType = 0; | ||
471 | |||
472 | hr = RegReadValue(hk, wzName, FALSE, ppbBuffer, pcbBuffer, &dwType); | ||
473 | if (E_FILENOTFOUND == hr) | ||
474 | { | ||
475 | ExitFunction(); | ||
476 | } | ||
477 | RegExitOnFailure(hr, "Failed to read binary registry value."); | ||
478 | |||
479 | if (REG_BINARY != dwType) | ||
480 | { | ||
481 | RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading binary registry value due to unexpected data type: %u", dwType); | ||
482 | } | ||
483 | |||
484 | LExit: | ||
485 | return hr; | ||
486 | } | ||
487 | |||
409 | 488 | ||
410 | DAPI_(HRESULT) RegReadString( | 489 | DAPI_(HRESULT) RegReadString( |
411 | __in HKEY hk, | 490 | __in HKEY hk, |
@@ -414,65 +493,64 @@ DAPI_(HRESULT) RegReadString( | |||
414 | ) | 493 | ) |
415 | { | 494 | { |
416 | HRESULT hr = S_OK; | 495 | HRESULT hr = S_OK; |
417 | DWORD er = ERROR_SUCCESS; | ||
418 | SIZE_T cbValue = 0; | 496 | SIZE_T cbValue = 0; |
419 | DWORD cch = 0; | ||
420 | DWORD cb = 0; | ||
421 | DWORD dwType = 0; | 497 | DWORD dwType = 0; |
422 | LPWSTR sczExpand = NULL; | ||
423 | 498 | ||
424 | if (psczValue && *psczValue) | 499 | if (psczValue && *psczValue) |
425 | { | 500 | { |
426 | hr = StrMaxLength(*psczValue, &cbValue); | 501 | hr = MemSizeChecked(*psczValue, &cbValue); |
427 | RegExitOnFailure(hr, "Failed to determine length of string."); | 502 | RegExitOnFailure(hr, "Failed to get size of input buffer."); |
428 | |||
429 | cch = (DWORD)min(DWORD_MAX, cbValue); | ||
430 | } | 503 | } |
431 | 504 | ||
432 | if (2 > cch) | 505 | hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(psczValue), &cbValue, &dwType); |
506 | if (E_FILENOTFOUND == hr) | ||
433 | { | 507 | { |
434 | cch = 2; | 508 | ExitFunction(); |
435 | |||
436 | hr = StrAlloc(psczValue, cch); | ||
437 | RegExitOnFailure(hr, "Failed to allocate string to minimum size."); | ||
438 | } | 509 | } |
510 | RegExitOnFailure(hr, "Failed to read string registry value."); | ||
439 | 511 | ||
440 | cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator. | 512 | if (REG_EXPAND_SZ != dwType && REG_SZ != dwType) |
441 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb); | ||
442 | if (ERROR_MORE_DATA == er) | ||
443 | { | 513 | { |
444 | cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator | 514 | RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading string registry value due to unexpected data type: %u", dwType); |
445 | hr = StrAlloc(psczValue, cch); | ||
446 | RegExitOnFailure(hr, "Failed to allocate string bigger for registry value."); | ||
447 | |||
448 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb); | ||
449 | } | 515 | } |
450 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | 516 | |
517 | LExit: | ||
518 | return hr; | ||
519 | } | ||
520 | |||
521 | |||
522 | DAPI_(HRESULT) RegReadUnexpandedString( | ||
523 | __in HKEY hk, | ||
524 | __in_z_opt LPCWSTR wzName, | ||
525 | __inout BOOL* pfNeedsExpansion, | ||
526 | __deref_out_z LPWSTR* psczValue | ||
527 | ) | ||
528 | { | ||
529 | HRESULT hr = S_OK; | ||
530 | SIZE_T cbValue = 0; | ||
531 | DWORD dwType = 0; | ||
532 | LPWSTR sczExpand = NULL; | ||
533 | |||
534 | if (psczValue && *psczValue) | ||
451 | { | 535 | { |
452 | ExitFunction1(hr = E_FILENOTFOUND); | 536 | hr = MemSizeChecked(*psczValue, &cbValue); |
537 | RegExitOnFailure(hr, "Failed to get size of input buffer."); | ||
453 | } | 538 | } |
454 | RegExitOnWin32Error(er, hr, "Failed to read registry key."); | ||
455 | 539 | ||
456 | if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) | 540 | hr = RegReadValue(hk, wzName, FALSE, reinterpret_cast<LPBYTE*>(psczValue), &cbValue, &dwType); |
541 | if (E_FILENOTFOUND == hr) | ||
457 | { | 542 | { |
458 | // Always ensure the registry value is null terminated. | 543 | ExitFunction(); |
459 | (*psczValue)[cch - 1] = L'\0'; | ||
460 | |||
461 | if (REG_EXPAND_SZ == dwType) | ||
462 | { | ||
463 | hr = StrAllocString(&sczExpand, *psczValue, 0); | ||
464 | RegExitOnFailure(hr, "Failed to copy registry value to expand."); | ||
465 | |||
466 | hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT); | ||
467 | RegExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); | ||
468 | } | ||
469 | } | 544 | } |
470 | else | 545 | RegExitOnFailure(hr, "Failed to read expand string registry value."); |
546 | |||
547 | if (REG_EXPAND_SZ != dwType && REG_SZ != dwType) | ||
471 | { | 548 | { |
472 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | 549 | RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading expand string registry value due to unexpected data type: %u", dwType); |
473 | RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); | ||
474 | } | 550 | } |
475 | 551 | ||
552 | *pfNeedsExpansion = REG_EXPAND_SZ == dwType; | ||
553 | |||
476 | LExit: | 554 | LExit: |
477 | ReleaseStr(sczExpand); | 555 | ReleaseStr(sczExpand); |
478 | 556 | ||
@@ -488,67 +566,57 @@ DAPI_(HRESULT) RegReadStringArray( | |||
488 | ) | 566 | ) |
489 | { | 567 | { |
490 | HRESULT hr = S_OK; | 568 | HRESULT hr = S_OK; |
491 | DWORD er = ERROR_SUCCESS; | ||
492 | DWORD dwNullCharacters = 0; | 569 | DWORD dwNullCharacters = 0; |
493 | DWORD dwType = 0; | 570 | DWORD dwType = 0; |
494 | DWORD cb = 0; | 571 | SIZE_T cb = 0; |
495 | DWORD cch = 0; | 572 | SIZE_T cch = 0; |
496 | LPCWSTR wzSource = NULL; | 573 | LPCWSTR wzSource = NULL; |
497 | LPWSTR sczValue = NULL; | 574 | LPWSTR sczValue = NULL; |
498 | 575 | ||
499 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb); | 576 | hr = RegReadValue(hk, wzName, FALSE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType); |
500 | if (0 < cb) | 577 | if (E_FILENOTFOUND == hr) |
501 | { | ||
502 | cch = cb / sizeof(WCHAR); | ||
503 | hr = StrAlloc(&sczValue, cch); | ||
504 | RegExitOnFailure(hr, "Failed to allocate string for registry value."); | ||
505 | |||
506 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb); | ||
507 | } | ||
508 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
509 | { | 578 | { |
510 | ExitFunction1(hr = E_FILENOTFOUND); | 579 | ExitFunction(); |
511 | } | 580 | } |
512 | RegExitOnWin32Error(er, hr, "Failed to read registry key."); | 581 | RegExitOnFailure(hr, "Failed to read string array registry value."); |
513 | 582 | ||
514 | if (cb / sizeof(WCHAR) != cch) | 583 | if (REG_MULTI_SZ != dwType) |
515 | { | 584 | { |
516 | hr = E_UNEXPECTED; | 585 | RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Tried to read string array, but registry value %ls is of an incorrect type", wzName); |
517 | RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); | ||
518 | } | 586 | } |
519 | 587 | ||
520 | if (REG_MULTI_SZ != dwType) | 588 | cch = cb / sizeof(WCHAR); |
589 | |||
590 | for (DWORD i = 0; i < cch; ++i) | ||
521 | { | 591 | { |
522 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | 592 | if (L'\0' == sczValue[i]) |
523 | RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); | 593 | { |
594 | ++dwNullCharacters; | ||
595 | } | ||
524 | } | 596 | } |
525 | 597 | ||
526 | // Value exists, but is empty, so no strings to return. | 598 | // Value exists, but is empty, so no strings to return. |
527 | if (2 > cch) | 599 | if (!cb || 1 == dwNullCharacters && 1 == cch || 2 == dwNullCharacters && 2 == cch) |
528 | { | 600 | { |
529 | *prgsczStrings = NULL; | 601 | *prgsczStrings = NULL; |
530 | *pcStrings = 0; | 602 | *pcStrings = 0; |
531 | ExitFunction1(hr = S_OK); | 603 | ExitFunction1(hr = S_OK); |
532 | } | 604 | } |
533 | 605 | ||
534 | // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too. | 606 | if (sczValue[cch - 1] != L'\0') |
535 | if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2]) | ||
536 | { | 607 | { |
537 | hr = E_INVALIDARG; | 608 | // Count the terminating null character that RegReadValue added past the end. |
538 | RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); | 609 | ++dwNullCharacters; |
610 | Assert(!sczValue[cch]); | ||
539 | } | 611 | } |
540 | 612 | else if (cch > 1 && sczValue[cch - 2] == L'\0') | |
541 | cch = cb / sizeof(WCHAR); | ||
542 | for (DWORD i = 0; i < cch; ++i) | ||
543 | { | 613 | { |
544 | if (L'\0' == sczValue[i]) | 614 | // Don't count the extra 1 at the end of the properly double-null terminated string. |
545 | { | 615 | --dwNullCharacters; |
546 | ++dwNullCharacters; | ||
547 | } | ||
548 | } | 616 | } |
549 | 617 | ||
550 | // There's one string for every null character encountered (except the extra 1 at the end of the string) | 618 | // There's one string for every null character encountered. |
551 | *pcStrings = dwNullCharacters - 1; | 619 | *pcStrings = dwNullCharacters; |
552 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); | 620 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); |
553 | RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); | 621 | RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); |
554 | 622 | ||
@@ -557,11 +625,13 @@ DAPI_(HRESULT) RegReadStringArray( | |||
557 | wzSource = sczValue; | 625 | wzSource = sczValue; |
558 | for (DWORD i = 0; i < *pcStrings; ++i) | 626 | for (DWORD i = 0; i < *pcStrings; ++i) |
559 | { | 627 | { |
560 | hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); | 628 | cch = lstrlenW(wzSource); |
629 | |||
630 | hr = StrAllocString(&(*prgsczStrings)[i], wzSource, cch); | ||
561 | RegExitOnFailure(hr, "Failed to allocate copy of string"); | 631 | RegExitOnFailure(hr, "Failed to allocate copy of string"); |
562 | 632 | ||
563 | // Skip past this string | 633 | // Skip past this string |
564 | wzSource += lstrlenW(wzSource) + 1; | 634 | wzSource += cch + 1; |
565 | } | 635 | } |
566 | #pragma prefast(pop) | 636 | #pragma prefast(pop) |
567 | 637 | ||
@@ -579,38 +649,36 @@ DAPI_(HRESULT) RegReadVersion( | |||
579 | ) | 649 | ) |
580 | { | 650 | { |
581 | HRESULT hr = S_OK; | 651 | HRESULT hr = S_OK; |
582 | DWORD er = ERROR_SUCCESS; | ||
583 | DWORD dwType = 0; | 652 | DWORD dwType = 0; |
584 | DWORD cb = 0; | 653 | SIZE_T cb = 0; |
585 | LPWSTR sczVersion = NULL; | 654 | LPWSTR sczValue = NULL; |
586 | 655 | ||
587 | cb = sizeof(DWORD64); | 656 | hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType); |
588 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*pdw64Version), &cb); | 657 | if (E_FILENOTFOUND == hr) |
589 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
590 | { | 658 | { |
591 | ExitFunction1(hr = E_FILENOTFOUND); | 659 | ExitFunction(); |
592 | } | 660 | } |
661 | RegExitOnFailure(hr, "Failed to read version registry value."); | ||
593 | 662 | ||
594 | if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) | 663 | if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) |
595 | { | 664 | { |
596 | hr = RegReadString(hk, wzName, &sczVersion); | 665 | hr = FileVersionFromStringEx(sczValue, 0, pdw64Version); |
597 | RegExitOnFailure(hr, "Failed to read registry version as string."); | ||
598 | |||
599 | hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version); | ||
600 | RegExitOnFailure(hr, "Failed to convert registry string to version."); | 666 | RegExitOnFailure(hr, "Failed to convert registry string to version."); |
601 | } | 667 | } |
602 | else if (REG_QWORD == dwType) | 668 | else if (REG_QWORD == dwType) |
603 | { | 669 | { |
604 | RegExitOnWin32Error(er, hr, "Failed to read registry key."); | 670 | if (memcpy_s(pdw64Version, sizeof(DWORD64), sczValue, cb)) |
671 | { | ||
672 | RegExitWithRootFailure(hr, E_INVALIDARG, "Failed to copy QWORD version value."); | ||
673 | } | ||
605 | } | 674 | } |
606 | else // unexpected data type | 675 | else // unexpected data type |
607 | { | 676 | { |
608 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | 677 | RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading version registry value due to unexpected data type: %u", dwType); |
609 | RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); | ||
610 | } | 678 | } |
611 | 679 | ||
612 | LExit: | 680 | LExit: |
613 | ReleaseStr(sczVersion); | 681 | ReleaseStr(sczValue); |
614 | 682 | ||
615 | return hr; | 683 | return hr; |
616 | } | 684 | } |
@@ -623,37 +691,38 @@ DAPI_(HRESULT) RegReadWixVersion( | |||
623 | ) | 691 | ) |
624 | { | 692 | { |
625 | HRESULT hr = S_OK; | 693 | HRESULT hr = S_OK; |
626 | DWORD er = ERROR_SUCCESS; | ||
627 | DWORD dwType = 0; | 694 | DWORD dwType = 0; |
628 | DWORD cb = 0; | 695 | SIZE_T cb = 0; |
629 | DWORD64 dw64Version = 0; | 696 | DWORD64 dw64Version = 0; |
630 | LPWSTR sczVersion = NULL; | 697 | LPWSTR sczValue = NULL; |
631 | VERUTIL_VERSION* pVersion = NULL; | 698 | VERUTIL_VERSION* pVersion = NULL; |
632 | 699 | ||
633 | cb = sizeof(DWORD64); | 700 | hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType); |
634 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(&dw64Version), &cb); | 701 | if (E_FILENOTFOUND == hr) |
635 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
636 | { | 702 | { |
637 | ExitFunction1(hr = E_FILENOTFOUND); | 703 | ExitFunction(); |
638 | } | 704 | } |
705 | RegExitOnFailure(hr, "Failed to read wix version registry value."); | ||
639 | 706 | ||
640 | if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) | 707 | if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) |
641 | { | 708 | { |
642 | hr = RegReadString(hk, wzName, &sczVersion); | 709 | hr = VerParseVersion(sczValue, 0, FALSE, &pVersion); |
643 | RegExitOnFailure(hr, "Failed to read registry version as string."); | 710 | RegExitOnFailure(hr, "Failed to convert registry string to wix version."); |
644 | |||
645 | hr = VerParseVersion(sczVersion, 0, FALSE, &pVersion); | ||
646 | RegExitOnFailure(hr, "Failed to convert registry string to version."); | ||
647 | } | 711 | } |
648 | else if (REG_QWORD == dwType) | 712 | else if (REG_QWORD == dwType) |
649 | { | 713 | { |
714 | if (memcpy_s(&dw64Version, sizeof(DWORD64), sczValue, cb)) | ||
715 | { | ||
716 | RegExitWithRootFailure(hr, E_INVALIDARG, "Failed to copy QWORD wix version value."); | ||
717 | } | ||
718 | |||
650 | hr = VerVersionFromQword(dw64Version, &pVersion); | 719 | hr = VerVersionFromQword(dw64Version, &pVersion); |
651 | RegExitOnFailure(hr, "Failed to convert registry string to version."); | 720 | RegExitOnFailure(hr, "Failed to convert registry string to wix version."); |
652 | } | 721 | } |
653 | else // unexpected data type | 722 | else // unexpected data type |
654 | { | 723 | { |
655 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | 724 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); |
656 | RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); | 725 | RegExitOnRootFailure(hr, "Error reading wix version registry value due to unexpected data type: %u", dwType); |
657 | } | 726 | } |
658 | 727 | ||
659 | *ppVersion = pVersion; | 728 | *ppVersion = pVersion; |
@@ -661,7 +730,7 @@ DAPI_(HRESULT) RegReadWixVersion( | |||
661 | 730 | ||
662 | LExit: | 731 | LExit: |
663 | ReleaseVerutilVersion(pVersion); | 732 | ReleaseVerutilVersion(pVersion); |
664 | ReleaseStr(sczVersion); | 733 | ReleaseStr(sczValue); |
665 | 734 | ||
666 | return hr; | 735 | return hr; |
667 | } | 736 | } |
@@ -673,20 +742,18 @@ DAPI_(HRESULT) RegReadNone( | |||
673 | ) | 742 | ) |
674 | { | 743 | { |
675 | HRESULT hr = S_OK; | 744 | HRESULT hr = S_OK; |
676 | DWORD er = ERROR_SUCCESS; | ||
677 | DWORD dwType = 0; | 745 | DWORD dwType = 0; |
678 | 746 | ||
679 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, NULL); | 747 | hr = RegGetType(hk, wzName, &dwType); |
680 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | 748 | if (E_FILENOTFOUND == hr) |
681 | { | 749 | { |
682 | ExitFunction1(hr = E_FILENOTFOUND); | 750 | ExitFunction(); |
683 | } | 751 | } |
684 | RegExitOnWin32Error(er, hr, "Failed to query registry key value."); | 752 | RegExitOnFailure(hr, "Error reading none registry value type."); |
685 | 753 | ||
686 | if (REG_NONE != dwType) | 754 | if (REG_NONE != dwType) |
687 | { | 755 | { |
688 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | 756 | RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading none registry value due to unexpected data type: %u", dwType); |
689 | RegExitOnRootFailure(hr, "Error reading none registry value due to unexpected data type: %u", dwType); | ||
690 | } | 757 | } |
691 | 758 | ||
692 | LExit: | 759 | LExit: |
@@ -829,12 +896,9 @@ DAPI_(HRESULT) RegWriteStringArray( | |||
829 | DWORD dwTotalStringSize = 0; | 896 | DWORD dwTotalStringSize = 0; |
830 | DWORD cbTotalStringSize = 0; | 897 | DWORD cbTotalStringSize = 0; |
831 | DWORD dwTemp = 0; | 898 | DWORD dwTemp = 0; |
899 | DWORD dwRemainingStringSize = 0; | ||
832 | 900 | ||
833 | if (0 == cValues) | 901 | if (cValues) |
834 | { | ||
835 | wzWriteValue = L"\0"; | ||
836 | } | ||
837 | else | ||
838 | { | 902 | { |
839 | // Add space for the null terminator | 903 | // Add space for the null terminator |
840 | dwTotalStringSize = 1; | 904 | dwTotalStringSize = 1; |
@@ -850,21 +914,22 @@ DAPI_(HRESULT) RegWriteStringArray( | |||
850 | RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); | 914 | RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); |
851 | 915 | ||
852 | wzCopyDestination = sczWriteValue; | 916 | wzCopyDestination = sczWriteValue; |
853 | dwTemp = dwTotalStringSize; | 917 | dwRemainingStringSize = dwTotalStringSize; |
854 | for (DWORD i = 0; i < cValues; ++i) | 918 | for (DWORD i = 0; i < cValues; ++i) |
855 | { | 919 | { |
856 | hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); | 920 | hr = ::StringCchCopyW(wzCopyDestination, dwRemainingStringSize, rgwzValues[i]); |
857 | RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); | 921 | RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); |
858 | 922 | ||
859 | dwTemp -= lstrlenW(rgwzValues[i]) + 1; | 923 | dwTemp = lstrlenW(rgwzValues[i]) + 1; |
860 | wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; | 924 | dwRemainingStringSize -= dwTemp; |
925 | wzCopyDestination += dwTemp; | ||
861 | } | 926 | } |
862 | 927 | ||
863 | wzWriteValue = sczWriteValue; | 928 | wzWriteValue = sczWriteValue; |
864 | } | ||
865 | 929 | ||
866 | hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); | 930 | hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); |
867 | RegExitOnFailure(hr, "Failed to get total string size in bytes"); | 931 | RegExitOnFailure(hr, "Failed to get total string size in bytes"); |
932 | } | ||
868 | 933 | ||
869 | er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize); | 934 | er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize); |
870 | RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); | 935 | RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); |
@@ -1000,6 +1065,48 @@ DAPI_(REGSAM) RegTranslateKeyBitness( | |||
1000 | } | 1065 | } |
1001 | } | 1066 | } |
1002 | 1067 | ||
1068 | static HRESULT GetRegValue( | ||
1069 | __in HKEY hk, | ||
1070 | __in_z_opt LPCWSTR wzName, | ||
1071 | __deref_out_bcount_opt(*pcbBuffer) BYTE* pbBuffer, | ||
1072 | __inout SIZE_T* pcbBuffer, | ||
1073 | __out DWORD* pdwType | ||
1074 | ) | ||
1075 | { | ||
1076 | HRESULT hr = S_OK; | ||
1077 | DWORD cb = (DWORD)min(DWORD_MAX, *pcbBuffer); | ||
1078 | |||
1079 | if (vpfnRegGetValueW) | ||
1080 | { | ||
1081 | hr = HRESULT_FROM_WIN32(vpfnRegGetValueW(hk, NULL, wzName, RRF_RT_ANY | RRF_NOEXPAND, pdwType, pbBuffer, &cb)); | ||
1082 | } | ||
1083 | else | ||
1084 | { | ||
1085 | hr = HRESULT_FROM_WIN32(vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, pbBuffer, &cb)); | ||
1086 | |||
1087 | if (REG_SZ == *pdwType || REG_EXPAND_SZ == *pdwType || REG_MULTI_SZ == *pdwType) | ||
1088 | { | ||
1089 | if (E_MOREDATA == hr || S_OK == hr && (cb + sizeof(WCHAR)) > *pcbBuffer) | ||
1090 | { | ||
1091 | // Make sure there's room for a null terminator at the end. | ||
1092 | HRESULT hrAdd = ::DWordAdd(cb, sizeof(WCHAR), &cb); | ||
1093 | |||
1094 | hr = FAILED(hrAdd) ? hrAdd : (pbBuffer ? E_MOREDATA : S_OK); | ||
1095 | } | ||
1096 | else if (S_OK == hr && pbBuffer) | ||
1097 | { | ||
1098 | // Always ensure the registry value is null terminated. | ||
1099 | WCHAR* pch = reinterpret_cast<WCHAR*>(pbBuffer + cb); | ||
1100 | *pch = L'\0'; | ||
1101 | } | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | *pcbBuffer = cb; | ||
1106 | |||
1107 | return hr; | ||
1108 | } | ||
1109 | |||
1003 | static HRESULT WriteStringToRegistry( | 1110 | static HRESULT WriteStringToRegistry( |
1004 | __in HKEY hk, | 1111 | __in HKEY hk, |
1005 | __in_z_opt LPCWSTR wzName, | 1112 | __in_z_opt LPCWSTR wzName, |
@@ -1013,9 +1120,12 @@ static HRESULT WriteStringToRegistry( | |||
1013 | 1120 | ||
1014 | if (wzValue) | 1121 | if (wzValue) |
1015 | { | 1122 | { |
1016 | hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue); | 1123 | hr = ::StringCbLengthW(wzValue, DWORD_MAX, &cbValue); |
1017 | RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); | 1124 | RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); |
1018 | 1125 | ||
1126 | // Need to include the null terminator. | ||
1127 | cbValue += sizeof(WCHAR); | ||
1128 | |||
1019 | er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast<const BYTE *>(wzValue), static_cast<DWORD>(cbValue)); | 1129 | er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast<const BYTE *>(wzValue), static_cast<DWORD>(cbValue)); |
1020 | RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); | 1130 | RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); |
1021 | } | 1131 | } |
diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj index c37bdad1..5b40eaf1 100644 --- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj | |||
@@ -61,6 +61,7 @@ | |||
61 | <!-- Warnings from referencing netstandard dlls --> | 61 | <!-- Warnings from referencing netstandard dlls --> |
62 | <DisableSpecificWarnings>4564;4691</DisableSpecificWarnings> | 62 | <DisableSpecificWarnings>4564;4691</DisableSpecificWarnings> |
63 | </ClCompile> | 63 | </ClCompile> |
64 | <ClCompile Include="RegUtilTest.cpp" /> | ||
64 | <ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " /> | 65 | <ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " /> |
65 | <ClCompile Include="StrUtilTest.cpp" /> | 66 | <ClCompile Include="StrUtilTest.cpp" /> |
66 | <ClCompile Include="UriUtilTest.cpp" /> | 67 | <ClCompile Include="UriUtilTest.cpp" /> |
diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters index 4df7af89..fde49348 100644 --- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters | |||
@@ -54,6 +54,9 @@ | |||
54 | <ClCompile Include="precomp.cpp"> | 54 | <ClCompile Include="precomp.cpp"> |
55 | <Filter>Source Files</Filter> | 55 | <Filter>Source Files</Filter> |
56 | </ClCompile> | 56 | </ClCompile> |
57 | <ClCompile Include="RegUtilTest.cpp"> | ||
58 | <Filter>Source Files</Filter> | ||
59 | </ClCompile> | ||
57 | <ClCompile Include="StrUtilTest.cpp"> | 60 | <ClCompile Include="StrUtilTest.cpp"> |
58 | <Filter>Source Files</Filter> | 61 | <Filter>Source Files</Filter> |
59 | </ClCompile> | 62 | </ClCompile> |
diff --git a/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp index 273f2eb6..09d0aeab 100644 --- a/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp +++ b/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp | |||
@@ -481,6 +481,8 @@ namespace DutilTests | |||
481 | ReleaseMem(pResults->rgDirectories); | 481 | ReleaseMem(pResults->rgDirectories); |
482 | ReleaseMem(pResults->rgRegKeys); | 482 | ReleaseMem(pResults->rgRegKeys); |
483 | ReleaseMem(pResults); | 483 | ReleaseMem(pResults); |
484 | |||
485 | RegUninitialize(); | ||
484 | } | 486 | } |
485 | } | 487 | } |
486 | }; | 488 | }; |
diff --git a/src/libs/dutil/test/DUtilUnitTest/RegUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/RegUtilTest.cpp new file mode 100644 index 00000000..575e3238 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/RegUtilTest.cpp | |||
@@ -0,0 +1,679 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | using namespace System; | ||
6 | using namespace System::Collections::Generic; | ||
7 | using namespace System::Runtime::InteropServices; | ||
8 | using namespace Xunit; | ||
9 | using namespace WixBuildTools::TestSupport; | ||
10 | |||
11 | LPCWSTR wzBaseRegKey = L"Software\\RegUtilTest\\"; | ||
12 | LPWSTR rgwzMultiValue[2] = { L"First", L"Second" }; | ||
13 | LPWSTR rgwzEmptyMultiValue[2] = { L"", L"" }; | ||
14 | |||
15 | HKEY hkBase; | ||
16 | |||
17 | namespace DutilTests | ||
18 | { | ||
19 | public ref class RegUtil : IDisposable | ||
20 | { | ||
21 | private: | ||
22 | |||
23 | void CreateBaseKey() | ||
24 | { | ||
25 | HRESULT hr = RegCreate(HKEY_CURRENT_USER, wzBaseRegKey, KEY_ALL_ACCESS, &hkBase); | ||
26 | NativeAssert::Succeeded(hr, "Failed to create base key."); | ||
27 | } | ||
28 | |||
29 | public: | ||
30 | RegUtil() | ||
31 | { | ||
32 | HRESULT hr = RegInitialize(); | ||
33 | NativeAssert::Succeeded(hr, "RegInitialize failed."); | ||
34 | } | ||
35 | |||
36 | ~RegUtil() | ||
37 | { | ||
38 | RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); | ||
39 | |||
40 | if (hkBase) | ||
41 | { | ||
42 | RegDelete(hkBase, NULL, REG_KEY_DEFAULT, TRUE); | ||
43 | } | ||
44 | |||
45 | ReleaseRegKey(hkBase); | ||
46 | |||
47 | RegUninitialize(); | ||
48 | } | ||
49 | |||
50 | [Fact] | ||
51 | void RegUtilStringValueTest() | ||
52 | { | ||
53 | this->StringValueTest(); | ||
54 | } | ||
55 | |||
56 | [Fact] | ||
57 | void RegUtilStringValueFallbackTest() | ||
58 | { | ||
59 | RegFunctionForceFallback(); | ||
60 | this->StringValueTest(); | ||
61 | } | ||
62 | |||
63 | void StringValueTest() | ||
64 | { | ||
65 | HRESULT hr = S_OK; | ||
66 | LPWSTR sczValue = NULL; | ||
67 | LPCWSTR wzValue = L"Value"; | ||
68 | |||
69 | try | ||
70 | { | ||
71 | this->CreateBaseKey(); | ||
72 | |||
73 | hr = RegWriteString(hkBase, L"String", wzValue); | ||
74 | NativeAssert::Succeeded(hr, "Failed to write string value."); | ||
75 | |||
76 | hr = RegReadString(hkBase, L"String", &sczValue); | ||
77 | NativeAssert::Succeeded(hr, "Failed to read string value."); | ||
78 | NativeAssert::StringEqual(wzValue, sczValue); | ||
79 | |||
80 | ReleaseNullStr(sczValue); | ||
81 | hr = StrAllocString(&sczValue, L"e", 0); | ||
82 | NativeAssert::Succeeded(hr, "Failed to reallocate string value."); | ||
83 | |||
84 | hr = RegReadString(hkBase, L"String", &sczValue); | ||
85 | NativeAssert::Succeeded(hr, "Failed to read string value."); | ||
86 | NativeAssert::StringEqual(wzValue, sczValue); | ||
87 | } | ||
88 | finally | ||
89 | { | ||
90 | ReleaseStr(sczValue); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | [Fact] | ||
95 | void RegUtilPartialStringValueTest() | ||
96 | { | ||
97 | this->PartialStringValueTest(); | ||
98 | } | ||
99 | |||
100 | [Fact] | ||
101 | void RegUtilPartialStringValueFallbackTest() | ||
102 | { | ||
103 | RegFunctionForceFallback(); | ||
104 | this->PartialStringValueTest(); | ||
105 | } | ||
106 | |||
107 | void PartialStringValueTest() | ||
108 | { | ||
109 | HRESULT hr = S_OK; | ||
110 | LPWSTR sczValue = NULL; | ||
111 | LPCWSTR wzValue = L"Value"; | ||
112 | BOOL fNeedsExpansion = FALSE; | ||
113 | |||
114 | try | ||
115 | { | ||
116 | this->CreateBaseKey(); | ||
117 | |||
118 | // Use API directly to write non-null terminated string. | ||
119 | hr = HRESULT_FROM_WIN32(::RegSetValueExW(hkBase, L"PartialString", 0, REG_SZ, reinterpret_cast<const BYTE*>(wzValue), 4 * sizeof(WCHAR))); | ||
120 | NativeAssert::Succeeded(hr, "Failed to write partial string value."); | ||
121 | |||
122 | hr = RegReadString(hkBase, L"PartialString", &sczValue); | ||
123 | NativeAssert::Succeeded(hr, "Failed to read partial string value."); | ||
124 | NativeAssert::StringEqual(L"Valu", sczValue); | ||
125 | |||
126 | hr = RegReadUnexpandedString(hkBase, L"PartialString", &fNeedsExpansion, &sczValue); | ||
127 | NativeAssert::Succeeded(hr, "Failed to read partial unexpanded string value."); | ||
128 | NativeAssert::StringEqual(L"Valu", sczValue); | ||
129 | Assert::False(fNeedsExpansion); | ||
130 | } | ||
131 | finally | ||
132 | { | ||
133 | ReleaseStr(sczValue); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | [Fact] | ||
138 | void RegUtilEmptyStringValueTest() | ||
139 | { | ||
140 | this->EmptyStringValueTest(); | ||
141 | } | ||
142 | |||
143 | [Fact] | ||
144 | void RegUtilEmptyStringValueFallbackTest() | ||
145 | { | ||
146 | RegFunctionForceFallback(); | ||
147 | this->EmptyStringValueTest(); | ||
148 | } | ||
149 | |||
150 | void EmptyStringValueTest() | ||
151 | { | ||
152 | HRESULT hr = S_OK; | ||
153 | LPWSTR sczValue = NULL; | ||
154 | |||
155 | try | ||
156 | { | ||
157 | this->CreateBaseKey(); | ||
158 | |||
159 | // Use API directly to write non-null terminated string. | ||
160 | hr = HRESULT_FROM_WIN32(::RegSetValueExW(hkBase, L"EmptyString", 0, REG_SZ, reinterpret_cast<const BYTE*>(L""), 0)); | ||
161 | NativeAssert::Succeeded(hr, "Failed to write partial string value."); | ||
162 | |||
163 | hr = RegReadString(hkBase, L"EmptyString", &sczValue); | ||
164 | NativeAssert::Succeeded(hr, "Failed to read partial string value."); | ||
165 | NativeAssert::StringEqual(L"", sczValue); | ||
166 | } | ||
167 | finally | ||
168 | { | ||
169 | ReleaseStr(sczValue); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | [Fact] | ||
174 | void RegUtilExpandStringValueTest() | ||
175 | { | ||
176 | this->ExpandStringValueTest(); | ||
177 | } | ||
178 | |||
179 | [Fact] | ||
180 | void RegUtilExpandStringValueFallbackTest() | ||
181 | { | ||
182 | RegFunctionForceFallback(); | ||
183 | this->ExpandStringValueTest(); | ||
184 | } | ||
185 | |||
186 | void ExpandStringValueTest() | ||
187 | { | ||
188 | HRESULT hr = S_OK; | ||
189 | LPWSTR sczValue = NULL; | ||
190 | LPCWSTR wzValue = L"Value_%USERNAME%"; | ||
191 | String^ expandedValue = Environment::ExpandEnvironmentVariables(gcnew String(wzValue)); | ||
192 | |||
193 | try | ||
194 | { | ||
195 | this->CreateBaseKey(); | ||
196 | |||
197 | hr = RegWriteExpandString(hkBase, L"ExpandString", wzValue); | ||
198 | NativeAssert::Succeeded(hr, "Failed to write expand string value."); | ||
199 | |||
200 | hr = RegReadString(hkBase, L"ExpandString", &sczValue); | ||
201 | NativeAssert::Succeeded(hr, "Failed to read expand string value."); | ||
202 | WixAssert::StringEqual(expandedValue, gcnew String(sczValue), false); | ||
203 | } | ||
204 | finally | ||
205 | { | ||
206 | ReleaseStr(sczValue); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | [Fact] | ||
211 | void RegUtilNotExpandStringValueTest() | ||
212 | { | ||
213 | this->NotExpandStringValueTest(); | ||
214 | } | ||
215 | |||
216 | [Fact] | ||
217 | void RegUtilNotExpandStringValueFallbackTest() | ||
218 | { | ||
219 | RegFunctionForceFallback(); | ||
220 | this->NotExpandStringValueTest(); | ||
221 | } | ||
222 | |||
223 | void NotExpandStringValueTest() | ||
224 | { | ||
225 | HRESULT hr = S_OK; | ||
226 | LPWSTR sczValue = NULL; | ||
227 | BOOL fNeedsExpansion = FALSE; | ||
228 | LPCWSTR wzValue = L"Value_%USERNAME%"; | ||
229 | |||
230 | try | ||
231 | { | ||
232 | this->CreateBaseKey(); | ||
233 | |||
234 | hr = RegWriteExpandString(hkBase, L"NotExpandString", wzValue); | ||
235 | NativeAssert::Succeeded(hr, "Failed to write expand string value."); | ||
236 | |||
237 | hr = RegReadUnexpandedString(hkBase, L"NotExpandString", &fNeedsExpansion, &sczValue); | ||
238 | NativeAssert::Succeeded(hr, "Failed to read expand string value."); | ||
239 | NativeAssert::StringEqual(wzValue, sczValue); | ||
240 | Assert::True(fNeedsExpansion); | ||
241 | } | ||
242 | finally | ||
243 | { | ||
244 | ReleaseStr(sczValue); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | [Fact] | ||
249 | void RegUtilMultiStringValueTest() | ||
250 | { | ||
251 | this->MultiStringValueTest(); | ||
252 | } | ||
253 | |||
254 | [Fact] | ||
255 | void RegUtilMultiStringValueFallbackTest() | ||
256 | { | ||
257 | RegFunctionForceFallback(); | ||
258 | this->MultiStringValueTest(); | ||
259 | } | ||
260 | |||
261 | void MultiStringValueTest() | ||
262 | { | ||
263 | HRESULT hr = S_OK; | ||
264 | LPWSTR* rgsczStrings = NULL; | ||
265 | DWORD cStrings = 0; | ||
266 | |||
267 | try | ||
268 | { | ||
269 | this->CreateBaseKey(); | ||
270 | |||
271 | hr = RegWriteStringArray(hkBase, L"MultiString", rgwzMultiValue, 2); | ||
272 | NativeAssert::Succeeded(hr, "Failed to write multi string value."); | ||
273 | |||
274 | hr = RegReadStringArray(hkBase, L"MultiString", &rgsczStrings, &cStrings); | ||
275 | NativeAssert::Succeeded(hr, "Failed to read multi string value."); | ||
276 | Assert::Equal<DWORD>(2, cStrings); | ||
277 | NativeAssert::StringEqual(L"First", rgsczStrings[0]); | ||
278 | NativeAssert::StringEqual(L"Second", rgsczStrings[1]); | ||
279 | } | ||
280 | finally | ||
281 | { | ||
282 | ReleaseStrArray(rgsczStrings, cStrings); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | [Fact] | ||
287 | void RegUtilPartialMultiStringValueTest() | ||
288 | { | ||
289 | this->PartialMultiStringValueTest(); | ||
290 | } | ||
291 | |||
292 | [Fact] | ||
293 | void RegUtilPartialMultiStringValueFallbackTest() | ||
294 | { | ||
295 | RegFunctionForceFallback(); | ||
296 | this->PartialMultiStringValueTest(); | ||
297 | } | ||
298 | |||
299 | void PartialMultiStringValueTest() | ||
300 | { | ||
301 | HRESULT hr = S_OK; | ||
302 | LPWSTR* rgsczStrings = NULL; | ||
303 | DWORD cStrings = 0; | ||
304 | |||
305 | try | ||
306 | { | ||
307 | this->CreateBaseKey(); | ||
308 | |||
309 | // Use API directly to write non-double-null terminated string. | ||
310 | hr = HRESULT_FROM_WIN32(::RegSetValueExW(hkBase, L"PartialMultiString", 0, REG_MULTI_SZ, reinterpret_cast<const BYTE*>(L"First\0Second"), 13 * sizeof(WCHAR))); | ||
311 | NativeAssert::Succeeded(hr, "Failed to write partial multi string value."); | ||
312 | |||
313 | hr = RegReadStringArray(hkBase, L"PartialMultiString", &rgsczStrings, &cStrings); | ||
314 | NativeAssert::Succeeded(hr, "Failed to read partial multi string value."); | ||
315 | Assert::Equal<DWORD>(2, cStrings); | ||
316 | NativeAssert::StringEqual(L"First", rgsczStrings[0]); | ||
317 | NativeAssert::StringEqual(L"Second", rgsczStrings[1]); | ||
318 | } | ||
319 | finally | ||
320 | { | ||
321 | ReleaseStrArray(rgsczStrings, cStrings); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | [Fact] | ||
326 | void RegUtilEmptyMultiStringValueTest() | ||
327 | { | ||
328 | this->EmptyMultiStringValueTest(); | ||
329 | } | ||
330 | |||
331 | [Fact] | ||
332 | void RegUtilEmptyMultiStringValueFallbackTest() | ||
333 | { | ||
334 | RegFunctionForceFallback(); | ||
335 | this->EmptyMultiStringValueTest(); | ||
336 | } | ||
337 | |||
338 | void EmptyMultiStringValueTest() | ||
339 | { | ||
340 | HRESULT hr = S_OK; | ||
341 | LPWSTR* rgsczStrings = NULL; | ||
342 | DWORD cStrings = 0; | ||
343 | |||
344 | try | ||
345 | { | ||
346 | this->CreateBaseKey(); | ||
347 | |||
348 | hr = RegWriteStringArray(hkBase, L"EmptyMultiString", rgwzMultiValue, 0); | ||
349 | NativeAssert::Succeeded(hr, "Failed to write empty multi string value."); | ||
350 | |||
351 | hr = RegReadStringArray(hkBase, L"EmptyMultiString", &rgsczStrings, &cStrings); | ||
352 | NativeAssert::Succeeded(hr, "Failed to read empty multi string value."); | ||
353 | Assert::Equal<DWORD>(0, cStrings); | ||
354 | } | ||
355 | finally | ||
356 | { | ||
357 | ReleaseStrArray(rgsczStrings, cStrings); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | [Fact] | ||
362 | void RegUtilOneEmptyMultiStringValueTest() | ||
363 | { | ||
364 | this->OneEmptyMultiStringValueTest(); | ||
365 | } | ||
366 | |||
367 | [Fact] | ||
368 | void RegUtilOneEmptyMultiStringValueFallbackTest() | ||
369 | { | ||
370 | RegFunctionForceFallback(); | ||
371 | this->OneEmptyMultiStringValueTest(); | ||
372 | } | ||
373 | |||
374 | void OneEmptyMultiStringValueTest() | ||
375 | { | ||
376 | HRESULT hr = S_OK; | ||
377 | LPWSTR* rgsczStrings = NULL; | ||
378 | DWORD cStrings = 0; | ||
379 | |||
380 | try | ||
381 | { | ||
382 | this->CreateBaseKey(); | ||
383 | |||
384 | hr = RegWriteStringArray(hkBase, L"OneEmptyMultiString", rgwzEmptyMultiValue, 1); | ||
385 | NativeAssert::Succeeded(hr, "Failed to write one empty multi string value."); | ||
386 | |||
387 | hr = RegReadStringArray(hkBase, L"OneEmptyMultiString", &rgsczStrings, &cStrings); | ||
388 | NativeAssert::Succeeded(hr, "Failed to read one empty multi string value."); | ||
389 | Assert::Equal<DWORD>(0, cStrings); | ||
390 | } | ||
391 | finally | ||
392 | { | ||
393 | ReleaseStrArray(rgsczStrings, cStrings); | ||
394 | } | ||
395 | } | ||
396 | |||
397 | [Fact] | ||
398 | void RegUtilTwoEmptyMultiStringValueTest() | ||
399 | { | ||
400 | this->TwoEmptyMultiStringValueTest(); | ||
401 | } | ||
402 | |||
403 | [Fact] | ||
404 | void RegUtilTwoEmptyMultiStringValueFallbackTest() | ||
405 | { | ||
406 | RegFunctionForceFallback(); | ||
407 | this->TwoEmptyMultiStringValueTest(); | ||
408 | } | ||
409 | |||
410 | void TwoEmptyMultiStringValueTest() | ||
411 | { | ||
412 | HRESULT hr = S_OK; | ||
413 | LPWSTR* rgsczStrings = NULL; | ||
414 | DWORD cStrings = 0; | ||
415 | |||
416 | try | ||
417 | { | ||
418 | this->CreateBaseKey(); | ||
419 | |||
420 | hr = RegWriteStringArray(hkBase, L"OneEmptyMultiString", rgwzEmptyMultiValue, 2); | ||
421 | NativeAssert::Succeeded(hr, "Failed to write one empty multi string value."); | ||
422 | |||
423 | hr = RegReadStringArray(hkBase, L"OneEmptyMultiString", &rgsczStrings, &cStrings); | ||
424 | NativeAssert::Succeeded(hr, "Failed to read one empty multi string value."); | ||
425 | Assert::Equal<DWORD>(2, cStrings); | ||
426 | NativeAssert::StringEqual(L"", rgsczStrings[0]); | ||
427 | NativeAssert::StringEqual(L"", rgsczStrings[1]); | ||
428 | } | ||
429 | finally | ||
430 | { | ||
431 | ReleaseStrArray(rgsczStrings, cStrings); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | [Fact] | ||
436 | void RegUtilOnePartialEmptyMultiStringValueTest() | ||
437 | { | ||
438 | this->OnePartialEmptyMultiStringValueTest(); | ||
439 | } | ||
440 | |||
441 | [Fact] | ||
442 | void RegUtilOnePartialEmptyMultiStringValueFallbackTest() | ||
443 | { | ||
444 | RegFunctionForceFallback(); | ||
445 | this->OnePartialEmptyMultiStringValueTest(); | ||
446 | } | ||
447 | |||
448 | void OnePartialEmptyMultiStringValueTest() | ||
449 | { | ||
450 | HRESULT hr = S_OK; | ||
451 | LPWSTR* rgsczStrings = NULL; | ||
452 | DWORD cStrings = 0; | ||
453 | |||
454 | try | ||
455 | { | ||
456 | this->CreateBaseKey(); | ||
457 | |||
458 | // Use API directly to write non-double-null terminated string. | ||
459 | hr = HRESULT_FROM_WIN32(::RegSetValueExW(hkBase, L"OnePartialEmptyMultiString", 0, REG_MULTI_SZ, reinterpret_cast<const BYTE*>(L""), 1 * sizeof(WCHAR))); | ||
460 | NativeAssert::Succeeded(hr, "Failed to write partial empty multi string value."); | ||
461 | |||
462 | hr = RegReadStringArray(hkBase, L"OnePartialEmptyMultiString", &rgsczStrings, &cStrings); | ||
463 | NativeAssert::Succeeded(hr, "Failed to read partial empty multi string value."); | ||
464 | Assert::Equal<DWORD>(0, cStrings); | ||
465 | } | ||
466 | finally | ||
467 | { | ||
468 | ReleaseStrArray(rgsczStrings, cStrings); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | [Fact] | ||
473 | void RegUtilBinaryValueTest() | ||
474 | { | ||
475 | this->BinaryValueTest(); | ||
476 | } | ||
477 | |||
478 | [Fact] | ||
479 | void RegUtilBinaryValueFallbackTest() | ||
480 | { | ||
481 | RegFunctionForceFallback(); | ||
482 | this->BinaryValueTest(); | ||
483 | } | ||
484 | |||
485 | void BinaryValueTest() | ||
486 | { | ||
487 | HRESULT hr = S_OK; | ||
488 | BYTE pbSource[4] = { 1, 2, 3, 4 }; | ||
489 | BYTE* pbBuffer = NULL; | ||
490 | SIZE_T cbBuffer = 0; | ||
491 | |||
492 | try | ||
493 | { | ||
494 | this->CreateBaseKey(); | ||
495 | |||
496 | hr = RegWriteBinary(hkBase, L"Binary", pbSource, 4); | ||
497 | NativeAssert::Succeeded(hr, "Failed to write binary value."); | ||
498 | |||
499 | hr = RegReadBinary(hkBase, L"Binary", &pbBuffer, &cbBuffer); | ||
500 | NativeAssert::Succeeded(hr, "Failed to read binary value."); | ||
501 | Assert::Equal<DWORD>(4, cbBuffer); | ||
502 | Assert::Equal<BYTE>(1, pbBuffer[0]); | ||
503 | Assert::Equal<BYTE>(2, pbBuffer[1]); | ||
504 | Assert::Equal<BYTE>(3, pbBuffer[2]); | ||
505 | Assert::Equal<BYTE>(4, pbBuffer[3]); | ||
506 | } | ||
507 | finally | ||
508 | { | ||
509 | ReleaseMem(pbBuffer); | ||
510 | } | ||
511 | } | ||
512 | |||
513 | [Fact] | ||
514 | void RegUtilEmptyBinaryValueTest() | ||
515 | { | ||
516 | this->EmptyBinaryValueTest(); | ||
517 | } | ||
518 | |||
519 | [Fact] | ||
520 | void RegUtilEmptyBinaryValueFallbackTest() | ||
521 | { | ||
522 | RegFunctionForceFallback(); | ||
523 | this->EmptyBinaryValueTest(); | ||
524 | } | ||
525 | |||
526 | void EmptyBinaryValueTest() | ||
527 | { | ||
528 | HRESULT hr = S_OK; | ||
529 | BYTE* pbBuffer = NULL; | ||
530 | SIZE_T cbBuffer = 0; | ||
531 | |||
532 | try | ||
533 | { | ||
534 | this->CreateBaseKey(); | ||
535 | |||
536 | hr = RegWriteBinary(hkBase, L"Binary", NULL, 0); | ||
537 | NativeAssert::Succeeded(hr, "Failed to write binary value."); | ||
538 | |||
539 | hr = RegReadBinary(hkBase, L"Binary", &pbBuffer, &cbBuffer); | ||
540 | NativeAssert::Succeeded(hr, "Failed to read binary value."); | ||
541 | Assert::Equal<DWORD>(0, cbBuffer); | ||
542 | } | ||
543 | finally | ||
544 | { | ||
545 | ReleaseMem(pbBuffer); | ||
546 | } | ||
547 | } | ||
548 | |||
549 | [Fact] | ||
550 | void RegUtilQwordVersionValueTest() | ||
551 | { | ||
552 | this->QwordVersionValueTest(); | ||
553 | } | ||
554 | |||
555 | [Fact] | ||
556 | void RegUtilQwordVersionValueFallbackTest() | ||
557 | { | ||
558 | RegFunctionForceFallback(); | ||
559 | this->QwordVersionValueTest(); | ||
560 | } | ||
561 | |||
562 | void QwordVersionValueTest() | ||
563 | { | ||
564 | HRESULT hr = S_OK; | ||
565 | DWORD64 qwVersion = FILEMAKEVERSION(1, 2, 3, 4); | ||
566 | DWORD64 qwValue = 0; | ||
567 | |||
568 | this->CreateBaseKey(); | ||
569 | |||
570 | hr = RegWriteQword(hkBase, L"QwordVersion", qwVersion); | ||
571 | NativeAssert::Succeeded(hr, "Failed to write qword version value."); | ||
572 | |||
573 | hr = RegReadVersion(hkBase, L"QwordVersion", &qwValue); | ||
574 | NativeAssert::Succeeded(hr, "Failed to read qword version value."); | ||
575 | Assert::Equal<DWORD64>(qwVersion, qwValue); | ||
576 | } | ||
577 | |||
578 | [Fact] | ||
579 | void RegUtilStringVersionValueTest() | ||
580 | { | ||
581 | this->StringVersionValueTest(); | ||
582 | } | ||
583 | |||
584 | [Fact] | ||
585 | void RegUtilStringVersionValueFallbackTest() | ||
586 | { | ||
587 | RegFunctionForceFallback(); | ||
588 | this->StringVersionValueTest(); | ||
589 | } | ||
590 | |||
591 | void StringVersionValueTest() | ||
592 | { | ||
593 | HRESULT hr = S_OK; | ||
594 | LPCWSTR wzVersion = L"65535.65535.65535.65535"; | ||
595 | DWORD64 qwValue = 0; | ||
596 | |||
597 | this->CreateBaseKey(); | ||
598 | |||
599 | hr = RegWriteString(hkBase, L"StringVersion", wzVersion); | ||
600 | NativeAssert::Succeeded(hr, "Failed to write string version value."); | ||
601 | |||
602 | hr = RegReadVersion(hkBase, L"StringVersion", &qwValue); | ||
603 | NativeAssert::Succeeded(hr, "Failed to read string version value."); | ||
604 | Assert::Equal<DWORD64>(MAXDWORD64, qwValue); | ||
605 | } | ||
606 | |||
607 | [Fact] | ||
608 | void RegUtilQwordWixVersionValueTest() | ||
609 | { | ||
610 | this->QwordWixVersionValueTest(); | ||
611 | } | ||
612 | |||
613 | [Fact] | ||
614 | void RegUtilQwordWixVersionValueFallbackTest() | ||
615 | { | ||
616 | RegFunctionForceFallback(); | ||
617 | this->QwordWixVersionValueTest(); | ||
618 | } | ||
619 | |||
620 | void QwordWixVersionValueTest() | ||
621 | { | ||
622 | HRESULT hr = S_OK; | ||
623 | DWORD64 qwVersion = FILEMAKEVERSION(1, 2, 3, 4); | ||
624 | VERUTIL_VERSION* pVersion = NULL; | ||
625 | |||
626 | try | ||
627 | { | ||
628 | this->CreateBaseKey(); | ||
629 | |||
630 | hr = RegWriteQword(hkBase, L"QwordWixVersion", qwVersion); | ||
631 | NativeAssert::Succeeded(hr, "Failed to write qword wix version value."); | ||
632 | |||
633 | hr = RegReadWixVersion(hkBase, L"QwordWixVersion", &pVersion); | ||
634 | NativeAssert::Succeeded(hr, "Failed to read qword wix version value."); | ||
635 | NativeAssert::StringEqual(L"1.2.3.4", pVersion->sczVersion); | ||
636 | } | ||
637 | finally | ||
638 | { | ||
639 | ReleaseVerutilVersion(pVersion); | ||
640 | } | ||
641 | } | ||
642 | |||
643 | [Fact] | ||
644 | void RegUtilStringWixVersionValueTest() | ||
645 | { | ||
646 | this->StringWixVersionValueTest(); | ||
647 | } | ||
648 | |||
649 | [Fact] | ||
650 | void RegUtilStringWixVersionValueFallbackTest() | ||
651 | { | ||
652 | RegFunctionForceFallback(); | ||
653 | this->StringWixVersionValueTest(); | ||
654 | } | ||
655 | |||
656 | void StringWixVersionValueTest() | ||
657 | { | ||
658 | HRESULT hr = S_OK; | ||
659 | LPCWSTR wzVersion = L"65535.65535.65535.65535-abc+def"; | ||
660 | VERUTIL_VERSION* pVersion = NULL; | ||
661 | |||
662 | try | ||
663 | { | ||
664 | this->CreateBaseKey(); | ||
665 | |||
666 | hr = RegWriteString(hkBase, L"StringWixVersion", wzVersion); | ||
667 | NativeAssert::Succeeded(hr, "Failed to write string wix version value."); | ||
668 | |||
669 | hr = RegReadWixVersion(hkBase, L"StringWixVersion", &pVersion); | ||
670 | NativeAssert::Succeeded(hr, "Failed to read string wix version value."); | ||
671 | NativeAssert::StringEqual(wzVersion, pVersion->sczVersion); | ||
672 | } | ||
673 | finally | ||
674 | { | ||
675 | ReleaseVerutilVersion(pVersion); | ||
676 | } | ||
677 | } | ||
678 | }; | ||
679 | } | ||