diff options
Diffstat (limited to 'src/dutil/regutil.cpp')
| -rw-r--r-- | src/dutil/regutil.cpp | 926 |
1 files changed, 926 insertions, 0 deletions
diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp new file mode 100644 index 00000000..ec370f58 --- /dev/null +++ b/src/dutil/regutil.cpp | |||
| @@ -0,0 +1,926 @@ | |||
| 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 | static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW; | ||
| 6 | static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW; | ||
| 7 | static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL; | ||
| 8 | static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL; | ||
| 9 | static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW; | ||
| 10 | static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW; | ||
| 11 | static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW; | ||
| 12 | static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW; | ||
| 13 | static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; | ||
| 14 | static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; | ||
| 15 | static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; | ||
| 16 | |||
| 17 | static HMODULE vhAdvApi32Dll = NULL; | ||
| 18 | static BOOL vfRegInitialized = FALSE; | ||
| 19 | |||
| 20 | /******************************************************************** | ||
| 21 | RegInitialize - initializes regutil | ||
| 22 | |||
| 23 | *********************************************************************/ | ||
| 24 | extern "C" HRESULT DAPI RegInitialize( | ||
| 25 | ) | ||
| 26 | { | ||
| 27 | HRESULT hr = S_OK; | ||
| 28 | |||
| 29 | hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); | ||
| 30 | ExitOnFailure(hr, "Failed to load AdvApi32.dll"); | ||
| 31 | |||
| 32 | // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW | ||
| 33 | vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); | ||
| 34 | |||
| 35 | if (NULL == vpfnRegDeleteKeyExW) | ||
| 36 | { | ||
| 37 | vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; | ||
| 38 | } | ||
| 39 | |||
| 40 | vfRegInitialized = TRUE; | ||
| 41 | |||
| 42 | LExit: | ||
| 43 | return hr; | ||
| 44 | } | ||
| 45 | |||
| 46 | |||
| 47 | /******************************************************************** | ||
| 48 | RegUninitialize - uninitializes regutil | ||
| 49 | |||
| 50 | *********************************************************************/ | ||
| 51 | extern "C" void DAPI RegUninitialize( | ||
| 52 | ) | ||
| 53 | { | ||
| 54 | if (vhAdvApi32Dll) | ||
| 55 | { | ||
| 56 | ::FreeLibrary(vhAdvApi32Dll); | ||
| 57 | vhAdvApi32Dll = NULL; | ||
| 58 | vpfnRegDeleteKeyExWFromLibrary = NULL; | ||
| 59 | vpfnRegDeleteKeyExW = NULL; | ||
| 60 | } | ||
| 61 | |||
| 62 | vfRegInitialized = FALSE; | ||
| 63 | } | ||
| 64 | |||
| 65 | |||
| 66 | /******************************************************************** | ||
| 67 | RegFunctionOverride - overrides the registry functions. Typically used | ||
| 68 | for unit testing. | ||
| 69 | |||
| 70 | *********************************************************************/ | ||
| 71 | extern "C" void DAPI RegFunctionOverride( | ||
| 72 | __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW, | ||
| 73 | __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW, | ||
| 74 | __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW, | ||
| 75 | __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW, | ||
| 76 | __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW, | ||
| 77 | __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, | ||
| 78 | __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, | ||
| 79 | __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, | ||
| 80 | __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW | ||
| 81 | ) | ||
| 82 | { | ||
| 83 | vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; | ||
| 84 | vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW; | ||
| 85 | vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary; | ||
| 86 | vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW; | ||
| 87 | vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW; | ||
| 88 | vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW; | ||
| 89 | vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; | ||
| 90 | vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; | ||
| 91 | vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; | ||
| 92 | } | ||
| 93 | |||
| 94 | |||
| 95 | /******************************************************************** | ||
| 96 | RegCreate - creates a registry key. | ||
| 97 | |||
| 98 | *********************************************************************/ | ||
| 99 | extern "C" HRESULT DAPI RegCreate( | ||
| 100 | __in HKEY hkRoot, | ||
| 101 | __in_z LPCWSTR wzSubKey, | ||
| 102 | __in DWORD dwAccess, | ||
| 103 | __out HKEY* phk | ||
| 104 | ) | ||
| 105 | { | ||
| 106 | HRESULT hr = S_OK; | ||
| 107 | DWORD er = ERROR_SUCCESS; | ||
| 108 | |||
| 109 | er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL); | ||
| 110 | ExitOnWin32Error(er, hr, "Failed to create registry key."); | ||
| 111 | |||
| 112 | LExit: | ||
| 113 | return hr; | ||
| 114 | } | ||
| 115 | |||
| 116 | |||
| 117 | /******************************************************************** | ||
| 118 | RegCreate - creates a registry key with extra options. | ||
| 119 | |||
| 120 | *********************************************************************/ | ||
| 121 | HRESULT DAPI RegCreateEx( | ||
| 122 | __in HKEY hkRoot, | ||
| 123 | __in_z LPCWSTR wzSubKey, | ||
| 124 | __in DWORD dwAccess, | ||
| 125 | __in BOOL fVolatile, | ||
| 126 | __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes, | ||
| 127 | __out HKEY* phk, | ||
| 128 | __out_opt BOOL* pfCreated | ||
| 129 | ) | ||
| 130 | { | ||
| 131 | HRESULT hr = S_OK; | ||
| 132 | DWORD er = ERROR_SUCCESS; | ||
| 133 | DWORD dwDisposition; | ||
| 134 | |||
| 135 | er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition); | ||
| 136 | ExitOnWin32Error(er, hr, "Failed to create registry key."); | ||
| 137 | |||
| 138 | if (pfCreated) | ||
| 139 | { | ||
| 140 | *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition); | ||
| 141 | } | ||
| 142 | |||
| 143 | LExit: | ||
| 144 | return hr; | ||
| 145 | } | ||
| 146 | |||
| 147 | |||
| 148 | /******************************************************************** | ||
| 149 | RegOpen - opens a registry key. | ||
| 150 | |||
| 151 | *********************************************************************/ | ||
| 152 | extern "C" HRESULT DAPI RegOpen( | ||
| 153 | __in HKEY hkRoot, | ||
| 154 | __in_z LPCWSTR wzSubKey, | ||
| 155 | __in DWORD dwAccess, | ||
| 156 | __out HKEY* phk | ||
| 157 | ) | ||
| 158 | { | ||
| 159 | HRESULT hr = S_OK; | ||
| 160 | DWORD er = ERROR_SUCCESS; | ||
| 161 | |||
| 162 | er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess, phk); | ||
| 163 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 164 | { | ||
| 165 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 166 | } | ||
| 167 | ExitOnWin32Error(er, hr, "Failed to open registry key."); | ||
| 168 | |||
| 169 | LExit: | ||
| 170 | return hr; | ||
| 171 | } | ||
| 172 | |||
| 173 | |||
| 174 | /******************************************************************** | ||
| 175 | RegDelete - deletes a registry key (and optionally it's whole tree). | ||
| 176 | |||
| 177 | *********************************************************************/ | ||
| 178 | extern "C" HRESULT DAPI RegDelete( | ||
| 179 | __in HKEY hkRoot, | ||
| 180 | __in_z LPCWSTR wzSubKey, | ||
| 181 | __in REG_KEY_BITNESS kbKeyBitness, | ||
| 182 | __in BOOL fDeleteTree | ||
| 183 | ) | ||
| 184 | { | ||
| 185 | HRESULT hr = S_OK; | ||
| 186 | DWORD er = ERROR_SUCCESS; | ||
| 187 | LPWSTR pszEnumeratedSubKey = NULL; | ||
| 188 | LPWSTR pszRecursiveSubKey = NULL; | ||
| 189 | HKEY hkKey = NULL; | ||
| 190 | REGSAM samDesired = 0; | ||
| 191 | |||
| 192 | if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness) | ||
| 193 | { | ||
| 194 | hr = E_INVALIDARG; | ||
| 195 | ExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!"); | ||
| 196 | } | ||
| 197 | |||
| 198 | switch (kbKeyBitness) | ||
| 199 | { | ||
| 200 | case REG_KEY_32BIT: | ||
| 201 | samDesired = KEY_WOW64_32KEY; | ||
| 202 | break; | ||
| 203 | case REG_KEY_64BIT: | ||
| 204 | samDesired = KEY_WOW64_64KEY; | ||
| 205 | break; | ||
| 206 | case REG_KEY_DEFAULT: | ||
| 207 | // Nothing to do | ||
| 208 | break; | ||
| 209 | } | ||
| 210 | |||
| 211 | if (fDeleteTree) | ||
| 212 | { | ||
| 213 | hr = RegOpen(hkRoot, wzSubKey, KEY_READ | samDesired, &hkKey); | ||
| 214 | if (E_FILENOTFOUND == hr) | ||
| 215 | { | ||
| 216 | ExitFunction1(hr = S_OK); | ||
| 217 | } | ||
| 218 | ExitOnFailure(hr, "Failed to open this key for enumerating subkeys", wzSubKey); | ||
| 219 | |||
| 220 | // Yes, keep enumerating the 0th item, because we're deleting it every time | ||
| 221 | while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey))) | ||
| 222 | { | ||
| 223 | ExitOnFailure(hr, "Failed to enumerate key 0"); | ||
| 224 | |||
| 225 | hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey); | ||
| 226 | ExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey); | ||
| 227 | |||
| 228 | hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree); | ||
| 229 | ExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey); | ||
| 230 | } | ||
| 231 | |||
| 232 | hr = S_OK; | ||
| 233 | } | ||
| 234 | |||
| 235 | if (NULL != vpfnRegDeleteKeyExW) | ||
| 236 | { | ||
| 237 | er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0); | ||
| 238 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 239 | { | ||
| 240 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 241 | } | ||
| 242 | ExitOnWin32Error(er, hr, "Failed to delete registry key (ex)."); | ||
| 243 | } | ||
| 244 | else | ||
| 245 | { | ||
| 246 | er = vpfnRegDeleteKeyW(hkRoot, wzSubKey); | ||
| 247 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 248 | { | ||
| 249 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 250 | } | ||
| 251 | ExitOnWin32Error(er, hr, "Failed to delete registry key."); | ||
| 252 | } | ||
| 253 | |||
| 254 | LExit: | ||
| 255 | ReleaseRegKey(hkKey); | ||
| 256 | ReleaseStr(pszEnumeratedSubKey); | ||
| 257 | ReleaseStr(pszRecursiveSubKey); | ||
| 258 | |||
| 259 | return hr; | ||
| 260 | } | ||
| 261 | |||
| 262 | |||
| 263 | /******************************************************************** | ||
| 264 | RegKeyEnum - enumerates a registry key. | ||
| 265 | |||
| 266 | *********************************************************************/ | ||
| 267 | extern "C" HRESULT DAPI RegKeyEnum( | ||
| 268 | __in HKEY hk, | ||
| 269 | __in DWORD dwIndex, | ||
| 270 | __deref_out_z LPWSTR* psczKey | ||
| 271 | ) | ||
| 272 | { | ||
| 273 | HRESULT hr = S_OK; | ||
| 274 | DWORD er = ERROR_SUCCESS; | ||
| 275 | DWORD cch = 0; | ||
| 276 | |||
| 277 | if (psczKey && *psczKey) | ||
| 278 | { | ||
| 279 | hr = StrMaxLength(*psczKey, reinterpret_cast<DWORD_PTR*>(&cch)); | ||
| 280 | ExitOnFailure(hr, "Failed to determine length of string."); | ||
| 281 | } | ||
| 282 | |||
| 283 | if (2 > cch) | ||
| 284 | { | ||
| 285 | cch = 2; | ||
| 286 | |||
| 287 | hr = StrAlloc(psczKey, cch); | ||
| 288 | ExitOnFailure(hr, "Failed to allocate string to minimum size."); | ||
| 289 | } | ||
| 290 | |||
| 291 | er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); | ||
| 292 | if (ERROR_MORE_DATA == er) | ||
| 293 | { | ||
| 294 | er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL); | ||
| 295 | ExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key."); | ||
| 296 | |||
| 297 | ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator. | ||
| 298 | hr = StrAlloc(psczKey, cch); | ||
| 299 | ExitOnFailure(hr, "Failed to allocate string bigger for enum registry key."); | ||
| 300 | |||
| 301 | er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL); | ||
| 302 | } | ||
| 303 | else if (ERROR_NO_MORE_ITEMS == er) | ||
| 304 | { | ||
| 305 | ExitFunction1(hr = E_NOMOREITEMS); | ||
| 306 | } | ||
| 307 | ExitOnWin32Error(er, hr, "Failed to enum registry key."); | ||
| 308 | |||
| 309 | // Always ensure the registry key name is null terminated. | ||
| 310 | #pragma prefast(push) | ||
| 311 | #pragma prefast(disable:26018) | ||
| 312 | (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works. | ||
| 313 | #pragma prefast(pop) | ||
| 314 | |||
| 315 | LExit: | ||
| 316 | return hr; | ||
| 317 | } | ||
| 318 | |||
| 319 | |||
| 320 | /******************************************************************** | ||
| 321 | RegValueEnum - enumerates a registry value. | ||
| 322 | |||
| 323 | *********************************************************************/ | ||
| 324 | HRESULT DAPI RegValueEnum( | ||
| 325 | __in HKEY hk, | ||
| 326 | __in DWORD dwIndex, | ||
| 327 | __deref_out_z LPWSTR* psczName, | ||
| 328 | __out_opt DWORD *pdwType | ||
| 329 | ) | ||
| 330 | { | ||
| 331 | HRESULT hr = S_OK; | ||
| 332 | DWORD er = ERROR_SUCCESS; | ||
| 333 | DWORD cbValueName = 0; | ||
| 334 | |||
| 335 | er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL); | ||
| 336 | ExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key."); | ||
| 337 | |||
| 338 | // Add one for null terminator | ||
| 339 | ++cbValueName; | ||
| 340 | |||
| 341 | hr = StrAlloc(psczName, cbValueName); | ||
| 342 | ExitOnFailure(hr, "Failed to allocate array for registry value name"); | ||
| 343 | |||
| 344 | er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL); | ||
| 345 | if (ERROR_NO_MORE_ITEMS == er) | ||
| 346 | { | ||
| 347 | ExitFunction1(hr = E_NOMOREITEMS); | ||
| 348 | } | ||
| 349 | ExitOnWin32Error(er, hr, "Failed to enumerate registry value"); | ||
| 350 | |||
| 351 | LExit: | ||
| 352 | return hr; | ||
| 353 | } | ||
| 354 | |||
| 355 | /******************************************************************** | ||
| 356 | RegGetType - reads a registry key value type. | ||
| 357 | *********************************************************************/ | ||
| 358 | HRESULT DAPI RegGetType( | ||
| 359 | __in HKEY hk, | ||
| 360 | __in_z_opt LPCWSTR wzName, | ||
| 361 | __out DWORD *pdwType | ||
| 362 | ) | ||
| 363 | { | ||
| 364 | HRESULT hr = S_OK; | ||
| 365 | DWORD er = ERROR_SUCCESS; | ||
| 366 | |||
| 367 | er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL); | ||
| 368 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 369 | { | ||
| 370 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 371 | } | ||
| 372 | ExitOnWin32Error(er, hr, "Failed to read registry value."); | ||
| 373 | LExit: | ||
| 374 | |||
| 375 | return hr; | ||
| 376 | } | ||
| 377 | |||
| 378 | /******************************************************************** | ||
| 379 | RegReadBinary - reads a registry key binary value. | ||
| 380 | NOTE: caller is responsible for freeing *ppbBuffer | ||
| 381 | *********************************************************************/ | ||
| 382 | HRESULT DAPI RegReadBinary( | ||
| 383 | __in HKEY hk, | ||
| 384 | __in_z_opt LPCWSTR wzName, | ||
| 385 | __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, | ||
| 386 | __out SIZE_T *pcbBuffer | ||
| 387 | ) | ||
| 388 | { | ||
| 389 | HRESULT hr = S_OK; | ||
| 390 | LPBYTE pbBuffer = NULL; | ||
| 391 | DWORD er = ERROR_SUCCESS; | ||
| 392 | DWORD cb = 0; | ||
| 393 | DWORD dwType = 0; | ||
| 394 | |||
| 395 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb); | ||
| 396 | ExitOnWin32Error(er, hr, "Failed to get size of registry value."); | ||
| 397 | |||
| 398 | // Zero-length binary values can exist | ||
| 399 | if (0 < cb) | ||
| 400 | { | ||
| 401 | pbBuffer = static_cast<LPBYTE>(MemAlloc(cb, FALSE)); | ||
| 402 | ExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); | ||
| 403 | |||
| 404 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); | ||
| 405 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 406 | { | ||
| 407 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 408 | } | ||
| 409 | ExitOnWin32Error(er, hr, "Failed to read registry value."); | ||
| 410 | } | ||
| 411 | |||
| 412 | if (REG_BINARY == dwType) | ||
| 413 | { | ||
| 414 | *ppbBuffer = pbBuffer; | ||
| 415 | pbBuffer = NULL; | ||
| 416 | *pcbBuffer = cb; | ||
| 417 | } | ||
| 418 | else | ||
| 419 | { | ||
| 420 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | ||
| 421 | ExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); | ||
| 422 | } | ||
| 423 | |||
| 424 | LExit: | ||
| 425 | ReleaseMem(pbBuffer); | ||
| 426 | |||
| 427 | return hr; | ||
| 428 | } | ||
| 429 | |||
| 430 | |||
| 431 | /******************************************************************** | ||
| 432 | RegReadString - reads a registry key value as a string. | ||
| 433 | |||
| 434 | *********************************************************************/ | ||
| 435 | extern "C" HRESULT DAPI RegReadString( | ||
| 436 | __in HKEY hk, | ||
| 437 | __in_z_opt LPCWSTR wzName, | ||
| 438 | __deref_out_z LPWSTR* psczValue | ||
| 439 | ) | ||
| 440 | { | ||
| 441 | HRESULT hr = S_OK; | ||
| 442 | DWORD er = ERROR_SUCCESS; | ||
| 443 | DWORD cch = 0; | ||
| 444 | DWORD cb = 0; | ||
| 445 | DWORD dwType = 0; | ||
| 446 | LPWSTR sczExpand = NULL; | ||
| 447 | |||
| 448 | if (psczValue && *psczValue) | ||
| 449 | { | ||
| 450 | hr = StrMaxLength(*psczValue, reinterpret_cast<DWORD_PTR*>(&cch)); | ||
| 451 | ExitOnFailure(hr, "Failed to determine length of string."); | ||
| 452 | } | ||
| 453 | |||
| 454 | if (2 > cch) | ||
| 455 | { | ||
| 456 | cch = 2; | ||
| 457 | |||
| 458 | hr = StrAlloc(psczValue, cch); | ||
| 459 | ExitOnFailure(hr, "Failed to allocate string to minimum size."); | ||
| 460 | } | ||
| 461 | |||
| 462 | cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator. | ||
| 463 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb); | ||
| 464 | if (ERROR_MORE_DATA == er) | ||
| 465 | { | ||
| 466 | cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator | ||
| 467 | hr = StrAlloc(psczValue, cch); | ||
| 468 | ExitOnFailure(hr, "Failed to allocate string bigger for registry value."); | ||
| 469 | |||
| 470 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb); | ||
| 471 | } | ||
| 472 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 473 | { | ||
| 474 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 475 | } | ||
| 476 | ExitOnWin32Error(er, hr, "Failed to read registry key."); | ||
| 477 | |||
| 478 | if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) | ||
| 479 | { | ||
| 480 | // Always ensure the registry value is null terminated. | ||
| 481 | (*psczValue)[cch - 1] = L'\0'; | ||
| 482 | |||
| 483 | if (REG_EXPAND_SZ == dwType) | ||
| 484 | { | ||
| 485 | hr = StrAllocString(&sczExpand, *psczValue, 0); | ||
| 486 | ExitOnFailure(hr, "Failed to copy registry value to expand."); | ||
| 487 | |||
| 488 | hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT); | ||
| 489 | ExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue); | ||
| 490 | } | ||
| 491 | } | ||
| 492 | else | ||
| 493 | { | ||
| 494 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | ||
| 495 | ExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType); | ||
| 496 | } | ||
| 497 | |||
| 498 | LExit: | ||
| 499 | ReleaseStr(sczExpand); | ||
| 500 | |||
| 501 | return hr; | ||
| 502 | } | ||
| 503 | |||
| 504 | |||
| 505 | /******************************************************************** | ||
| 506 | RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. | ||
| 507 | |||
| 508 | *********************************************************************/ | ||
| 509 | HRESULT DAPI RegReadStringArray( | ||
| 510 | __in HKEY hk, | ||
| 511 | __in_z_opt LPCWSTR wzName, | ||
| 512 | __deref_out_ecount_opt(pcStrings) LPWSTR** prgsczStrings, | ||
| 513 | __out DWORD *pcStrings | ||
| 514 | ) | ||
| 515 | { | ||
| 516 | HRESULT hr = S_OK; | ||
| 517 | DWORD er = ERROR_SUCCESS; | ||
| 518 | DWORD dwNullCharacters = 0; | ||
| 519 | DWORD dwType = 0; | ||
| 520 | DWORD cb = 0; | ||
| 521 | DWORD cch = 0; | ||
| 522 | LPCWSTR wzSource = NULL; | ||
| 523 | LPWSTR sczValue = NULL; | ||
| 524 | |||
| 525 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb); | ||
| 526 | if (0 < cb) | ||
| 527 | { | ||
| 528 | cch = cb / sizeof(WCHAR); | ||
| 529 | hr = StrAlloc(&sczValue, cch); | ||
| 530 | ExitOnFailure(hr, "Failed to allocate string for registry value."); | ||
| 531 | |||
| 532 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb); | ||
| 533 | } | ||
| 534 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 535 | { | ||
| 536 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 537 | } | ||
| 538 | ExitOnWin32Error(er, hr, "Failed to read registry key."); | ||
| 539 | |||
| 540 | if (cb / sizeof(WCHAR) != cch) | ||
| 541 | { | ||
| 542 | hr = E_UNEXPECTED; | ||
| 543 | ExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName); | ||
| 544 | } | ||
| 545 | |||
| 546 | if (REG_MULTI_SZ != dwType) | ||
| 547 | { | ||
| 548 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | ||
| 549 | ExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); | ||
| 550 | } | ||
| 551 | |||
| 552 | // Value exists, but is empty, so no strings to return. | ||
| 553 | if (2 > cch) | ||
| 554 | { | ||
| 555 | *prgsczStrings = NULL; | ||
| 556 | *pcStrings = 0; | ||
| 557 | ExitFunction1(hr = S_OK); | ||
| 558 | } | ||
| 559 | |||
| 560 | // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too. | ||
| 561 | if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2]) | ||
| 562 | { | ||
| 563 | hr = E_INVALIDARG; | ||
| 564 | ExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); | ||
| 565 | } | ||
| 566 | |||
| 567 | cch = cb / sizeof(WCHAR); | ||
| 568 | for (DWORD i = 0; i < cch; ++i) | ||
| 569 | { | ||
| 570 | if (L'\0' == sczValue[i]) | ||
| 571 | { | ||
| 572 | ++dwNullCharacters; | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | // There's one string for every null character encountered (except the extra 1 at the end of the string) | ||
| 577 | *pcStrings = dwNullCharacters - 1; | ||
| 578 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); | ||
| 579 | ExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); | ||
| 580 | |||
| 581 | #pragma prefast(push) | ||
| 582 | #pragma prefast(disable:26010) | ||
| 583 | wzSource = sczValue; | ||
| 584 | for (DWORD i = 0; i < *pcStrings; ++i) | ||
| 585 | { | ||
| 586 | hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); | ||
| 587 | ExitOnFailure(hr, "Failed to allocate copy of string"); | ||
| 588 | |||
| 589 | // Skip past this string | ||
| 590 | wzSource += lstrlenW(wzSource) + 1; | ||
| 591 | } | ||
| 592 | #pragma prefast(pop) | ||
| 593 | |||
| 594 | LExit: | ||
| 595 | ReleaseStr(sczValue); | ||
| 596 | |||
| 597 | return hr; | ||
| 598 | } | ||
| 599 | |||
| 600 | |||
| 601 | /******************************************************************** | ||
| 602 | RegReadVersion - reads a registry key value as a version. | ||
| 603 | |||
| 604 | *********************************************************************/ | ||
| 605 | extern "C" HRESULT DAPI RegReadVersion( | ||
| 606 | __in HKEY hk, | ||
| 607 | __in_z_opt LPCWSTR wzName, | ||
| 608 | __out DWORD64* pdw64Version | ||
| 609 | ) | ||
| 610 | { | ||
| 611 | HRESULT hr = S_OK; | ||
| 612 | DWORD er = ERROR_SUCCESS; | ||
| 613 | DWORD dwType = 0; | ||
| 614 | DWORD cb = 0; | ||
| 615 | LPWSTR sczVersion = NULL; | ||
| 616 | |||
| 617 | cb = sizeof(DWORD64); | ||
| 618 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*pdw64Version), &cb); | ||
| 619 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 620 | { | ||
| 621 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 622 | } | ||
| 623 | if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) | ||
| 624 | { | ||
| 625 | hr = RegReadString(hk, wzName, &sczVersion); | ||
| 626 | ExitOnFailure(hr, "Failed to read registry version as string."); | ||
| 627 | |||
| 628 | hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version); | ||
| 629 | ExitOnFailure(hr, "Failed to convert registry string to version."); | ||
| 630 | } | ||
| 631 | else if (REG_QWORD == dwType) | ||
| 632 | { | ||
| 633 | ExitOnWin32Error(er, hr, "Failed to read registry key."); | ||
| 634 | } | ||
| 635 | else // unexpected data type | ||
| 636 | { | ||
| 637 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | ||
| 638 | ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); | ||
| 639 | } | ||
| 640 | |||
| 641 | LExit: | ||
| 642 | ReleaseStr(sczVersion); | ||
| 643 | |||
| 644 | return hr; | ||
| 645 | } | ||
| 646 | |||
| 647 | |||
| 648 | /******************************************************************** | ||
| 649 | RegReadNumber - reads a DWORD registry key value as a number. | ||
| 650 | |||
| 651 | *********************************************************************/ | ||
| 652 | extern "C" HRESULT DAPI RegReadNumber( | ||
| 653 | __in HKEY hk, | ||
| 654 | __in_z_opt LPCWSTR wzName, | ||
| 655 | __out DWORD* pdwValue | ||
| 656 | ) | ||
| 657 | { | ||
| 658 | HRESULT hr = S_OK; | ||
| 659 | DWORD er = ERROR_SUCCESS; | ||
| 660 | DWORD dwType = 0; | ||
| 661 | DWORD cb = sizeof(DWORD); | ||
| 662 | |||
| 663 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(pdwValue), &cb); | ||
| 664 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 665 | { | ||
| 666 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 667 | } | ||
| 668 | ExitOnWin32Error(er, hr, "Failed to query registry key value."); | ||
| 669 | |||
| 670 | if (REG_DWORD != dwType) | ||
| 671 | { | ||
| 672 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | ||
| 673 | ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); | ||
| 674 | } | ||
| 675 | |||
| 676 | LExit: | ||
| 677 | return hr; | ||
| 678 | } | ||
| 679 | |||
| 680 | |||
| 681 | /******************************************************************** | ||
| 682 | RegReadQword - reads a QWORD registry key value as a number. | ||
| 683 | |||
| 684 | *********************************************************************/ | ||
| 685 | extern "C" HRESULT DAPI RegReadQword( | ||
| 686 | __in HKEY hk, | ||
| 687 | __in_z_opt LPCWSTR wzName, | ||
| 688 | __out DWORD64* pqwValue | ||
| 689 | ) | ||
| 690 | { | ||
| 691 | HRESULT hr = S_OK; | ||
| 692 | DWORD er = ERROR_SUCCESS; | ||
| 693 | DWORD dwType = 0; | ||
| 694 | DWORD cb = sizeof(DWORD64); | ||
| 695 | |||
| 696 | er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(pqwValue), &cb); | ||
| 697 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
| 698 | { | ||
| 699 | ExitFunction1(hr = E_FILENOTFOUND); | ||
| 700 | } | ||
| 701 | ExitOnWin32Error(er, hr, "Failed to query registry key value."); | ||
| 702 | |||
| 703 | if (REG_QWORD != dwType) | ||
| 704 | { | ||
| 705 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); | ||
| 706 | ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); | ||
| 707 | } | ||
| 708 | |||
| 709 | LExit: | ||
| 710 | return hr; | ||
| 711 | } | ||
| 712 | |||
| 713 | |||
| 714 | /******************************************************************** | ||
| 715 | RegWriteBinary - writes a registry key value as a binary. | ||
| 716 | |||
| 717 | *********************************************************************/ | ||
| 718 | HRESULT DAPI RegWriteBinary( | ||
| 719 | __in HKEY hk, | ||
| 720 | __in_z_opt LPCWSTR wzName, | ||
| 721 | __in_bcount(cbBuffer) const BYTE *pbBuffer, | ||
| 722 | __in DWORD cbBuffer | ||
| 723 | ) | ||
| 724 | { | ||
| 725 | HRESULT hr = S_OK; | ||
| 726 | DWORD er = ERROR_SUCCESS; | ||
| 727 | |||
| 728 | er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer); | ||
| 729 | ExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName); | ||
| 730 | |||
| 731 | LExit: | ||
| 732 | return hr; | ||
| 733 | } | ||
| 734 | |||
| 735 | |||
| 736 | /******************************************************************** | ||
| 737 | RegWriteString - writes a registry key value as a string. | ||
| 738 | |||
| 739 | Note: if wzValue is NULL the value will be removed. | ||
| 740 | *********************************************************************/ | ||
| 741 | extern "C" HRESULT DAPI RegWriteString( | ||
| 742 | __in HKEY hk, | ||
| 743 | __in_z_opt LPCWSTR wzName, | ||
| 744 | __in_z_opt LPCWSTR wzValue | ||
| 745 | ) | ||
| 746 | { | ||
| 747 | HRESULT hr = S_OK; | ||
| 748 | DWORD er = ERROR_SUCCESS; | ||
| 749 | DWORD cbValue = 0; | ||
| 750 | |||
| 751 | if (wzValue) | ||
| 752 | { | ||
| 753 | hr = ::StringCbLengthW(wzValue, DWORD_MAX, reinterpret_cast<size_t*>(&cbValue)); | ||
| 754 | ExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); | ||
| 755 | |||
| 756 | er = vpfnRegSetValueExW(hk, wzName, 0, REG_SZ, reinterpret_cast<const BYTE *>(wzValue), cbValue); | ||
| 757 | ExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); | ||
| 758 | } | ||
| 759 | else | ||
| 760 | { | ||
| 761 | er = vpfnRegDeleteValueW(hk, wzName); | ||
| 762 | if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) | ||
| 763 | { | ||
| 764 | er = ERROR_SUCCESS; | ||
| 765 | } | ||
| 766 | ExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName); | ||
| 767 | } | ||
| 768 | |||
| 769 | LExit: | ||
| 770 | return hr; | ||
| 771 | } | ||
| 772 | |||
| 773 | |||
| 774 | /******************************************************************** | ||
| 775 | RegWriteStringFormatted - writes a registry key value as a formatted string. | ||
| 776 | |||
| 777 | *********************************************************************/ | ||
| 778 | extern "C" HRESULT DAPI RegWriteStringFormatted( | ||
| 779 | __in HKEY hk, | ||
| 780 | __in_z_opt LPCWSTR wzName, | ||
| 781 | __in __format_string LPCWSTR szFormat, | ||
| 782 | ... | ||
| 783 | ) | ||
| 784 | { | ||
| 785 | HRESULT hr = S_OK; | ||
| 786 | LPWSTR sczValue = NULL; | ||
| 787 | va_list args; | ||
| 788 | |||
| 789 | va_start(args, szFormat); | ||
| 790 | hr = StrAllocFormattedArgs(&sczValue, szFormat, args); | ||
| 791 | va_end(args); | ||
| 792 | ExitOnFailure(hr, "Failed to allocate %ls value.", wzName); | ||
| 793 | |||
| 794 | hr = RegWriteString(hk, wzName, sczValue); | ||
| 795 | |||
| 796 | LExit: | ||
| 797 | ReleaseStr(sczValue); | ||
| 798 | |||
| 799 | return hr; | ||
| 800 | } | ||
| 801 | |||
| 802 | |||
| 803 | /******************************************************************** | ||
| 804 | RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value | ||
| 805 | |||
| 806 | *********************************************************************/ | ||
| 807 | HRESULT DAPI RegWriteStringArray( | ||
| 808 | __in HKEY hk, | ||
| 809 | __in_z_opt LPCWSTR wzName, | ||
| 810 | __in_ecount(cValues) LPWSTR *rgwzValues, | ||
| 811 | __in DWORD cValues | ||
| 812 | ) | ||
| 813 | { | ||
| 814 | HRESULT hr = S_OK; | ||
| 815 | DWORD er = ERROR_SUCCESS; | ||
| 816 | LPWSTR wzCopyDestination = NULL; | ||
| 817 | LPCWSTR wzWriteValue = NULL; | ||
| 818 | LPWSTR sczWriteValue = NULL; | ||
| 819 | DWORD dwTotalStringSize = 0; | ||
| 820 | DWORD cbTotalStringSize = 0; | ||
| 821 | DWORD dwTemp = 0; | ||
| 822 | |||
| 823 | if (0 == cValues) | ||
| 824 | { | ||
| 825 | wzWriteValue = L"\0"; | ||
| 826 | } | ||
| 827 | else | ||
| 828 | { | ||
| 829 | // Add space for the null terminator | ||
| 830 | dwTotalStringSize = 1; | ||
| 831 | |||
| 832 | for (DWORD i = 0; i < cValues; ++i) | ||
| 833 | { | ||
| 834 | dwTemp = dwTotalStringSize; | ||
| 835 | hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize); | ||
| 836 | ExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ"); | ||
| 837 | } | ||
| 838 | |||
| 839 | hr = StrAlloc(&sczWriteValue, dwTotalStringSize); | ||
| 840 | ExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); | ||
| 841 | |||
| 842 | wzCopyDestination = sczWriteValue; | ||
| 843 | dwTemp = dwTotalStringSize; | ||
| 844 | for (DWORD i = 0; i < cValues; ++i) | ||
| 845 | { | ||
| 846 | hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); | ||
| 847 | ExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); | ||
| 848 | |||
| 849 | dwTemp -= lstrlenW(rgwzValues[i]) + 1; | ||
| 850 | wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; | ||
| 851 | } | ||
| 852 | |||
| 853 | wzWriteValue = sczWriteValue; | ||
| 854 | } | ||
| 855 | |||
| 856 | hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); | ||
| 857 | ExitOnFailure(hr, "Failed to get total string size in bytes"); | ||
| 858 | |||
| 859 | er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize); | ||
| 860 | ExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); | ||
| 861 | |||
| 862 | LExit: | ||
| 863 | ReleaseStr(sczWriteValue); | ||
| 864 | |||
| 865 | return hr; | ||
| 866 | } | ||
| 867 | |||
| 868 | /******************************************************************** | ||
| 869 | RegWriteNumber - writes a registry key value as a number. | ||
| 870 | |||
| 871 | *********************************************************************/ | ||
| 872 | extern "C" HRESULT DAPI RegWriteNumber( | ||
| 873 | __in HKEY hk, | ||
| 874 | __in_z_opt LPCWSTR wzName, | ||
| 875 | __in DWORD dwValue | ||
| 876 | ) | ||
| 877 | { | ||
| 878 | HRESULT hr = S_OK; | ||
| 879 | DWORD er = ERROR_SUCCESS; | ||
| 880 | |||
| 881 | er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast<const BYTE *>(&dwValue), sizeof(dwValue)); | ||
| 882 | ExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); | ||
| 883 | |||
| 884 | LExit: | ||
| 885 | return hr; | ||
| 886 | } | ||
| 887 | |||
| 888 | /******************************************************************** | ||
| 889 | RegWriteQword - writes a registry key value as a Qword. | ||
| 890 | |||
| 891 | *********************************************************************/ | ||
| 892 | extern "C" HRESULT DAPI RegWriteQword( | ||
| 893 | __in HKEY hk, | ||
| 894 | __in_z_opt LPCWSTR wzName, | ||
| 895 | __in DWORD64 qwValue | ||
| 896 | ) | ||
| 897 | { | ||
| 898 | HRESULT hr = S_OK; | ||
| 899 | DWORD er = ERROR_SUCCESS; | ||
| 900 | |||
| 901 | er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast<const BYTE *>(&qwValue), sizeof(qwValue)); | ||
| 902 | ExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName); | ||
| 903 | |||
| 904 | LExit: | ||
| 905 | return hr; | ||
| 906 | } | ||
| 907 | |||
| 908 | /******************************************************************** | ||
| 909 | RegQueryKey - queries the key for the number of subkeys and values. | ||
| 910 | |||
| 911 | *********************************************************************/ | ||
| 912 | extern "C" HRESULT DAPI RegQueryKey( | ||
| 913 | __in HKEY hk, | ||
| 914 | __out_opt DWORD* pcSubKeys, | ||
| 915 | __out_opt DWORD* pcValues | ||
| 916 | ) | ||
| 917 | { | ||
| 918 | HRESULT hr = S_OK; | ||
| 919 | DWORD er = ERROR_SUCCESS; | ||
| 920 | |||
| 921 | er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL); | ||
| 922 | ExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key."); | ||
| 923 | |||
| 924 | LExit: | ||
| 925 | return hr; | ||
| 926 | } | ||
