diff options
Diffstat (limited to '')
| -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 | } | ||
