aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/burn/engine/search.cpp50
-rw-r--r--src/burn/test/BurnUnitTest/TestRegistryFixture.cpp4
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/regutil.h44
-rw-r--r--src/libs/dutil/WixToolset.DUtil/regutil.cpp420
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj1
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters3
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp2
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/RegUtilTest.cpp679
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 );
102typedef 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*********************************************************************/
147void 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*********************************************************************/
244HRESULT 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*********************************************************************/
278HRESULT 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;
28static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; 29static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW;
29static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; 30static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW;
30static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; 31static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW;
32static PFN_REGGETVALUEW vpfnRegGetValueW = NULL;
33static PFN_REGGETVALUEW vpfnRegGetValueWFromLibrary = NULL;
31 34
32static HMODULE vhAdvApi32Dll = NULL; 35static HMODULE vhAdvApi32Dll = NULL;
33static BOOL vfRegInitialized = FALSE; 36static BOOL vfRegInitialized = FALSE;
34 37
38static 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 );
35static HRESULT WriteStringToRegistry( 45static 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
59LExit: 77LExit:
@@ -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
124DAPI_(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
361DAPI_(HRESULT) RegReadBinary( 390DAPI_(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
403LExit: 456LExit:
404 ReleaseMem(pbBuffer); 457 ReleaseStr(sczExpand);
405 458
406 return hr; 459 return hr;
407} 460}
408 461
462DAPI_(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
484LExit:
485 return hr;
486}
487
409 488
410DAPI_(HRESULT) RegReadString( 489DAPI_(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
517LExit:
518 return hr;
519}
520
521
522DAPI_(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
476LExit: 554LExit:
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
612LExit: 680LExit:
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
662LExit: 731LExit:
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
692LExit: 759LExit:
@@ -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
1068static 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
1003static HRESULT WriteStringToRegistry( 1110static 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
5using namespace System;
6using namespace System::Collections::Generic;
7using namespace System::Runtime::InteropServices;
8using namespace Xunit;
9using namespace WixBuildTools::TestSupport;
10
11LPCWSTR wzBaseRegKey = L"Software\\RegUtilTest\\";
12LPWSTR rgwzMultiValue[2] = { L"First", L"Second" };
13LPWSTR rgwzEmptyMultiValue[2] = { L"", L"" };
14
15HKEY hkBase;
16
17namespace 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}