1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/win/registry.h"
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "base/win/windows_version.h"
20 // RegEnumValue() reports the number of characters from the name that were
21 // written to the buffer, not how many there are. This constant is the maximum
22 // name size, such that a buffer with this size should read any name.
23 const DWORD MAX_REGISTRY_NAME_SIZE
= 16384;
25 // Registry values are read as BYTE* but can have wchar_t* data whose last
26 // wchar_t is truncated. This function converts the reported |byte_size| to
27 // a size in wchar_t that can store a truncated wchar_t if necessary.
28 inline DWORD
to_wchar_size(DWORD byte_size
) {
29 return (byte_size
+ sizeof(wchar_t) - 1) / sizeof(wchar_t);
32 // Mask to pull WOW64 access flags out of REGSAM access.
33 const REGSAM kWow64AccessMask
= KEY_WOW64_32KEY
| KEY_WOW64_64KEY
;
37 // RegKey ----------------------------------------------------------------------
45 RegKey::RegKey(HKEY key
)
51 RegKey::RegKey(HKEY rootkey
, const wchar_t* subkey
, REGSAM access
)
56 if (access
& (KEY_SET_VALUE
| KEY_CREATE_SUB_KEY
| KEY_CREATE_LINK
))
57 Create(rootkey
, subkey
, access
);
59 Open(rootkey
, subkey
, access
);
62 wow64access_
= access
& kWow64AccessMask
;
70 LONG
RegKey::Create(HKEY rootkey
, const wchar_t* subkey
, REGSAM access
) {
71 DWORD disposition_value
;
72 return CreateWithDisposition(rootkey
, subkey
, &disposition_value
, access
);
75 LONG
RegKey::CreateWithDisposition(HKEY rootkey
, const wchar_t* subkey
,
76 DWORD
* disposition
, REGSAM access
) {
77 DCHECK(rootkey
&& subkey
&& access
&& disposition
);
79 LONG result
= RegCreateKeyEx(rootkey
, subkey
, 0, NULL
,
80 REG_OPTION_NON_VOLATILE
, access
, NULL
, &subhkey
,
82 if (result
== ERROR_SUCCESS
) {
85 wow64access_
= access
& kWow64AccessMask
;
91 LONG
RegKey::CreateKey(const wchar_t* name
, REGSAM access
) {
92 DCHECK(name
&& access
);
93 // After the application has accessed an alternate registry view using one of
94 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
95 // (create, delete, or open) on child registry keys must explicitly use the
96 // same flag. Otherwise, there can be unexpected behavior.
97 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
98 if ((access
& kWow64AccessMask
) != wow64access_
) {
100 return ERROR_INVALID_PARAMETER
;
103 LONG result
= RegCreateKeyEx(key_
, name
, 0, NULL
, REG_OPTION_NON_VOLATILE
,
104 access
, NULL
, &subkey
, NULL
);
105 if (result
== ERROR_SUCCESS
) {
108 wow64access_
= access
& kWow64AccessMask
;
114 LONG
RegKey::Open(HKEY rootkey
, const wchar_t* subkey
, REGSAM access
) {
115 DCHECK(rootkey
&& subkey
&& access
);
118 LONG result
= RegOpenKeyEx(rootkey
, subkey
, 0, access
, &subhkey
);
119 if (result
== ERROR_SUCCESS
) {
122 wow64access_
= access
& kWow64AccessMask
;
128 LONG
RegKey::OpenKey(const wchar_t* relative_key_name
, REGSAM access
) {
129 DCHECK(relative_key_name
&& access
);
130 // After the application has accessed an alternate registry view using one of
131 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
132 // (create, delete, or open) on child registry keys must explicitly use the
133 // same flag. Otherwise, there can be unexpected behavior.
134 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
135 if ((access
& kWow64AccessMask
) != wow64access_
) {
137 return ERROR_INVALID_PARAMETER
;
140 LONG result
= RegOpenKeyEx(key_
, relative_key_name
, 0, access
, &subkey
);
142 // We have to close the current opened key before replacing it with the new
144 if (result
== ERROR_SUCCESS
) {
147 wow64access_
= access
& kWow64AccessMask
;
152 void RegKey::Close() {
161 // TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
162 void RegKey::Set(HKEY key
) {
169 HKEY
RegKey::Take() {
170 DCHECK(wow64access_
== 0);
177 bool RegKey::HasValue(const wchar_t* name
) const {
178 return RegQueryValueEx(key_
, name
, 0, NULL
, NULL
, NULL
) == ERROR_SUCCESS
;
181 DWORD
RegKey::GetValueCount() const {
183 LONG result
= RegQueryInfoKey(key_
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, &count
,
184 NULL
, NULL
, NULL
, NULL
);
185 return (result
== ERROR_SUCCESS
) ? count
: 0;
188 LONG
RegKey::GetValueNameAt(int index
, std::wstring
* name
) const {
190 DWORD bufsize
= arraysize(buf
);
191 LONG r
= ::RegEnumValue(key_
, index
, buf
, &bufsize
, NULL
, NULL
, NULL
, NULL
);
192 if (r
== ERROR_SUCCESS
)
198 LONG
RegKey::DeleteKey(const wchar_t* name
) {
203 // Verify the key exists before attempting delete to replicate previous
206 RegOpenKeyEx(key_
, name
, 0, READ_CONTROL
| wow64access_
, &subkey
);
207 if (result
!= ERROR_SUCCESS
)
211 return RegDelRecurse(key_
, std::wstring(name
), wow64access_
);
214 LONG
RegKey::DeleteEmptyKey(const wchar_t* name
) {
218 HKEY target_key
= NULL
;
219 LONG result
= RegOpenKeyEx(key_
, name
, 0, KEY_READ
| wow64access_
,
222 if (result
!= ERROR_SUCCESS
)
226 result
= RegQueryInfoKey(target_key
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, &count
,
227 NULL
, NULL
, NULL
, NULL
);
229 RegCloseKey(target_key
);
231 if (result
!= ERROR_SUCCESS
)
235 return RegDeleteKeyExWrapper(key_
, name
, wow64access_
, 0);
237 return ERROR_DIR_NOT_EMPTY
;
240 LONG
RegKey::DeleteValue(const wchar_t* value_name
) {
242 LONG result
= RegDeleteValue(key_
, value_name
);
246 LONG
RegKey::ReadValueDW(const wchar_t* name
, DWORD
* out_value
) const {
248 DWORD type
= REG_DWORD
;
249 DWORD size
= sizeof(DWORD
);
250 DWORD local_value
= 0;
251 LONG result
= ReadValue(name
, &local_value
, &size
, &type
);
252 if (result
== ERROR_SUCCESS
) {
253 if ((type
== REG_DWORD
|| type
== REG_BINARY
) && size
== sizeof(DWORD
))
254 *out_value
= local_value
;
256 result
= ERROR_CANTREAD
;
262 LONG
RegKey::ReadInt64(const wchar_t* name
, int64
* out_value
) const {
264 DWORD type
= REG_QWORD
;
265 int64 local_value
= 0;
266 DWORD size
= sizeof(local_value
);
267 LONG result
= ReadValue(name
, &local_value
, &size
, &type
);
268 if (result
== ERROR_SUCCESS
) {
269 if ((type
== REG_QWORD
|| type
== REG_BINARY
) &&
270 size
== sizeof(local_value
))
271 *out_value
= local_value
;
273 result
= ERROR_CANTREAD
;
279 LONG
RegKey::ReadValue(const wchar_t* name
, std::wstring
* out_value
) const {
281 const size_t kMaxStringLength
= 1024; // This is after expansion.
282 // Use the one of the other forms of ReadValue if 1024 is too small for you.
283 wchar_t raw_value
[kMaxStringLength
];
284 DWORD type
= REG_SZ
, size
= sizeof(raw_value
);
285 LONG result
= ReadValue(name
, raw_value
, &size
, &type
);
286 if (result
== ERROR_SUCCESS
) {
287 if (type
== REG_SZ
) {
288 *out_value
= raw_value
;
289 } else if (type
== REG_EXPAND_SZ
) {
290 wchar_t expanded
[kMaxStringLength
];
291 size
= ExpandEnvironmentStrings(raw_value
, expanded
, kMaxStringLength
);
292 // Success: returns the number of wchar_t's copied
293 // Fail: buffer too small, returns the size required
294 // Fail: other, returns 0
295 if (size
== 0 || size
> kMaxStringLength
) {
296 result
= ERROR_MORE_DATA
;
298 *out_value
= expanded
;
301 // Not a string. Oops.
302 result
= ERROR_CANTREAD
;
309 LONG
RegKey::ReadValue(const wchar_t* name
,
312 DWORD
* dtype
) const {
313 LONG result
= RegQueryValueEx(key_
, name
, 0, dtype
,
314 reinterpret_cast<LPBYTE
>(data
), dsize
);
318 LONG
RegKey::ReadValues(const wchar_t* name
,
319 std::vector
<std::wstring
>* values
) {
322 DWORD type
= REG_MULTI_SZ
;
324 LONG result
= ReadValue(name
, NULL
, &size
, &type
);
325 if (FAILED(result
) || size
== 0)
328 if (type
!= REG_MULTI_SZ
)
329 return ERROR_CANTREAD
;
331 std::vector
<wchar_t> buffer(size
/ sizeof(wchar_t));
332 result
= ReadValue(name
, &buffer
[0], &size
, NULL
);
333 if (FAILED(result
) || size
== 0)
336 // Parse the double-null-terminated list of strings.
337 // Note: This code is paranoid to not read outside of |buf|, in the case where
338 // it may not be properly terminated.
339 const wchar_t* entry
= &buffer
[0];
340 const wchar_t* buffer_end
= entry
+ (size
/ sizeof(wchar_t));
341 while (entry
< buffer_end
&& entry
[0] != '\0') {
342 const wchar_t* entry_end
= std::find(entry
, buffer_end
, L
'\0');
343 values
->push_back(std::wstring(entry
, entry_end
));
344 entry
= entry_end
+ 1;
349 LONG
RegKey::WriteValue(const wchar_t* name
, DWORD in_value
) {
351 name
, &in_value
, static_cast<DWORD
>(sizeof(in_value
)), REG_DWORD
);
354 LONG
RegKey::WriteValue(const wchar_t * name
, const wchar_t* in_value
) {
355 return WriteValue(name
, in_value
,
356 static_cast<DWORD
>(sizeof(*in_value
) * (wcslen(in_value
) + 1)), REG_SZ
);
359 LONG
RegKey::WriteValue(const wchar_t* name
,
363 DCHECK(data
|| !dsize
);
365 LONG result
= RegSetValueEx(key_
, name
, 0, dtype
,
366 reinterpret_cast<LPBYTE
>(const_cast<void*>(data
)), dsize
);
370 LONG
RegKey::StartWatching() {
373 watch_event_
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
375 DWORD filter
= REG_NOTIFY_CHANGE_NAME
|
376 REG_NOTIFY_CHANGE_ATTRIBUTES
|
377 REG_NOTIFY_CHANGE_LAST_SET
|
378 REG_NOTIFY_CHANGE_SECURITY
;
380 // Watch the registry key for a change of value.
381 LONG result
= RegNotifyChangeKeyValue(key_
, TRUE
, filter
, watch_event_
, TRUE
);
382 if (result
!= ERROR_SUCCESS
) {
383 CloseHandle(watch_event_
);
390 bool RegKey::HasChanged() {
392 if (WaitForSingleObject(watch_event_
, 0) == WAIT_OBJECT_0
) {
400 LONG
RegKey::StopWatching() {
401 LONG result
= ERROR_INVALID_HANDLE
;
403 CloseHandle(watch_event_
);
405 result
= ERROR_SUCCESS
;
411 LONG
RegKey::RegDeleteKeyExWrapper(HKEY hKey
,
412 const wchar_t* lpSubKey
,
415 typedef LSTATUS(WINAPI
* RegDeleteKeyExPtr
)(HKEY
, LPCWSTR
, REGSAM
, DWORD
);
417 RegDeleteKeyExPtr reg_delete_key_ex_func
=
418 reinterpret_cast<RegDeleteKeyExPtr
>(
419 GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
421 if (reg_delete_key_ex_func
)
422 return reg_delete_key_ex_func(hKey
, lpSubKey
, samDesired
, Reserved
);
424 // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
425 return RegDeleteKey(hKey
, lpSubKey
);
429 LONG
RegKey::RegDelRecurse(HKEY root_key
,
430 const std::wstring
& name
,
432 // First, see if the key can be deleted without having to recurse.
433 LONG result
= RegDeleteKeyExWrapper(root_key
, name
.c_str(), access
, 0);
434 if (result
== ERROR_SUCCESS
)
437 HKEY target_key
= NULL
;
438 result
= RegOpenKeyEx(
439 root_key
, name
.c_str(), 0, KEY_ENUMERATE_SUB_KEYS
| access
, &target_key
);
441 if (result
== ERROR_FILE_NOT_FOUND
)
442 return ERROR_SUCCESS
;
443 if (result
!= ERROR_SUCCESS
)
446 std::wstring
subkey_name(name
);
448 // Check for an ending slash and add one if it is missing.
449 if (!name
.empty() && subkey_name
[name
.length() - 1] != L
'\\')
450 subkey_name
+= L
"\\";
452 // Enumerate the keys
453 result
= ERROR_SUCCESS
;
454 const DWORD kMaxKeyNameLength
= MAX_PATH
;
455 const size_t base_key_length
= subkey_name
.length();
456 std::wstring key_name
;
457 while (result
== ERROR_SUCCESS
) {
458 DWORD key_size
= kMaxKeyNameLength
;
459 result
= RegEnumKeyEx(target_key
,
461 WriteInto(&key_name
, kMaxKeyNameLength
),
468 if (result
!= ERROR_SUCCESS
)
471 key_name
.resize(key_size
);
472 subkey_name
.resize(base_key_length
);
473 subkey_name
+= key_name
;
475 if (RegDelRecurse(root_key
, subkey_name
, access
) != ERROR_SUCCESS
)
479 RegCloseKey(target_key
);
481 // Try again to delete the key.
482 result
= RegDeleteKeyExWrapper(root_key
, name
.c_str(), access
, 0);
487 // RegistryValueIterator ------------------------------------------------------
489 RegistryValueIterator::RegistryValueIterator(HKEY root_key
,
490 const wchar_t* folder_key
)
491 : name_(MAX_PATH
, L
'\0'),
492 value_(MAX_PATH
, L
'\0') {
493 LONG result
= RegOpenKeyEx(root_key
, folder_key
, 0, KEY_READ
, &key_
);
494 if (result
!= ERROR_SUCCESS
) {
498 result
= ::RegQueryInfoKey(key_
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, &count
,
499 NULL
, NULL
, NULL
, NULL
);
501 if (result
!= ERROR_SUCCESS
) {
512 RegistryValueIterator::~RegistryValueIterator() {
517 DWORD
RegistryValueIterator::ValueCount() const {
519 LONG result
= ::RegQueryInfoKey(key_
, NULL
, 0, NULL
, NULL
, NULL
, NULL
,
520 &count
, NULL
, NULL
, NULL
, NULL
);
521 if (result
!= ERROR_SUCCESS
)
527 bool RegistryValueIterator::Valid() const {
528 return key_
!= NULL
&& index_
>= 0;
531 void RegistryValueIterator::operator++() {
536 bool RegistryValueIterator::Read() {
538 DWORD capacity
= static_cast<DWORD
>(name_
.capacity());
539 DWORD name_size
= capacity
;
540 // |value_size_| is in bytes. Reserve the last character for a NUL.
541 value_size_
= static_cast<DWORD
>((value_
.size() - 1) * sizeof(wchar_t));
542 LONG result
= ::RegEnumValue(
543 key_
, index_
, WriteInto(&name_
, name_size
), &name_size
, NULL
, &type_
,
544 reinterpret_cast<BYTE
*>(vector_as_array(&value_
)), &value_size_
);
546 if (result
== ERROR_MORE_DATA
) {
547 // Registry key names are limited to 255 characters and fit within
548 // MAX_PATH (which is 260) but registry value names can use up to 16,383
549 // characters and the value itself is not limited
550 // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
551 // ms724872(v=vs.85).aspx).
552 // Resize the buffers and retry if their size caused the failure.
553 DWORD value_size_in_wchars
= to_wchar_size(value_size_
);
554 if (value_size_in_wchars
+ 1 > value_
.size())
555 value_
.resize(value_size_in_wchars
+ 1, L
'\0');
556 value_size_
= static_cast<DWORD
>((value_
.size() - 1) * sizeof(wchar_t));
557 name_size
= name_size
== capacity
? MAX_REGISTRY_NAME_SIZE
: capacity
;
558 result
= ::RegEnumValue(
559 key_
, index_
, WriteInto(&name_
, name_size
), &name_size
, NULL
, &type_
,
560 reinterpret_cast<BYTE
*>(vector_as_array(&value_
)), &value_size_
);
563 if (result
== ERROR_SUCCESS
) {
564 DCHECK_LT(to_wchar_size(value_size_
), value_
.size());
565 value_
[to_wchar_size(value_size_
)] = L
'\0';
576 // RegistryKeyIterator --------------------------------------------------------
578 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key
,
579 const wchar_t* folder_key
) {
580 LONG result
= RegOpenKeyEx(root_key
, folder_key
, 0, KEY_READ
, &key_
);
581 if (result
!= ERROR_SUCCESS
) {
585 LONG result
= ::RegQueryInfoKey(key_
, NULL
, 0, NULL
, &count
, NULL
, NULL
,
586 NULL
, NULL
, NULL
, NULL
, NULL
);
588 if (result
!= ERROR_SUCCESS
) {
599 RegistryKeyIterator::~RegistryKeyIterator() {
604 DWORD
RegistryKeyIterator::SubkeyCount() const {
606 LONG result
= ::RegQueryInfoKey(key_
, NULL
, 0, NULL
, &count
, NULL
, NULL
,
607 NULL
, NULL
, NULL
, NULL
, NULL
);
608 if (result
!= ERROR_SUCCESS
)
614 bool RegistryKeyIterator::Valid() const {
615 return key_
!= NULL
&& index_
>= 0;
618 void RegistryKeyIterator::operator++() {
623 bool RegistryKeyIterator::Read() {
625 DWORD ncount
= arraysize(name_
);
627 LONG r
= ::RegEnumKeyEx(key_
, index_
, name_
, &ncount
, NULL
, NULL
,
629 if (ERROR_SUCCESS
== r
)