main
cpp 1,165 lines 32.5 KB
Raw
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
6 // Exit macros
7 #define RegExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
8 #define RegExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(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__)
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__)
13 #define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
14 #define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__)
15 #define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__)
16 #define RegExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__)
17 #define RegExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__)
18 #define RegExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REGUTIL, e, x, s, __VA_ARGS__)
19 #define RegExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REGUTIL, g, x, s, __VA_ARGS__)
20 #define RegExitOnPathFailure(x, b, s, ...) ExitOnPathFailureSource(DUTIL_SOURCE_REGUTIL, x, b, s, __VA_ARGS__)
21
22 static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW;
23 static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW;
24 static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL;
25 static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL;
26 static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW;
27 static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW;
28 static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW;
29 static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW;
30 static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW;
31 static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW;
32 static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW;
33 static PFN_REGGETVALUEW vpfnRegGetValueW = NULL;
34 static PFN_REGGETVALUEW vpfnRegGetValueWFromLibrary = NULL;
35
36 static HMODULE vhAdvApi32Dll = NULL;
37 static BOOL vfRegInitialized = FALSE;
38
39 static HRESULT GetRegValue(
40 __in HKEY hk,
41 __in_z_opt LPCWSTR wzName,
42 __deref_out_bcount_opt(*pcbBuffer) BYTE* pbBuffer,
43 __inout SIZE_T* pcbBuffer,
44 __out DWORD* pdwType
45 );
46 static HRESULT WriteStringToRegistry(
47 __in HKEY hk,
48 __in_z_opt LPCWSTR wzName,
49 __in_z_opt LPCWSTR wzValue,
50 __in DWORD dwType
51 );
52
53 DAPI_(HRESULT) RegInitialize()
54 {
55 HRESULT hr = S_OK;
56
57 hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll);
58 RegExitOnFailure(hr, "Failed to load AdvApi32.dll");
59
60 // Ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW.
61 vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW"));
62
63 // Ignore failures - if this doesn't exist, we'll fall back to RegQueryValueExW.
64 vpfnRegGetValueWFromLibrary = reinterpret_cast<PFN_REGGETVALUEW>(::GetProcAddress(vhAdvApi32Dll, "RegGetValueW"));
65
66 if (!vpfnRegDeleteKeyExW)
67 {
68 vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary;
69 }
70
71 if (!vpfnRegGetValueW)
72 {
73 vpfnRegGetValueW = vpfnRegGetValueWFromLibrary;
74 }
75
76 vfRegInitialized = TRUE;
77
78 LExit:
79 return hr;
80 }
81
82
83 DAPI_(void) RegUninitialize()
84 {
85 if (vhAdvApi32Dll)
86 {
87 ::FreeLibrary(vhAdvApi32Dll);
88 vhAdvApi32Dll = NULL;
89 vpfnRegDeleteKeyExWFromLibrary = NULL;
90 vpfnRegGetValueWFromLibrary = NULL;
91 vpfnRegDeleteKeyExW = NULL;
92 vpfnRegGetValueW = NULL;
93 }
94
95 vfRegInitialized = FALSE;
96 }
97
98
99 DAPI_(void) RegFunctionOverride(
100 __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW,
101 __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW,
102 __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW,
103 __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW,
104 __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW,
105 __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
106 __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
107 __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
108 __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW,
109 __in_opt PFN_REGGETVALUEW pfnRegGetValueW
110 )
111 {
112 vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW;
113 vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW;
114 vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary;
115 vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW;
116 vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW;
117 vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW;
118 vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW;
119 vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW;
120 vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW;
121 vpfnRegGetValueW = pfnRegGetValueW ? pfnRegGetValueW : vpfnRegGetValueWFromLibrary;
122 }
123
124
125 DAPI_(void) RegFunctionForceFallback()
126 {
127 vpfnRegDeleteKeyExW = NULL;
128 vpfnRegGetValueW = NULL;
129 }
130
131
132 DAPI_(HRESULT) RegCreate(
133 __in HKEY hkRoot,
134 __in_z LPCWSTR wzSubKey,
135 __in DWORD dwAccess,
136 __out HKEY* phk
137 )
138 {
139 HRESULT hr = S_OK;
140
141 hr = RegCreateEx(hkRoot, wzSubKey, dwAccess, REG_KEY_DEFAULT, FALSE, NULL, phk, NULL);
142 RegExitOnFailure(hr, "Failed to create registry key.");
143
144 LExit:
145 return hr;
146 }
147
148
149 DAPI_(HRESULT) RegCreateEx(
150 __in HKEY hkRoot,
151 __in_z LPCWSTR wzSubKey,
152 __in DWORD dwAccess,
153 __in REG_KEY_BITNESS kbKeyBitness,
154 __in BOOL fVolatile,
155 __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes,
156 __out HKEY* phk,
157 __out_opt BOOL* pfCreated
158 )
159 {
160 HRESULT hr = S_OK;
161 DWORD er = ERROR_SUCCESS;
162 DWORD dwDisposition;
163
164 REGSAM samDesired = RegTranslateKeyBitness(kbKeyBitness);
165 er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess | samDesired, pSecurityAttributes, phk, &dwDisposition);
166 RegExitOnWin32Error(er, hr, "Failed to create registry key.");
167
168 if (pfCreated)
169 {
170 *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition);
171 }
172
173 LExit:
174 return hr;
175 }
176
177
178 DAPI_(HRESULT) RegOpen(
179 __in HKEY hkRoot,
180 __in_z LPCWSTR wzSubKey,
181 __in DWORD dwAccess,
182 __out HKEY* phk
183 )
184 {
185 return RegOpenEx(hkRoot, wzSubKey, dwAccess, REG_KEY_DEFAULT, phk);
186 }
187
188
189 DAPI_(HRESULT) RegOpenEx(
190 __in HKEY hkRoot,
191 __in_z LPCWSTR wzSubKey,
192 __in DWORD dwAccess,
193 __in REG_KEY_BITNESS kbKeyBitness,
194 __out HKEY* phk
195 )
196 {
197 HRESULT hr = S_OK;
198 DWORD er = ERROR_SUCCESS;
199
200 REGSAM samDesired = RegTranslateKeyBitness(kbKeyBitness);
201 er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess | samDesired, phk);
202 if (ERROR_PATH_NOT_FOUND == er || ERROR_FILE_NOT_FOUND == er)
203 {
204 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
205 }
206 RegExitOnWin32Error(er, hr, "Failed to open registry key, root: %x, subkey: %ls.", hkRoot, wzSubKey);
207
208 LExit:
209 return hr;
210 }
211
212
213 DAPI_(HRESULT) RegDelete(
214 __in HKEY hkRoot,
215 __in_z LPCWSTR wzSubKey,
216 __in REG_KEY_BITNESS kbKeyBitness,
217 __in BOOL fDeleteTree
218 )
219 {
220 HRESULT hr = S_OK;
221 DWORD er = ERROR_SUCCESS;
222 LPWSTR pszEnumeratedSubKey = NULL;
223 LPWSTR pszRecursiveSubKey = NULL;
224 HKEY hkKey = NULL;
225 BOOL fExists = FALSE;
226
227 if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness)
228 {
229 hr = E_INVALIDARG;
230 RegExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!");
231 }
232
233 if (fDeleteTree)
234 {
235 hr = RegOpenEx(hkRoot, wzSubKey, KEY_READ, kbKeyBitness, &hkKey);
236 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
237 {
238 ExitFunction();
239 }
240 RegExitOnFailure(hr, "Failed to open this key for enumerating subkeys: %ls", wzSubKey);
241
242 // Yes, keep enumerating the 0th item, because we're deleting it every time
243 while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey)))
244 {
245 RegExitOnFailure(hr, "Failed to enumerate key 0");
246
247 hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey);
248 RegExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey);
249
250 hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree);
251 RegExitOnPathFailure(hr, fExists, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey);
252 }
253
254 hr = S_OK;
255
256 // Release the handle to make sure it's deleted immediately.
257 ReleaseRegKey(hkKey);
258 }
259
260 if (NULL != vpfnRegDeleteKeyExW)
261 {
262 REGSAM samDesired = RegTranslateKeyBitness(kbKeyBitness);
263 er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0);
264 if (ERROR_PATH_NOT_FOUND == er || ERROR_FILE_NOT_FOUND == er)
265 {
266 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
267 }
268 RegExitOnWin32Error(er, hr, "Failed to delete registry key (ex).");
269 }
270 else
271 {
272 er = vpfnRegDeleteKeyW(hkRoot, wzSubKey);
273 if (ERROR_PATH_NOT_FOUND == er || ERROR_FILE_NOT_FOUND == er)
274 {
275 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
276 }
277 RegExitOnWin32Error(er, hr, "Failed to delete registry key.");
278 }
279
280 LExit:
281 ReleaseRegKey(hkKey);
282 ReleaseStr(pszEnumeratedSubKey);
283 ReleaseStr(pszRecursiveSubKey);
284
285 return hr;
286 }
287
288 DAPI_(HRESULT) RegKeyEnum(
289 __in HKEY hk,
290 __in DWORD dwIndex,
291 __deref_out_z LPWSTR* psczKey
292 )
293 {
294 HRESULT hr = S_OK;
295 DWORD er = ERROR_SUCCESS;
296 SIZE_T cb = 0;
297 DWORD cch = 0;
298
299 if (psczKey && *psczKey)
300 {
301 hr = StrMaxLength(*psczKey, &cb);
302 RegExitOnFailure(hr, "Failed to determine length of string.");
303
304 cch = (DWORD)min(DWORD_MAX, cb);
305 }
306
307 if (2 > cch)
308 {
309 cch = 2;
310
311 hr = StrAlloc(psczKey, cch);
312 RegExitOnFailure(hr, "Failed to allocate string to minimum size.");
313 }
314
315 er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL);
316 if (ERROR_MORE_DATA == er)
317 {
318 er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL);
319 RegExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key.");
320
321 ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator.
322 hr = StrAlloc(psczKey, cch);
323 RegExitOnFailure(hr, "Failed to allocate string bigger for enum registry key.");
324
325 er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL);
326 }
327 else if (ERROR_NO_MORE_ITEMS == er)
328 {
329 ExitFunction1(hr = E_NOMOREITEMS);
330 }
331 RegExitOnWin32Error(er, hr, "Failed to enum registry key.");
332
333 // Always ensure the registry key name is null terminated.
334 #pragma prefast(push)
335 #pragma prefast(disable:26018)
336 (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works.
337 #pragma prefast(pop)
338
339 LExit:
340 return hr;
341 }
342
343
344 DAPI_(HRESULT) RegValueEnum(
345 __in HKEY hk,
346 __in DWORD dwIndex,
347 __deref_out_z LPWSTR* psczName,
348 __out_opt DWORD *pdwType
349 )
350 {
351 HRESULT hr = S_OK;
352 DWORD er = ERROR_SUCCESS;
353 DWORD cbValueName = 0;
354
355 er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL);
356 RegExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key.");
357
358 // Add one for null terminator
359 ++cbValueName;
360
361 hr = StrAlloc(psczName, cbValueName);
362 RegExitOnFailure(hr, "Failed to allocate array for registry value name");
363
364 er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL);
365 if (ERROR_NO_MORE_ITEMS == er)
366 {
367 ExitFunction1(hr = E_NOMOREITEMS);
368 }
369 RegExitOnWin32Error(er, hr, "Failed to enumerate registry value");
370
371 LExit:
372 return hr;
373 }
374
375 DAPI_(HRESULT) RegGetType(
376 __in HKEY hk,
377 __in_z_opt LPCWSTR wzName,
378 __out DWORD *pdwType
379 )
380 {
381 HRESULT hr = S_OK;
382 DWORD er = ERROR_SUCCESS;
383
384 er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL);
385 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
386 {
387 ExitFunction1(hr = E_FILENOTFOUND);
388 }
389 RegExitOnWin32Error(er, hr, "Failed to read registry value.");
390 LExit:
391
392 return hr;
393 }
394
395 DAPI_(HRESULT) RegReadValue(
396 __in HKEY hk,
397 __in_z_opt LPCWSTR wzName,
398 __in BOOL fExpand,
399 __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
400 __inout SIZE_T* pcbBuffer,
401 __out DWORD* pdwType
402 )
403 {
404 HRESULT hr = S_OK;
405 DWORD dwAttempts = 0;
406 LPWSTR sczExpand = NULL;
407
408 hr = GetRegValue(hk, wzName, *ppbBuffer, pcbBuffer, pdwType);
409 if (E_FILENOTFOUND == hr)
410 {
411 ExitFunction();
412 }
413 else if (E_MOREDATA != hr)
414 {
415 RegExitOnFailure(hr, "Failed to get size of raw registry value.");
416
417 // Zero-length raw values can exist
418 if (!*ppbBuffer && 0 < *pcbBuffer)
419 {
420 hr = E_MOREDATA;
421 }
422 }
423
424 while (E_MOREDATA == hr && dwAttempts < 10)
425 {
426 ++dwAttempts;
427
428 if (*ppbBuffer)
429 {
430 *ppbBuffer = static_cast<LPBYTE>(MemReAlloc(*ppbBuffer, *pcbBuffer, FALSE));
431 }
432 else
433 {
434 *ppbBuffer = static_cast<LPBYTE>(MemAlloc(*pcbBuffer, FALSE));
435 }
436 RegExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for raw registry value.");
437
438 hr = GetRegValue(hk, wzName, *ppbBuffer, pcbBuffer, pdwType);
439 if (E_FILENOTFOUND == hr)
440 {
441 ExitFunction();
442 }
443 else if (E_MOREDATA != hr)
444 {
445 RegExitOnFailure(hr, "Failed to read raw registry value.");
446 }
447 }
448
449 if (fExpand && SUCCEEDED(hr) && REG_EXPAND_SZ == *pdwType)
450 {
451 LPWSTR sczValue = reinterpret_cast<LPWSTR>(*ppbBuffer);
452 hr = EnvExpandEnvironmentStrings(sczValue, &sczExpand, NULL);
453 RegExitOnFailure(hr, "Failed to expand registry value: %ls", sczValue);
454
455 *ppbBuffer = reinterpret_cast<LPBYTE>(sczExpand);
456 *pcbBuffer = (lstrlenW(sczExpand) + 1) * sizeof(WCHAR);
457 sczExpand = NULL;
458 ReleaseMem(sczValue);
459 }
460
461 LExit:
462 ReleaseStr(sczExpand);
463
464 return hr;
465 }
466
467 DAPI_(HRESULT) RegReadBinary(
468 __in HKEY hk,
469 __in_z_opt LPCWSTR wzName,
470 __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
471 __out SIZE_T* pcbBuffer
472 )
473 {
474 HRESULT hr = S_OK;
475 DWORD dwType = 0;
476
477 hr = RegReadValue(hk, wzName, FALSE, ppbBuffer, pcbBuffer, &dwType);
478 if (E_FILENOTFOUND == hr)
479 {
480 ExitFunction();
481 }
482 RegExitOnFailure(hr, "Failed to read binary registry value.");
483
484 if (REG_BINARY != dwType)
485 {
486 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading binary registry value due to unexpected data type: %u", dwType);
487 }
488
489 LExit:
490 return hr;
491 }
492
493
494 DAPI_(HRESULT) RegReadString(
495 __in HKEY hk,
496 __in_z_opt LPCWSTR wzName,
497 __deref_out_z LPWSTR* psczValue
498 )
499 {
500 HRESULT hr = S_OK;
501 SIZE_T cbValue = 0;
502 DWORD dwType = 0;
503
504 if (psczValue && *psczValue)
505 {
506 hr = MemSizeChecked(*psczValue, &cbValue);
507 RegExitOnFailure(hr, "Failed to get size of input buffer.");
508 }
509
510 hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(psczValue), &cbValue, &dwType);
511 if (E_FILENOTFOUND == hr)
512 {
513 ExitFunction();
514 }
515 RegExitOnFailure(hr, "Failed to read string registry value.");
516
517 if (REG_EXPAND_SZ != dwType && REG_SZ != dwType)
518 {
519 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading string registry value due to unexpected data type: %u", dwType);
520 }
521
522 LExit:
523 return hr;
524 }
525
526
527 DAPI_(HRESULT) RegReadUnexpandedString(
528 __in HKEY hk,
529 __in_z_opt LPCWSTR wzName,
530 __inout BOOL* pfNeedsExpansion,
531 __deref_out_z LPWSTR* psczValue
532 )
533 {
534 HRESULT hr = S_OK;
535 SIZE_T cbValue = 0;
536 DWORD dwType = 0;
537 LPWSTR sczExpand = NULL;
538
539 if (psczValue && *psczValue)
540 {
541 hr = MemSizeChecked(*psczValue, &cbValue);
542 RegExitOnFailure(hr, "Failed to get size of input buffer.");
543 }
544
545 hr = RegReadValue(hk, wzName, FALSE, reinterpret_cast<LPBYTE*>(psczValue), &cbValue, &dwType);
546 if (E_FILENOTFOUND == hr)
547 {
548 ExitFunction();
549 }
550 RegExitOnFailure(hr, "Failed to read expand string registry value.");
551
552 if (REG_EXPAND_SZ != dwType && REG_SZ != dwType)
553 {
554 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading expand string registry value due to unexpected data type: %u", dwType);
555 }
556
557 *pfNeedsExpansion = REG_EXPAND_SZ == dwType;
558
559 LExit:
560 ReleaseStr(sczExpand);
561
562 return hr;
563 }
564
565
566 DAPI_(HRESULT) RegReadStringArray(
567 __in HKEY hk,
568 __in_z_opt LPCWSTR wzName,
569 __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings,
570 __out DWORD *pcStrings
571 )
572 {
573 HRESULT hr = S_OK;
574 DWORD dwNullCharacters = 0;
575 DWORD dwType = 0;
576 SIZE_T cb = 0;
577 SIZE_T cch = 0;
578 LPCWSTR wzSource = NULL;
579 LPWSTR sczValue = NULL;
580
581 hr = RegReadValue(hk, wzName, FALSE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
582 if (E_FILENOTFOUND == hr)
583 {
584 ExitFunction();
585 }
586 RegExitOnFailure(hr, "Failed to read string array registry value.");
587
588 if (REG_MULTI_SZ != dwType)
589 {
590 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Tried to read string array, but registry value %ls is of an incorrect type", wzName);
591 }
592
593 cch = cb / sizeof(WCHAR);
594
595 for (DWORD i = 0; i < cch; ++i)
596 {
597 if (L'\0' == sczValue[i])
598 {
599 ++dwNullCharacters;
600 }
601 }
602
603 // Value exists, but is empty, so no strings to return.
604 if (!cb || 1 == dwNullCharacters && 1 == cch || 2 == dwNullCharacters && 2 == cch)
605 {
606 *prgsczStrings = NULL;
607 *pcStrings = 0;
608 ExitFunction1(hr = S_OK);
609 }
610
611 if (sczValue[cch - 1] != L'\0')
612 {
613 // Count the terminating null character that RegReadValue added past the end.
614 ++dwNullCharacters;
615 Assert(!sczValue[cch]);
616 }
617 else if (cch > 1 && sczValue[cch - 2] == L'\0')
618 {
619 // Don't count the extra 1 at the end of the properly double-null terminated string.
620 --dwNullCharacters;
621 }
622
623 // There's one string for every null character encountered.
624 *pcStrings = dwNullCharacters;
625 hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0);
626 RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value");
627
628 #pragma prefast(push)
629 #pragma prefast(disable:26010)
630 wzSource = sczValue;
631 for (DWORD i = 0; i < *pcStrings; ++i)
632 {
633 cch = lstrlenW(wzSource);
634
635 hr = StrAllocString(&(*prgsczStrings)[i], wzSource, cch);
636 RegExitOnFailure(hr, "Failed to allocate copy of string");
637
638 // Skip past this string
639 wzSource += cch + 1;
640 }
641 #pragma prefast(pop)
642
643 LExit:
644 ReleaseStr(sczValue);
645
646 return hr;
647 }
648
649
650 DAPI_(HRESULT) RegReadVersion(
651 __in HKEY hk,
652 __in_z_opt LPCWSTR wzName,
653 __out DWORD64* pdw64Version
654 )
655 {
656 HRESULT hr = S_OK;
657 DWORD dwType = 0;
658 SIZE_T cb = 0;
659 LPWSTR sczValue = NULL;
660
661 hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
662 if (E_FILENOTFOUND == hr)
663 {
664 ExitFunction();
665 }
666 RegExitOnFailure(hr, "Failed to read version registry value.");
667
668 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
669 {
670 hr = FileVersionFromStringEx(sczValue, 0, pdw64Version);
671 RegExitOnFailure(hr, "Failed to convert registry string to version.");
672 }
673 else if (REG_QWORD == dwType)
674 {
675 if (memcpy_s(pdw64Version, sizeof(DWORD64), sczValue, cb))
676 {
677 RegExitWithRootFailure(hr, E_INVALIDARG, "Failed to copy QWORD version value.");
678 }
679 }
680 else // unexpected data type
681 {
682 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading version registry value due to unexpected data type: %u", dwType);
683 }
684
685 LExit:
686 ReleaseStr(sczValue);
687
688 return hr;
689 }
690
691
692 DAPI_(HRESULT) RegReadWixVersion(
693 __in HKEY hk,
694 __in_z_opt LPCWSTR wzName,
695 __out VERUTIL_VERSION** ppVersion
696 )
697 {
698 HRESULT hr = S_OK;
699 DWORD dwType = 0;
700 SIZE_T cb = 0;
701 DWORD64 dw64Version = 0;
702 LPWSTR sczValue = NULL;
703 VERUTIL_VERSION* pVersion = NULL;
704
705 hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
706 if (E_FILENOTFOUND == hr)
707 {
708 ExitFunction();
709 }
710 RegExitOnFailure(hr, "Failed to read wix version registry value.");
711
712 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
713 {
714 hr = VerParseVersion(sczValue, 0, FALSE, &pVersion);
715 RegExitOnFailure(hr, "Failed to convert registry string to wix version.");
716 }
717 else if (REG_QWORD == dwType)
718 {
719 if (memcpy_s(&dw64Version, sizeof(DWORD64), sczValue, cb))
720 {
721 RegExitWithRootFailure(hr, E_INVALIDARG, "Failed to copy QWORD wix version value.");
722 }
723
724 hr = VerVersionFromQword(dw64Version, &pVersion);
725 RegExitOnFailure(hr, "Failed to convert registry string to wix version.");
726 }
727 else // unexpected data type
728 {
729 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
730 RegExitOnRootFailure(hr, "Error reading wix version registry value due to unexpected data type: %u", dwType);
731 }
732
733 *ppVersion = pVersion;
734 pVersion = NULL;
735
736 LExit:
737 ReleaseVerutilVersion(pVersion);
738 ReleaseStr(sczValue);
739
740 return hr;
741 }
742
743
744 DAPI_(HRESULT) RegReadNone(
745 __in HKEY hk,
746 __in_z_opt LPCWSTR wzName
747 )
748 {
749 HRESULT hr = S_OK;
750 DWORD dwType = 0;
751
752 hr = RegGetType(hk, wzName, &dwType);
753 if (E_FILENOTFOUND == hr)
754 {
755 ExitFunction();
756 }
757 RegExitOnFailure(hr, "Error reading none registry value type.");
758
759 if (REG_NONE != dwType)
760 {
761 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading none registry value due to unexpected data type: %u", dwType);
762 }
763
764 LExit:
765 return hr;
766 }
767
768 DAPI_(HRESULT) RegReadNumber(
769 __in HKEY hk,
770 __in_z_opt LPCWSTR wzName,
771 __out DWORD* pdwValue
772 )
773 {
774 HRESULT hr = S_OK;
775 DWORD er = ERROR_SUCCESS;
776 DWORD dwType = 0;
777 DWORD cb = sizeof(DWORD);
778
779 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(pdwValue), &cb);
780 if (ERROR_FILE_NOT_FOUND == er)
781 {
782 ExitFunction1(hr = E_FILENOTFOUND);
783 }
784 RegExitOnWin32Error(er, hr, "Failed to query registry key value.");
785
786 if (REG_DWORD != dwType)
787 {
788 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
789 RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
790 }
791
792 LExit:
793 return hr;
794 }
795
796
797 DAPI_(HRESULT) RegReadQword(
798 __in HKEY hk,
799 __in_z_opt LPCWSTR wzName,
800 __out DWORD64* pqwValue
801 )
802 {
803 HRESULT hr = S_OK;
804 DWORD er = ERROR_SUCCESS;
805 DWORD dwType = 0;
806 DWORD cb = sizeof(DWORD64);
807
808 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(pqwValue), &cb);
809 if (ERROR_FILE_NOT_FOUND == er)
810 {
811 ExitFunction1(hr = E_FILENOTFOUND);
812 }
813 RegExitOnWin32Error(er, hr, "Failed to query registry key value.");
814
815 if (REG_QWORD != dwType)
816 {
817 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
818 RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
819 }
820
821 LExit:
822 return hr;
823 }
824
825
826 DAPI_(HRESULT) RegWriteBinary(
827 __in HKEY hk,
828 __in_z_opt LPCWSTR wzName,
829 __in_bcount(cbBuffer) const BYTE *pbBuffer,
830 __in DWORD cbBuffer
831 )
832 {
833 HRESULT hr = S_OK;
834 DWORD er = ERROR_SUCCESS;
835
836 er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer);
837 RegExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName);
838
839 LExit:
840 return hr;
841 }
842
843
844 DAPI_(HRESULT) RegWriteExpandString(
845 __in HKEY hk,
846 __in_z_opt LPCWSTR wzName,
847 __in_z_opt LPCWSTR wzValue
848 )
849 {
850 return WriteStringToRegistry(hk, wzName, wzValue, REG_EXPAND_SZ);
851 }
852
853
854 DAPI_(HRESULT) RegWriteString(
855 __in HKEY hk,
856 __in_z_opt LPCWSTR wzName,
857 __in_z_opt LPCWSTR wzValue
858 )
859 {
860 return WriteStringToRegistry(hk, wzName, wzValue, REG_SZ);
861 }
862
863
864 DAPIV_(HRESULT) RegWriteStringFormatted(
865 __in HKEY hk,
866 __in_z_opt LPCWSTR wzName,
867 __in __format_string LPCWSTR szFormat,
868 ...
869 )
870 {
871 HRESULT hr = S_OK;
872 LPWSTR sczValue = NULL;
873 va_list args;
874
875 va_start(args, szFormat);
876 hr = StrAllocFormattedArgs(&sczValue, szFormat, args);
877 va_end(args);
878 RegExitOnFailure(hr, "Failed to allocate %ls value.", wzName);
879
880 hr = WriteStringToRegistry(hk, wzName, sczValue, REG_SZ);
881
882 LExit:
883 ReleaseStr(sczValue);
884
885 return hr;
886 }
887
888
889 DAPI_(HRESULT) RegWriteStringArray(
890 __in HKEY hk,
891 __in_z_opt LPCWSTR wzName,
892 __in_ecount(cValues) LPWSTR *rgwzValues,
893 __in DWORD cValues
894 )
895 {
896 HRESULT hr = S_OK;
897 DWORD er = ERROR_SUCCESS;
898 LPWSTR wzCopyDestination = NULL;
899 LPCWSTR wzWriteValue = NULL;
900 LPWSTR sczWriteValue = NULL;
901 DWORD dwTotalStringSize = 0;
902 DWORD cbTotalStringSize = 0;
903 DWORD dwTemp = 0;
904 DWORD dwRemainingStringSize = 0;
905
906 if (cValues)
907 {
908 // Add space for the null terminator
909 dwTotalStringSize = 1;
910
911 for (DWORD i = 0; i < cValues; ++i)
912 {
913 dwTemp = dwTotalStringSize;
914 hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize);
915 RegExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ");
916 }
917
918 hr = StrAlloc(&sczWriteValue, dwTotalStringSize);
919 RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ");
920
921 wzCopyDestination = sczWriteValue;
922 dwRemainingStringSize = dwTotalStringSize;
923 for (DWORD i = 0; i < cValues; ++i)
924 {
925 hr = ::StringCchCopyW(wzCopyDestination, dwRemainingStringSize, rgwzValues[i]);
926 RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]);
927
928 dwTemp = lstrlenW(rgwzValues[i]) + 1;
929 dwRemainingStringSize -= dwTemp;
930 wzCopyDestination += dwTemp;
931 }
932
933 wzWriteValue = sczWriteValue;
934
935 hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize);
936 RegExitOnFailure(hr, "Failed to get total string size in bytes");
937 }
938
939 er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize);
940 RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue);
941
942 LExit:
943 ReleaseStr(sczWriteValue);
944
945 return hr;
946 }
947
948 DAPI_(HRESULT) RegWriteNone(
949 __in HKEY hk,
950 __in_z_opt LPCWSTR wzName
951 )
952 {
953 HRESULT hr = S_OK;
954 DWORD er = ERROR_SUCCESS;
955
956 er = vpfnRegSetValueExW(hk, wzName, 0, REG_NONE, NULL, NULL);
957 RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName);
958
959 LExit:
960 return hr;
961 }
962
963 DAPI_(HRESULT) RegWriteNumber(
964 __in HKEY hk,
965 __in_z_opt LPCWSTR wzName,
966 __in DWORD dwValue
967 )
968 {
969 HRESULT hr = S_OK;
970 DWORD er = ERROR_SUCCESS;
971
972 er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast<const BYTE *>(&dwValue), sizeof(dwValue));
973 RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName);
974
975 LExit:
976 return hr;
977 }
978
979 DAPI_(HRESULT) RegWriteQword(
980 __in HKEY hk,
981 __in_z_opt LPCWSTR wzName,
982 __in DWORD64 qwValue
983 )
984 {
985 HRESULT hr = S_OK;
986 DWORD er = ERROR_SUCCESS;
987
988 er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast<const BYTE *>(&qwValue), sizeof(qwValue));
989 RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName);
990
991 LExit:
992 return hr;
993 }
994
995 DAPI_(HRESULT) RegQueryKey(
996 __in HKEY hk,
997 __out_opt DWORD* pcSubKeys,
998 __out_opt DWORD* pcValues
999 )
1000 {
1001 HRESULT hr = S_OK;
1002 DWORD er = ERROR_SUCCESS;
1003
1004 er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL);
1005 RegExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key.");
1006
1007 LExit:
1008 return hr;
1009 }
1010
1011 DAPI_(HRESULT) RegKeyReadNumber(
1012 __in HKEY hk,
1013 __in_z LPCWSTR wzSubKey,
1014 __in_z_opt LPCWSTR wzName,
1015 __in REG_KEY_BITNESS kbKeyBitness,
1016 __out DWORD* pdwValue
1017 )
1018 {
1019 HRESULT hr = S_OK;
1020 HKEY hkKey = NULL;
1021
1022 hr = RegOpenEx(hk, wzSubKey, KEY_READ, kbKeyBitness, &hkKey);
1023 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1024 {
1025 ExitFunction();
1026 }
1027 RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey);
1028
1029 hr = RegReadNumber(hkKey, wzName, pdwValue);
1030 if (E_FILENOTFOUND == hr)
1031 {
1032 ExitFunction();
1033 }
1034 RegExitOnFailure(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName);
1035
1036 LExit:
1037 ReleaseRegKey(hkKey);
1038
1039 return hr;
1040 }
1041
1042 DAPI_(BOOL) RegValueExists(
1043 __in HKEY hk,
1044 __in_z LPCWSTR wzSubKey,
1045 __in_z_opt LPCWSTR wzName,
1046 __in REG_KEY_BITNESS kbKeyBitness
1047 )
1048 {
1049 HRESULT hr = S_OK;
1050 HKEY hkKey = NULL;
1051 DWORD dwType = 0;
1052
1053 hr = RegOpenEx(hk, wzSubKey, KEY_READ, kbKeyBitness, &hkKey);
1054 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1055 {
1056 ExitFunction();
1057 }
1058 RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey);
1059
1060 hr = RegGetType(hkKey, wzName, &dwType);
1061 if (E_FILENOTFOUND == hr)
1062 {
1063 ExitFunction();
1064 }
1065 RegExitOnFailure(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName);
1066
1067 LExit:
1068 ReleaseRegKey(hkKey);
1069
1070 return SUCCEEDED(hr);
1071 }
1072
1073 DAPI_(REGSAM) RegTranslateKeyBitness(
1074 __in REG_KEY_BITNESS kbKeyBitness
1075 )
1076 {
1077 switch (kbKeyBitness)
1078 {
1079 case REG_KEY_32BIT:
1080 return KEY_WOW64_32KEY;
1081 case REG_KEY_64BIT:
1082 return KEY_WOW64_64KEY;
1083 case REG_KEY_DEFAULT:
1084 default:
1085 return 0;
1086 }
1087 }
1088
1089 static HRESULT GetRegValue(
1090 __in HKEY hk,
1091 __in_z_opt LPCWSTR wzName,
1092 __deref_out_bcount_opt(*pcbBuffer) BYTE* pbBuffer,
1093 __inout SIZE_T* pcbBuffer,
1094 __out DWORD* pdwType
1095 )
1096 {
1097 HRESULT hr = S_OK;
1098 DWORD cb = (DWORD)min(DWORD_MAX, *pcbBuffer);
1099
1100 if (vpfnRegGetValueW)
1101 {
1102 hr = HRESULT_FROM_WIN32(vpfnRegGetValueW(hk, NULL, wzName, RRF_RT_ANY | RRF_NOEXPAND, pdwType, pbBuffer, &cb));
1103 }
1104 else
1105 {
1106 hr = HRESULT_FROM_WIN32(vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, pbBuffer, &cb));
1107
1108 if (REG_SZ == *pdwType || REG_EXPAND_SZ == *pdwType || REG_MULTI_SZ == *pdwType)
1109 {
1110 if (E_MOREDATA == hr || S_OK == hr && (cb + sizeof(WCHAR)) > *pcbBuffer)
1111 {
1112 // Make sure there's room for a null terminator at the end.
1113 HRESULT hrAdd = ::DWordAdd(cb, sizeof(WCHAR), &cb);
1114
1115 hr = FAILED(hrAdd) ? hrAdd : (pbBuffer ? E_MOREDATA : S_OK);
1116 }
1117 else if (S_OK == hr && pbBuffer)
1118 {
1119 // Always ensure the registry value is null terminated.
1120 WCHAR* pch = reinterpret_cast<WCHAR*>(pbBuffer + cb);
1121 *pch = L'\0';
1122 }
1123 }
1124 }
1125
1126 *pcbBuffer = cb;
1127
1128 return hr;
1129 }
1130
1131 static HRESULT WriteStringToRegistry(
1132 __in HKEY hk,
1133 __in_z_opt LPCWSTR wzName,
1134 __in_z_opt LPCWSTR wzValue,
1135 __in DWORD dwType
1136 )
1137 {
1138 HRESULT hr = S_OK;
1139 DWORD er = ERROR_SUCCESS;
1140 size_t cbValue = 0;
1141
1142 if (wzValue)
1143 {
1144 hr = ::StringCbLengthW(wzValue, DWORD_MAX, &cbValue);
1145 RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName);
1146
1147 // Need to include the null terminator.
1148 cbValue += sizeof(WCHAR);
1149
1150 er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast<const BYTE *>(wzValue), static_cast<DWORD>(cbValue));
1151 RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName);
1152 }
1153 else
1154 {
1155 er = vpfnRegDeleteValueW(hk, wzName);
1156 if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er)
1157 {
1158 er = ERROR_SUCCESS;
1159 }
1160 RegExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName);
1161 }
1162
1163 LExit:
1164 return hr;
1165 }