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 // Watches for modifications to a key.
38 class RegKey::Watcher
: public ObjectWatcher::Delegate
{
40 explicit Watcher(RegKey
* owner
) : owner_(owner
) {}
43 bool StartWatching(HKEY key
, const ChangeCallback
& callback
);
45 // Implementation of ObjectWatcher::Delegate.
46 void OnObjectSignaled(HANDLE object
) override
{
47 DCHECK(watch_event_
.IsValid() && watch_event_
.Get() == object
);
48 ChangeCallback callback
= callback_
;
55 ScopedHandle watch_event_
;
56 ObjectWatcher object_watcher_
;
57 ChangeCallback callback_
;
58 DISALLOW_COPY_AND_ASSIGN(Watcher
);
61 bool RegKey::Watcher::StartWatching(HKEY key
, const ChangeCallback
& callback
) {
63 DCHECK(callback_
.is_null());
65 if (!watch_event_
.IsValid())
66 watch_event_
.Set(CreateEvent(NULL
, TRUE
, FALSE
, NULL
));
68 if (!watch_event_
.IsValid())
71 DWORD filter
= REG_NOTIFY_CHANGE_NAME
|
72 REG_NOTIFY_CHANGE_ATTRIBUTES
|
73 REG_NOTIFY_CHANGE_LAST_SET
|
74 REG_NOTIFY_CHANGE_SECURITY
;
76 // Watch the registry key for a change of value.
77 LONG result
= RegNotifyChangeKeyValue(key
, TRUE
, filter
, watch_event_
.Get(),
79 if (result
!= ERROR_SUCCESS
) {
85 return object_watcher_
.StartWatching(watch_event_
.Get(), this);
88 // RegKey ----------------------------------------------------------------------
90 RegKey::RegKey() : key_(NULL
), wow64access_(0) {
93 RegKey::RegKey(HKEY key
) : key_(key
), wow64access_(0) {
96 RegKey::RegKey(HKEY rootkey
, const wchar_t* subkey
, REGSAM access
)
100 if (access
& (KEY_SET_VALUE
| KEY_CREATE_SUB_KEY
| KEY_CREATE_LINK
))
101 Create(rootkey
, subkey
, access
);
103 Open(rootkey
, subkey
, access
);
106 wow64access_
= access
& kWow64AccessMask
;
114 LONG
RegKey::Create(HKEY rootkey
, const wchar_t* subkey
, REGSAM access
) {
115 DWORD disposition_value
;
116 return CreateWithDisposition(rootkey
, subkey
, &disposition_value
, access
);
119 LONG
RegKey::CreateWithDisposition(HKEY rootkey
, const wchar_t* subkey
,
120 DWORD
* disposition
, REGSAM access
) {
121 DCHECK(rootkey
&& subkey
&& access
&& disposition
);
123 LONG result
= RegCreateKeyEx(rootkey
, subkey
, 0, NULL
,
124 REG_OPTION_NON_VOLATILE
, access
, NULL
, &subhkey
,
126 if (result
== ERROR_SUCCESS
) {
129 wow64access_
= access
& kWow64AccessMask
;
135 LONG
RegKey::CreateKey(const wchar_t* name
, REGSAM access
) {
136 DCHECK(name
&& access
);
137 // After the application has accessed an alternate registry view using one of
138 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
139 // (create, delete, or open) on child registry keys must explicitly use the
140 // same flag. Otherwise, there can be unexpected behavior.
141 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
142 if ((access
& kWow64AccessMask
) != wow64access_
) {
144 return ERROR_INVALID_PARAMETER
;
147 LONG result
= RegCreateKeyEx(key_
, name
, 0, NULL
, REG_OPTION_NON_VOLATILE
,
148 access
, NULL
, &subkey
, NULL
);
149 if (result
== ERROR_SUCCESS
) {
152 wow64access_
= access
& kWow64AccessMask
;
158 LONG
RegKey::Open(HKEY rootkey
, const wchar_t* subkey
, REGSAM access
) {
159 DCHECK(rootkey
&& subkey
&& access
);
162 LONG result
= RegOpenKeyEx(rootkey
, subkey
, 0, access
, &subhkey
);
163 if (result
== ERROR_SUCCESS
) {
166 wow64access_
= access
& kWow64AccessMask
;
172 LONG
RegKey::OpenKey(const wchar_t* relative_key_name
, REGSAM access
) {
173 DCHECK(relative_key_name
&& access
);
174 // After the application has accessed an alternate registry view using one of
175 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
176 // (create, delete, or open) on child registry keys must explicitly use the
177 // same flag. Otherwise, there can be unexpected behavior.
178 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
179 if ((access
& kWow64AccessMask
) != wow64access_
) {
181 return ERROR_INVALID_PARAMETER
;
184 LONG result
= RegOpenKeyEx(key_
, relative_key_name
, 0, access
, &subkey
);
186 // We have to close the current opened key before replacing it with the new
188 if (result
== ERROR_SUCCESS
) {
191 wow64access_
= access
& kWow64AccessMask
;
196 void RegKey::Close() {
204 // TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
205 void RegKey::Set(HKEY key
) {
212 HKEY
RegKey::Take() {
213 DCHECK(wow64access_
== 0);
219 bool RegKey::HasValue(const wchar_t* name
) const {
220 return RegQueryValueEx(key_
, name
, 0, NULL
, NULL
, NULL
) == ERROR_SUCCESS
;
223 DWORD
RegKey::GetValueCount() const {
225 LONG result
= RegQueryInfoKey(key_
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, &count
,
226 NULL
, NULL
, NULL
, NULL
);
227 return (result
== ERROR_SUCCESS
) ? count
: 0;
230 LONG
RegKey::GetValueNameAt(int index
, std::wstring
* name
) const {
232 DWORD bufsize
= arraysize(buf
);
233 LONG r
= ::RegEnumValue(key_
, index
, buf
, &bufsize
, NULL
, NULL
, NULL
, NULL
);
234 if (r
== ERROR_SUCCESS
)
240 LONG
RegKey::DeleteKey(const wchar_t* name
) {
245 // Verify the key exists before attempting delete to replicate previous
248 RegOpenKeyEx(key_
, name
, 0, READ_CONTROL
| wow64access_
, &subkey
);
249 if (result
!= ERROR_SUCCESS
)
253 return RegDelRecurse(key_
, std::wstring(name
), wow64access_
);
256 LONG
RegKey::DeleteEmptyKey(const wchar_t* name
) {
260 HKEY target_key
= NULL
;
261 LONG result
= RegOpenKeyEx(key_
, name
, 0, KEY_READ
| wow64access_
,
264 if (result
!= ERROR_SUCCESS
)
268 result
= RegQueryInfoKey(target_key
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, &count
,
269 NULL
, NULL
, NULL
, NULL
);
271 RegCloseKey(target_key
);
273 if (result
!= ERROR_SUCCESS
)
277 return RegDeleteKeyExWrapper(key_
, name
, wow64access_
, 0);
279 return ERROR_DIR_NOT_EMPTY
;
282 LONG
RegKey::DeleteValue(const wchar_t* value_name
) {
284 LONG result
= RegDeleteValue(key_
, value_name
);
288 LONG
RegKey::ReadValueDW(const wchar_t* name
, DWORD
* out_value
) const {
290 DWORD type
= REG_DWORD
;
291 DWORD size
= sizeof(DWORD
);
292 DWORD local_value
= 0;
293 LONG result
= ReadValue(name
, &local_value
, &size
, &type
);
294 if (result
== ERROR_SUCCESS
) {
295 if ((type
== REG_DWORD
|| type
== REG_BINARY
) && size
== sizeof(DWORD
))
296 *out_value
= local_value
;
298 result
= ERROR_CANTREAD
;
304 LONG
RegKey::ReadInt64(const wchar_t* name
, int64
* out_value
) const {
306 DWORD type
= REG_QWORD
;
307 int64 local_value
= 0;
308 DWORD size
= sizeof(local_value
);
309 LONG result
= ReadValue(name
, &local_value
, &size
, &type
);
310 if (result
== ERROR_SUCCESS
) {
311 if ((type
== REG_QWORD
|| type
== REG_BINARY
) &&
312 size
== sizeof(local_value
))
313 *out_value
= local_value
;
315 result
= ERROR_CANTREAD
;
321 LONG
RegKey::ReadValue(const wchar_t* name
, std::wstring
* out_value
) const {
323 const size_t kMaxStringLength
= 1024; // This is after expansion.
324 // Use the one of the other forms of ReadValue if 1024 is too small for you.
325 wchar_t raw_value
[kMaxStringLength
];
326 DWORD type
= REG_SZ
, size
= sizeof(raw_value
);
327 LONG result
= ReadValue(name
, raw_value
, &size
, &type
);
328 if (result
== ERROR_SUCCESS
) {
329 if (type
== REG_SZ
) {
330 *out_value
= raw_value
;
331 } else if (type
== REG_EXPAND_SZ
) {
332 wchar_t expanded
[kMaxStringLength
];
333 size
= ExpandEnvironmentStrings(raw_value
, expanded
, kMaxStringLength
);
334 // Success: returns the number of wchar_t's copied
335 // Fail: buffer too small, returns the size required
336 // Fail: other, returns 0
337 if (size
== 0 || size
> kMaxStringLength
) {
338 result
= ERROR_MORE_DATA
;
340 *out_value
= expanded
;
343 // Not a string. Oops.
344 result
= ERROR_CANTREAD
;
351 LONG
RegKey::ReadValue(const wchar_t* name
,
354 DWORD
* dtype
) const {
355 LONG result
= RegQueryValueEx(key_
, name
, 0, dtype
,
356 reinterpret_cast<LPBYTE
>(data
), dsize
);
360 LONG
RegKey::ReadValues(const wchar_t* name
,
361 std::vector
<std::wstring
>* values
) {
364 DWORD type
= REG_MULTI_SZ
;
366 LONG result
= ReadValue(name
, NULL
, &size
, &type
);
367 if (FAILED(result
) || size
== 0)
370 if (type
!= REG_MULTI_SZ
)
371 return ERROR_CANTREAD
;
373 std::vector
<wchar_t> buffer(size
/ sizeof(wchar_t));
374 result
= ReadValue(name
, &buffer
[0], &size
, NULL
);
375 if (FAILED(result
) || size
== 0)
378 // Parse the double-null-terminated list of strings.
379 // Note: This code is paranoid to not read outside of |buf|, in the case where
380 // it may not be properly terminated.
381 const wchar_t* entry
= &buffer
[0];
382 const wchar_t* buffer_end
= entry
+ (size
/ sizeof(wchar_t));
383 while (entry
< buffer_end
&& entry
[0] != '\0') {
384 const wchar_t* entry_end
= std::find(entry
, buffer_end
, L
'\0');
385 values
->push_back(std::wstring(entry
, entry_end
));
386 entry
= entry_end
+ 1;
391 LONG
RegKey::WriteValue(const wchar_t* name
, DWORD in_value
) {
393 name
, &in_value
, static_cast<DWORD
>(sizeof(in_value
)), REG_DWORD
);
396 LONG
RegKey::WriteValue(const wchar_t * name
, const wchar_t* in_value
) {
397 return WriteValue(name
, in_value
,
398 static_cast<DWORD
>(sizeof(*in_value
) * (wcslen(in_value
) + 1)), REG_SZ
);
401 LONG
RegKey::WriteValue(const wchar_t* name
,
405 DCHECK(data
|| !dsize
);
407 LONG result
= RegSetValueEx(key_
, name
, 0, dtype
,
408 reinterpret_cast<LPBYTE
>(const_cast<void*>(data
)), dsize
);
412 bool RegKey::StartWatching(const ChangeCallback
& callback
) {
414 key_watcher_
.reset(new Watcher(this));
416 if (!key_watcher_
.get()->StartWatching(key_
, callback
))
423 LONG
RegKey::RegDeleteKeyExWrapper(HKEY hKey
,
424 const wchar_t* lpSubKey
,
427 typedef LSTATUS(WINAPI
* RegDeleteKeyExPtr
)(HKEY
, LPCWSTR
, REGSAM
, DWORD
);
429 RegDeleteKeyExPtr reg_delete_key_ex_func
=
430 reinterpret_cast<RegDeleteKeyExPtr
>(
431 GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
433 if (reg_delete_key_ex_func
)
434 return reg_delete_key_ex_func(hKey
, lpSubKey
, samDesired
, Reserved
);
436 // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
437 return RegDeleteKey(hKey
, lpSubKey
);
441 LONG
RegKey::RegDelRecurse(HKEY root_key
,
442 const std::wstring
& name
,
444 // First, see if the key can be deleted without having to recurse.
445 LONG result
= RegDeleteKeyExWrapper(root_key
, name
.c_str(), access
, 0);
446 if (result
== ERROR_SUCCESS
)
449 HKEY target_key
= NULL
;
450 result
= RegOpenKeyEx(
451 root_key
, name
.c_str(), 0, KEY_ENUMERATE_SUB_KEYS
| access
, &target_key
);
453 if (result
== ERROR_FILE_NOT_FOUND
)
454 return ERROR_SUCCESS
;
455 if (result
!= ERROR_SUCCESS
)
458 std::wstring
subkey_name(name
);
460 // Check for an ending slash and add one if it is missing.
461 if (!name
.empty() && subkey_name
[name
.length() - 1] != L
'\\')
462 subkey_name
+= L
"\\";
464 // Enumerate the keys
465 result
= ERROR_SUCCESS
;
466 const DWORD kMaxKeyNameLength
= MAX_PATH
;
467 const size_t base_key_length
= subkey_name
.length();
468 std::wstring key_name
;
469 while (result
== ERROR_SUCCESS
) {
470 DWORD key_size
= kMaxKeyNameLength
;
471 result
= RegEnumKeyEx(target_key
,
473 WriteInto(&key_name
, kMaxKeyNameLength
),
480 if (result
!= ERROR_SUCCESS
)
483 key_name
.resize(key_size
);
484 subkey_name
.resize(base_key_length
);
485 subkey_name
+= key_name
;
487 if (RegDelRecurse(root_key
, subkey_name
, access
) != ERROR_SUCCESS
)
491 RegCloseKey(target_key
);
493 // Try again to delete the key.
494 result
= RegDeleteKeyExWrapper(root_key
, name
.c_str(), access
, 0);
499 // RegistryValueIterator ------------------------------------------------------
501 RegistryValueIterator::RegistryValueIterator(HKEY root_key
,
502 const wchar_t* folder_key
,
504 : name_(MAX_PATH
, L
'\0'),
505 value_(MAX_PATH
, L
'\0') {
506 Initialize(root_key
, folder_key
, wow64access
);
509 RegistryValueIterator::RegistryValueIterator(HKEY root_key
,
510 const wchar_t* folder_key
)
511 : name_(MAX_PATH
, L
'\0'),
512 value_(MAX_PATH
, L
'\0') {
513 Initialize(root_key
, folder_key
, 0);
516 void RegistryValueIterator::Initialize(HKEY root_key
,
517 const wchar_t* folder_key
,
518 REGSAM wow64access
) {
519 DCHECK_EQ(wow64access
& ~kWow64AccessMask
, static_cast<REGSAM
>(0));
521 RegOpenKeyEx(root_key
, folder_key
, 0, KEY_READ
| wow64access
, &key_
);
522 if (result
!= ERROR_SUCCESS
) {
526 result
= ::RegQueryInfoKey(key_
, NULL
, 0, NULL
, NULL
, NULL
, NULL
, &count
,
527 NULL
, NULL
, NULL
, NULL
);
529 if (result
!= ERROR_SUCCESS
) {
540 RegistryValueIterator::~RegistryValueIterator() {
545 DWORD
RegistryValueIterator::ValueCount() const {
547 LONG result
= ::RegQueryInfoKey(key_
, NULL
, 0, NULL
, NULL
, NULL
, NULL
,
548 &count
, NULL
, NULL
, NULL
, NULL
);
549 if (result
!= ERROR_SUCCESS
)
555 bool RegistryValueIterator::Valid() const {
556 return key_
!= NULL
&& index_
>= 0;
559 void RegistryValueIterator::operator++() {
564 bool RegistryValueIterator::Read() {
566 DWORD capacity
= static_cast<DWORD
>(name_
.capacity());
567 DWORD name_size
= capacity
;
568 // |value_size_| is in bytes. Reserve the last character for a NUL.
569 value_size_
= static_cast<DWORD
>((value_
.size() - 1) * sizeof(wchar_t));
570 LONG result
= ::RegEnumValue(
571 key_
, index_
, WriteInto(&name_
, name_size
), &name_size
, NULL
, &type_
,
572 reinterpret_cast<BYTE
*>(vector_as_array(&value_
)), &value_size_
);
574 if (result
== ERROR_MORE_DATA
) {
575 // Registry key names are limited to 255 characters and fit within
576 // MAX_PATH (which is 260) but registry value names can use up to 16,383
577 // characters and the value itself is not limited
578 // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
579 // ms724872(v=vs.85).aspx).
580 // Resize the buffers and retry if their size caused the failure.
581 DWORD value_size_in_wchars
= to_wchar_size(value_size_
);
582 if (value_size_in_wchars
+ 1 > value_
.size())
583 value_
.resize(value_size_in_wchars
+ 1, L
'\0');
584 value_size_
= static_cast<DWORD
>((value_
.size() - 1) * sizeof(wchar_t));
585 name_size
= name_size
== capacity
? MAX_REGISTRY_NAME_SIZE
: capacity
;
586 result
= ::RegEnumValue(
587 key_
, index_
, WriteInto(&name_
, name_size
), &name_size
, NULL
, &type_
,
588 reinterpret_cast<BYTE
*>(vector_as_array(&value_
)), &value_size_
);
591 if (result
== ERROR_SUCCESS
) {
592 DCHECK_LT(to_wchar_size(value_size_
), value_
.size());
593 value_
[to_wchar_size(value_size_
)] = L
'\0';
604 // RegistryKeyIterator --------------------------------------------------------
606 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key
,
607 const wchar_t* folder_key
) {
608 Initialize(root_key
, folder_key
, 0);
611 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key
,
612 const wchar_t* folder_key
,
613 REGSAM wow64access
) {
614 Initialize(root_key
, folder_key
, wow64access
);
617 RegistryKeyIterator::~RegistryKeyIterator() {
622 DWORD
RegistryKeyIterator::SubkeyCount() const {
624 LONG result
= ::RegQueryInfoKey(key_
, NULL
, 0, NULL
, &count
, NULL
, NULL
,
625 NULL
, NULL
, NULL
, NULL
, NULL
);
626 if (result
!= ERROR_SUCCESS
)
632 bool RegistryKeyIterator::Valid() const {
633 return key_
!= NULL
&& index_
>= 0;
636 void RegistryKeyIterator::operator++() {
641 bool RegistryKeyIterator::Read() {
643 DWORD ncount
= arraysize(name_
);
645 LONG r
= ::RegEnumKeyEx(key_
, index_
, name_
, &ncount
, NULL
, NULL
,
647 if (ERROR_SUCCESS
== r
)
655 void RegistryKeyIterator::Initialize(HKEY root_key
,
656 const wchar_t* folder_key
,
657 REGSAM wow64access
) {
658 DCHECK_EQ(wow64access
& ~kWow64AccessMask
, static_cast<REGSAM
>(0));
660 RegOpenKeyEx(root_key
, folder_key
, 0, KEY_READ
| wow64access
, &key_
);
661 if (result
!= ERROR_SUCCESS
) {
665 LONG result
= ::RegQueryInfoKey(key_
, NULL
, 0, NULL
, &count
, NULL
, NULL
,
666 NULL
, NULL
, NULL
, NULL
, NULL
);
668 if (result
!= ERROR_SUCCESS
) {