diff options
author | Rob Mensching <rob@firegiant.com> | 2017-09-03 11:22:38 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2017-09-03 13:33:33 -0700 |
commit | 5d8375007754101ff2889d0e79486c8f9b7cf5ab (patch) | |
tree | a76d6fb6a38dd9f04a93ffcfd9d64e76779b3414 /src/dutil/regutil.cpp | |
parent | 8e8da6dbc051ec884b5d439bb4f44dc027d05bbf (diff) | |
download | wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.gz wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.bz2 wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.zip |
Initial commit
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 | } | ||