• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "base/win/registry.h"
6 
7 #include <shlwapi.h>
8 #include <algorithm>
9 
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"
14 
15 namespace base {
16 namespace win {
17 
18 namespace {
19 
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;
24 
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.
to_wchar_size(DWORD byte_size)28 inline DWORD to_wchar_size(DWORD byte_size) {
29   return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
30 }
31 
32 // Mask to pull WOW64 access flags out of REGSAM access.
33 const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
34 
35 }  // namespace
36 
37 // RegKey ----------------------------------------------------------------------
38 
RegKey()39 RegKey::RegKey()
40     : key_(NULL),
41       watch_event_(0),
42       wow64access_(0) {
43 }
44 
RegKey(HKEY key)45 RegKey::RegKey(HKEY key)
46     : key_(key),
47       watch_event_(0),
48       wow64access_(0) {
49 }
50 
RegKey(HKEY rootkey,const wchar_t * subkey,REGSAM access)51 RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
52     : key_(NULL),
53       watch_event_(0),
54       wow64access_(0) {
55   if (rootkey) {
56     if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
57       Create(rootkey, subkey, access);
58     else
59       Open(rootkey, subkey, access);
60   } else {
61     DCHECK(!subkey);
62     wow64access_ = access & kWow64AccessMask;
63   }
64 }
65 
~RegKey()66 RegKey::~RegKey() {
67   Close();
68 }
69 
Create(HKEY rootkey,const wchar_t * subkey,REGSAM access)70 LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
71   DWORD disposition_value;
72   return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
73 }
74 
CreateWithDisposition(HKEY rootkey,const wchar_t * subkey,DWORD * disposition,REGSAM access)75 LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
76                                    DWORD* disposition, REGSAM access) {
77   DCHECK(rootkey && subkey && access && disposition);
78   HKEY subhkey = NULL;
79   LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
80                                REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
81                                disposition);
82   if (result == ERROR_SUCCESS) {
83     Close();
84     key_ = subhkey;
85     wow64access_ = access & kWow64AccessMask;
86   }
87 
88   return result;
89 }
90 
CreateKey(const wchar_t * name,REGSAM access)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_) {
99     NOTREACHED();
100     return ERROR_INVALID_PARAMETER;
101   }
102   HKEY subkey = NULL;
103   LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
104                                access, NULL, &subkey, NULL);
105   if (result == ERROR_SUCCESS) {
106     Close();
107     key_ = subkey;
108     wow64access_ = access & kWow64AccessMask;
109   }
110 
111   return result;
112 }
113 
Open(HKEY rootkey,const wchar_t * subkey,REGSAM access)114 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
115   DCHECK(rootkey && subkey && access);
116   HKEY subhkey = NULL;
117 
118   LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
119   if (result == ERROR_SUCCESS) {
120     Close();
121     key_ = subhkey;
122     wow64access_ = access & kWow64AccessMask;
123   }
124 
125   return result;
126 }
127 
OpenKey(const wchar_t * relative_key_name,REGSAM access)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_) {
136     NOTREACHED();
137     return ERROR_INVALID_PARAMETER;
138   }
139   HKEY subkey = NULL;
140   LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
141 
142   // We have to close the current opened key before replacing it with the new
143   // one.
144   if (result == ERROR_SUCCESS) {
145     Close();
146     key_ = subkey;
147     wow64access_ = access & kWow64AccessMask;
148   }
149   return result;
150 }
151 
Close()152 void RegKey::Close() {
153   StopWatching();
154   if (key_) {
155     ::RegCloseKey(key_);
156     key_ = NULL;
157     wow64access_ = 0;
158   }
159 }
160 
161 // TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
Set(HKEY key)162 void RegKey::Set(HKEY key) {
163   if (key_ != key) {
164     Close();
165     key_ = key;
166   }
167 }
168 
Take()169 HKEY RegKey::Take() {
170   DCHECK(wow64access_ == 0);
171   StopWatching();
172   HKEY key = key_;
173   key_ = NULL;
174   return key;
175 }
176 
HasValue(const wchar_t * name) const177 bool RegKey::HasValue(const wchar_t* name) const {
178   return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
179 }
180 
GetValueCount() const181 DWORD RegKey::GetValueCount() const {
182   DWORD count = 0;
183   LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
184                                 NULL, NULL, NULL, NULL);
185   return (result == ERROR_SUCCESS) ? count : 0;
186 }
187 
GetValueNameAt(int index,std::wstring * name) const188 LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
189   wchar_t buf[256];
190   DWORD bufsize = arraysize(buf);
191   LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
192   if (r == ERROR_SUCCESS)
193     *name = buf;
194 
195   return r;
196 }
197 
DeleteKey(const wchar_t * name)198 LONG RegKey::DeleteKey(const wchar_t* name) {
199   DCHECK(key_);
200   DCHECK(name);
201   HKEY subkey = NULL;
202 
203   // Verify the key exists before attempting delete to replicate previous
204   // behavior.
205   LONG result =
206       RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
207   if (result != ERROR_SUCCESS)
208     return result;
209   RegCloseKey(subkey);
210 
211   return RegDelRecurse(key_, std::wstring(name), wow64access_);
212 }
213 
DeleteEmptyKey(const wchar_t * name)214 LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
215   DCHECK(key_);
216   DCHECK(name);
217 
218   HKEY target_key = NULL;
219   LONG result = RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_,
220                              &target_key);
221 
222   if (result != ERROR_SUCCESS)
223     return result;
224 
225   DWORD count = 0;
226   result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
227                            NULL, NULL, NULL, NULL);
228 
229   RegCloseKey(target_key);
230 
231   if (result != ERROR_SUCCESS)
232     return result;
233 
234   if (count == 0)
235     return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
236 
237   return ERROR_DIR_NOT_EMPTY;
238 }
239 
DeleteValue(const wchar_t * value_name)240 LONG RegKey::DeleteValue(const wchar_t* value_name) {
241   DCHECK(key_);
242   LONG result = RegDeleteValue(key_, value_name);
243   return result;
244 }
245 
ReadValueDW(const wchar_t * name,DWORD * out_value) const246 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
247   DCHECK(out_value);
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;
255     else
256       result = ERROR_CANTREAD;
257   }
258 
259   return result;
260 }
261 
ReadInt64(const wchar_t * name,int64 * out_value) const262 LONG RegKey::ReadInt64(const wchar_t* name, int64* out_value) const {
263   DCHECK(out_value);
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;
272     else
273       result = ERROR_CANTREAD;
274   }
275 
276   return result;
277 }
278 
ReadValue(const wchar_t * name,std::wstring * out_value) const279 LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
280   DCHECK(out_value);
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;
297       } else {
298         *out_value = expanded;
299       }
300     } else {
301       // Not a string. Oops.
302       result = ERROR_CANTREAD;
303     }
304   }
305 
306   return result;
307 }
308 
ReadValue(const wchar_t * name,void * data,DWORD * dsize,DWORD * dtype) const309 LONG RegKey::ReadValue(const wchar_t* name,
310                        void* data,
311                        DWORD* dsize,
312                        DWORD* dtype) const {
313   LONG result = RegQueryValueEx(key_, name, 0, dtype,
314                                 reinterpret_cast<LPBYTE>(data), dsize);
315   return result;
316 }
317 
ReadValues(const wchar_t * name,std::vector<std::wstring> * values)318 LONG RegKey::ReadValues(const wchar_t* name,
319                         std::vector<std::wstring>* values) {
320   values->clear();
321 
322   DWORD type = REG_MULTI_SZ;
323   DWORD size = 0;
324   LONG result = ReadValue(name, NULL, &size, &type);
325   if (FAILED(result) || size == 0)
326     return result;
327 
328   if (type != REG_MULTI_SZ)
329     return ERROR_CANTREAD;
330 
331   std::vector<wchar_t> buffer(size / sizeof(wchar_t));
332   result = ReadValue(name, &buffer[0], &size, NULL);
333   if (FAILED(result) || size == 0)
334     return result;
335 
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;
345   }
346   return 0;
347 }
348 
WriteValue(const wchar_t * name,DWORD in_value)349 LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
350   return WriteValue(
351       name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD);
352 }
353 
WriteValue(const wchar_t * name,const wchar_t * in_value)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);
357 }
358 
WriteValue(const wchar_t * name,const void * data,DWORD dsize,DWORD dtype)359 LONG RegKey::WriteValue(const wchar_t* name,
360                         const void* data,
361                         DWORD dsize,
362                         DWORD dtype) {
363   DCHECK(data || !dsize);
364 
365   LONG result = RegSetValueEx(key_, name, 0, dtype,
366       reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
367   return result;
368 }
369 
StartWatching()370 LONG RegKey::StartWatching() {
371   DCHECK(key_);
372   if (!watch_event_)
373     watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
374 
375   DWORD filter = REG_NOTIFY_CHANGE_NAME |
376                  REG_NOTIFY_CHANGE_ATTRIBUTES |
377                  REG_NOTIFY_CHANGE_LAST_SET |
378                  REG_NOTIFY_CHANGE_SECURITY;
379 
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_);
384     watch_event_ = 0;
385   }
386 
387   return result;
388 }
389 
HasChanged()390 bool RegKey::HasChanged() {
391   if (watch_event_) {
392     if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
393       StartWatching();
394       return true;
395     }
396   }
397   return false;
398 }
399 
StopWatching()400 LONG RegKey::StopWatching() {
401   LONG result = ERROR_INVALID_HANDLE;
402   if (watch_event_) {
403     CloseHandle(watch_event_);
404     watch_event_ = 0;
405     result = ERROR_SUCCESS;
406   }
407   return result;
408 }
409 
410 // static
RegDeleteKeyExWrapper(HKEY hKey,const wchar_t * lpSubKey,REGSAM samDesired,DWORD Reserved)411 LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
412                                    const wchar_t* lpSubKey,
413                                    REGSAM samDesired,
414                                    DWORD Reserved) {
415   typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
416 
417   RegDeleteKeyExPtr reg_delete_key_ex_func =
418       reinterpret_cast<RegDeleteKeyExPtr>(
419           GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
420 
421   if (reg_delete_key_ex_func)
422     return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved);
423 
424   // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
425   return RegDeleteKey(hKey, lpSubKey);
426 }
427 
428 // static
RegDelRecurse(HKEY root_key,const std::wstring & name,REGSAM access)429 LONG RegKey::RegDelRecurse(HKEY root_key,
430                            const std::wstring& name,
431                            REGSAM access) {
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)
435     return result;
436 
437   HKEY target_key = NULL;
438   result = RegOpenKeyEx(
439       root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key);
440 
441   if (result == ERROR_FILE_NOT_FOUND)
442     return ERROR_SUCCESS;
443   if (result != ERROR_SUCCESS)
444     return result;
445 
446   std::wstring subkey_name(name);
447 
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"\\";
451 
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,
460                           0,
461                           WriteInto(&key_name, kMaxKeyNameLength),
462                           &key_size,
463                           NULL,
464                           NULL,
465                           NULL,
466                           NULL);
467 
468     if (result != ERROR_SUCCESS)
469       break;
470 
471     key_name.resize(key_size);
472     subkey_name.resize(base_key_length);
473     subkey_name += key_name;
474 
475     if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
476       break;
477   }
478 
479   RegCloseKey(target_key);
480 
481   // Try again to delete the key.
482   result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
483 
484   return result;
485 }
486 
487 // RegistryValueIterator ------------------------------------------------------
488 
RegistryValueIterator(HKEY root_key,const wchar_t * folder_key)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) {
495     key_ = NULL;
496   } else {
497     DWORD count = 0;
498     result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
499                                NULL, NULL, NULL, NULL);
500 
501     if (result != ERROR_SUCCESS) {
502       ::RegCloseKey(key_);
503       key_ = NULL;
504     } else {
505       index_ = count - 1;
506     }
507   }
508 
509   Read();
510 }
511 
~RegistryValueIterator()512 RegistryValueIterator::~RegistryValueIterator() {
513   if (key_)
514     ::RegCloseKey(key_);
515 }
516 
ValueCount() const517 DWORD RegistryValueIterator::ValueCount() const {
518   DWORD count = 0;
519   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
520                                   &count, NULL, NULL, NULL, NULL);
521   if (result != ERROR_SUCCESS)
522     return 0;
523 
524   return count;
525 }
526 
Valid() const527 bool RegistryValueIterator::Valid() const {
528   return key_ != NULL && index_ >= 0;
529 }
530 
operator ++()531 void RegistryValueIterator::operator++() {
532   --index_;
533   Read();
534 }
535 
Read()536 bool RegistryValueIterator::Read() {
537   if (Valid()) {
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_);
545 
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_);
561     }
562 
563     if (result == ERROR_SUCCESS) {
564       DCHECK_LT(to_wchar_size(value_size_), value_.size());
565       value_[to_wchar_size(value_size_)] = L'\0';
566       return true;
567     }
568   }
569 
570   name_[0] = L'\0';
571   value_[0] = L'\0';
572   value_size_ = 0;
573   return false;
574 }
575 
576 // RegistryKeyIterator --------------------------------------------------------
577 
RegistryKeyIterator(HKEY root_key,const wchar_t * folder_key)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) {
582     key_ = NULL;
583   } else {
584     DWORD count = 0;
585     LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
586                                     NULL, NULL, NULL, NULL, NULL);
587 
588     if (result != ERROR_SUCCESS) {
589       ::RegCloseKey(key_);
590       key_ = NULL;
591     } else {
592       index_ = count - 1;
593     }
594   }
595 
596   Read();
597 }
598 
~RegistryKeyIterator()599 RegistryKeyIterator::~RegistryKeyIterator() {
600   if (key_)
601     ::RegCloseKey(key_);
602 }
603 
SubkeyCount() const604 DWORD RegistryKeyIterator::SubkeyCount() const {
605   DWORD count = 0;
606   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
607                                   NULL, NULL, NULL, NULL, NULL);
608   if (result != ERROR_SUCCESS)
609     return 0;
610 
611   return count;
612 }
613 
Valid() const614 bool RegistryKeyIterator::Valid() const {
615   return key_ != NULL && index_ >= 0;
616 }
617 
operator ++()618 void RegistryKeyIterator::operator++() {
619   --index_;
620   Read();
621 }
622 
Read()623 bool RegistryKeyIterator::Read() {
624   if (Valid()) {
625     DWORD ncount = arraysize(name_);
626     FILETIME written;
627     LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
628                             NULL, &written);
629     if (ERROR_SUCCESS == r)
630       return true;
631   }
632 
633   name_[0] = '\0';
634   return false;
635 }
636 
637 }  // namespace win
638 }  // namespace base
639