Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / base / win / registry.cc
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.
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 // Watches for modifications to a key.
38 class RegKey::Watcher : public ObjectWatcher::Delegate {
39  public:
40   explicit Watcher(RegKey* owner) : owner_(owner) {}
41   ~Watcher() {}
42
43   bool StartWatching(HKEY key, const ChangeCallback& callback);
44
45   // Implementation of ObjectWatcher::Delegate.
46   void OnObjectSignaled(HANDLE object) override {
47     DCHECK(watch_event_.IsValid() && watch_event_.Get() == object);
48     ChangeCallback callback = callback_;
49     callback_.Reset();
50     callback.Run();
51   }
52
53  private:
54   RegKey* owner_;
55   ScopedHandle watch_event_;
56   ObjectWatcher object_watcher_;
57   ChangeCallback callback_;
58   DISALLOW_COPY_AND_ASSIGN(Watcher);
59 };
60
61 bool RegKey::Watcher::StartWatching(HKEY key, const ChangeCallback& callback) {
62   DCHECK(key);
63   DCHECK(callback_.is_null());
64
65   if (!watch_event_.IsValid())
66     watch_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
67
68   if (!watch_event_.IsValid())
69     return false;
70
71   DWORD filter = REG_NOTIFY_CHANGE_NAME |
72                  REG_NOTIFY_CHANGE_ATTRIBUTES |
73                  REG_NOTIFY_CHANGE_LAST_SET |
74                  REG_NOTIFY_CHANGE_SECURITY;
75
76   // Watch the registry key for a change of value.
77   LONG result = RegNotifyChangeKeyValue(key, TRUE, filter, watch_event_.Get(),
78                                         TRUE);
79   if (result != ERROR_SUCCESS) {
80     watch_event_.Close();
81     return false;
82   }
83
84   callback_ = callback;
85   return object_watcher_.StartWatching(watch_event_.Get(), this);
86 }
87
88 // RegKey ----------------------------------------------------------------------
89
90 RegKey::RegKey() : key_(NULL), wow64access_(0) {
91 }
92
93 RegKey::RegKey(HKEY key) : key_(key), wow64access_(0) {
94 }
95
96 RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
97     : key_(NULL),
98       wow64access_(0) {
99   if (rootkey) {
100     if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
101       Create(rootkey, subkey, access);
102     else
103       Open(rootkey, subkey, access);
104   } else {
105     DCHECK(!subkey);
106     wow64access_ = access & kWow64AccessMask;
107   }
108 }
109
110 RegKey::~RegKey() {
111   Close();
112 }
113
114 LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
115   DWORD disposition_value;
116   return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
117 }
118
119 LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
120                                    DWORD* disposition, REGSAM access) {
121   DCHECK(rootkey && subkey && access && disposition);
122   HKEY subhkey = NULL;
123   LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
124                                REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
125                                disposition);
126   if (result == ERROR_SUCCESS) {
127     Close();
128     key_ = subhkey;
129     wow64access_ = access & kWow64AccessMask;
130   }
131
132   return result;
133 }
134
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_) {
143     NOTREACHED();
144     return ERROR_INVALID_PARAMETER;
145   }
146   HKEY subkey = NULL;
147   LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
148                                access, NULL, &subkey, NULL);
149   if (result == ERROR_SUCCESS) {
150     Close();
151     key_ = subkey;
152     wow64access_ = access & kWow64AccessMask;
153   }
154
155   return result;
156 }
157
158 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
159   DCHECK(rootkey && subkey && access);
160   HKEY subhkey = NULL;
161
162   LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
163   if (result == ERROR_SUCCESS) {
164     Close();
165     key_ = subhkey;
166     wow64access_ = access & kWow64AccessMask;
167   }
168
169   return result;
170 }
171
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_) {
180     NOTREACHED();
181     return ERROR_INVALID_PARAMETER;
182   }
183   HKEY subkey = NULL;
184   LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
185
186   // We have to close the current opened key before replacing it with the new
187   // one.
188   if (result == ERROR_SUCCESS) {
189     Close();
190     key_ = subkey;
191     wow64access_ = access & kWow64AccessMask;
192   }
193   return result;
194 }
195
196 void RegKey::Close() {
197   if (key_) {
198     ::RegCloseKey(key_);
199     key_ = NULL;
200     wow64access_ = 0;
201   }
202 }
203
204 // TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
205 void RegKey::Set(HKEY key) {
206   if (key_ != key) {
207     Close();
208     key_ = key;
209   }
210 }
211
212 HKEY RegKey::Take() {
213   DCHECK(wow64access_ == 0);
214   HKEY key = key_;
215   key_ = NULL;
216   return key;
217 }
218
219 bool RegKey::HasValue(const wchar_t* name) const {
220   return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
221 }
222
223 DWORD RegKey::GetValueCount() const {
224   DWORD count = 0;
225   LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
226                                 NULL, NULL, NULL, NULL);
227   return (result == ERROR_SUCCESS) ? count : 0;
228 }
229
230 LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
231   wchar_t buf[256];
232   DWORD bufsize = arraysize(buf);
233   LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
234   if (r == ERROR_SUCCESS)
235     *name = buf;
236
237   return r;
238 }
239
240 LONG RegKey::DeleteKey(const wchar_t* name) {
241   DCHECK(key_);
242   DCHECK(name);
243   HKEY subkey = NULL;
244
245   // Verify the key exists before attempting delete to replicate previous
246   // behavior.
247   LONG result =
248       RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
249   if (result != ERROR_SUCCESS)
250     return result;
251   RegCloseKey(subkey);
252
253   return RegDelRecurse(key_, std::wstring(name), wow64access_);
254 }
255
256 LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
257   DCHECK(key_);
258   DCHECK(name);
259
260   HKEY target_key = NULL;
261   LONG result = RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_,
262                              &target_key);
263
264   if (result != ERROR_SUCCESS)
265     return result;
266
267   DWORD count = 0;
268   result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
269                            NULL, NULL, NULL, NULL);
270
271   RegCloseKey(target_key);
272
273   if (result != ERROR_SUCCESS)
274     return result;
275
276   if (count == 0)
277     return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
278
279   return ERROR_DIR_NOT_EMPTY;
280 }
281
282 LONG RegKey::DeleteValue(const wchar_t* value_name) {
283   DCHECK(key_);
284   LONG result = RegDeleteValue(key_, value_name);
285   return result;
286 }
287
288 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
289   DCHECK(out_value);
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;
297     else
298       result = ERROR_CANTREAD;
299   }
300
301   return result;
302 }
303
304 LONG RegKey::ReadInt64(const wchar_t* name, int64* out_value) const {
305   DCHECK(out_value);
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;
314     else
315       result = ERROR_CANTREAD;
316   }
317
318   return result;
319 }
320
321 LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
322   DCHECK(out_value);
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;
339       } else {
340         *out_value = expanded;
341       }
342     } else {
343       // Not a string. Oops.
344       result = ERROR_CANTREAD;
345     }
346   }
347
348   return result;
349 }
350
351 LONG RegKey::ReadValue(const wchar_t* name,
352                        void* data,
353                        DWORD* dsize,
354                        DWORD* dtype) const {
355   LONG result = RegQueryValueEx(key_, name, 0, dtype,
356                                 reinterpret_cast<LPBYTE>(data), dsize);
357   return result;
358 }
359
360 LONG RegKey::ReadValues(const wchar_t* name,
361                         std::vector<std::wstring>* values) {
362   values->clear();
363
364   DWORD type = REG_MULTI_SZ;
365   DWORD size = 0;
366   LONG result = ReadValue(name, NULL, &size, &type);
367   if (FAILED(result) || size == 0)
368     return result;
369
370   if (type != REG_MULTI_SZ)
371     return ERROR_CANTREAD;
372
373   std::vector<wchar_t> buffer(size / sizeof(wchar_t));
374   result = ReadValue(name, &buffer[0], &size, NULL);
375   if (FAILED(result) || size == 0)
376     return result;
377
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;
387   }
388   return 0;
389 }
390
391 LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
392   return WriteValue(
393       name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD);
394 }
395
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);
399 }
400
401 LONG RegKey::WriteValue(const wchar_t* name,
402                         const void* data,
403                         DWORD dsize,
404                         DWORD dtype) {
405   DCHECK(data || !dsize);
406
407   LONG result = RegSetValueEx(key_, name, 0, dtype,
408       reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
409   return result;
410 }
411
412 bool RegKey::StartWatching(const ChangeCallback& callback) {
413   if (!key_watcher_)
414     key_watcher_.reset(new Watcher(this));
415
416   if (!key_watcher_.get()->StartWatching(key_, callback))
417     return false;
418
419   return true;
420 }
421
422 // static
423 LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
424                                    const wchar_t* lpSubKey,
425                                    REGSAM samDesired,
426                                    DWORD Reserved) {
427   typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
428
429   RegDeleteKeyExPtr reg_delete_key_ex_func =
430       reinterpret_cast<RegDeleteKeyExPtr>(
431           GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
432
433   if (reg_delete_key_ex_func)
434     return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved);
435
436   // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
437   return RegDeleteKey(hKey, lpSubKey);
438 }
439
440 // static
441 LONG RegKey::RegDelRecurse(HKEY root_key,
442                            const std::wstring& name,
443                            REGSAM access) {
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)
447     return result;
448
449   HKEY target_key = NULL;
450   result = RegOpenKeyEx(
451       root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key);
452
453   if (result == ERROR_FILE_NOT_FOUND)
454     return ERROR_SUCCESS;
455   if (result != ERROR_SUCCESS)
456     return result;
457
458   std::wstring subkey_name(name);
459
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"\\";
463
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,
472                           0,
473                           WriteInto(&key_name, kMaxKeyNameLength),
474                           &key_size,
475                           NULL,
476                           NULL,
477                           NULL,
478                           NULL);
479
480     if (result != ERROR_SUCCESS)
481       break;
482
483     key_name.resize(key_size);
484     subkey_name.resize(base_key_length);
485     subkey_name += key_name;
486
487     if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
488       break;
489   }
490
491   RegCloseKey(target_key);
492
493   // Try again to delete the key.
494   result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
495
496   return result;
497 }
498
499 // RegistryValueIterator ------------------------------------------------------
500
501 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
502                                              const wchar_t* folder_key,
503                                              REGSAM wow64access)
504     : name_(MAX_PATH, L'\0'),
505       value_(MAX_PATH, L'\0') {
506   Initialize(root_key, folder_key, wow64access);
507 }
508
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);
514 }
515
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));
520   LONG result =
521       RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_);
522   if (result != ERROR_SUCCESS) {
523     key_ = NULL;
524   } else {
525     DWORD count = 0;
526     result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
527                                NULL, NULL, NULL, NULL);
528
529     if (result != ERROR_SUCCESS) {
530       ::RegCloseKey(key_);
531       key_ = NULL;
532     } else {
533       index_ = count - 1;
534     }
535   }
536
537   Read();
538 }
539
540 RegistryValueIterator::~RegistryValueIterator() {
541   if (key_)
542     ::RegCloseKey(key_);
543 }
544
545 DWORD RegistryValueIterator::ValueCount() const {
546   DWORD count = 0;
547   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
548                                   &count, NULL, NULL, NULL, NULL);
549   if (result != ERROR_SUCCESS)
550     return 0;
551
552   return count;
553 }
554
555 bool RegistryValueIterator::Valid() const {
556   return key_ != NULL && index_ >= 0;
557 }
558
559 void RegistryValueIterator::operator++() {
560   --index_;
561   Read();
562 }
563
564 bool RegistryValueIterator::Read() {
565   if (Valid()) {
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_);
573
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_);
589     }
590
591     if (result == ERROR_SUCCESS) {
592       DCHECK_LT(to_wchar_size(value_size_), value_.size());
593       value_[to_wchar_size(value_size_)] = L'\0';
594       return true;
595     }
596   }
597
598   name_[0] = L'\0';
599   value_[0] = L'\0';
600   value_size_ = 0;
601   return false;
602 }
603
604 // RegistryKeyIterator --------------------------------------------------------
605
606 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
607                                          const wchar_t* folder_key) {
608   Initialize(root_key, folder_key, 0);
609 }
610
611 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
612                                          const wchar_t* folder_key,
613                                          REGSAM wow64access) {
614   Initialize(root_key, folder_key, wow64access);
615 }
616
617 RegistryKeyIterator::~RegistryKeyIterator() {
618   if (key_)
619     ::RegCloseKey(key_);
620 }
621
622 DWORD RegistryKeyIterator::SubkeyCount() const {
623   DWORD count = 0;
624   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
625                                   NULL, NULL, NULL, NULL, NULL);
626   if (result != ERROR_SUCCESS)
627     return 0;
628
629   return count;
630 }
631
632 bool RegistryKeyIterator::Valid() const {
633   return key_ != NULL && index_ >= 0;
634 }
635
636 void RegistryKeyIterator::operator++() {
637   --index_;
638   Read();
639 }
640
641 bool RegistryKeyIterator::Read() {
642   if (Valid()) {
643     DWORD ncount = arraysize(name_);
644     FILETIME written;
645     LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
646                             NULL, &written);
647     if (ERROR_SUCCESS == r)
648       return true;
649   }
650
651   name_[0] = '\0';
652   return false;
653 }
654
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));
659   LONG result =
660       RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_);
661   if (result != ERROR_SUCCESS) {
662     key_ = NULL;
663   } else {
664     DWORD count = 0;
665     LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
666                                     NULL, NULL, NULL, NULL, NULL);
667
668     if (result != ERROR_SUCCESS) {
669       ::RegCloseKey(key_);
670       key_ = NULL;
671     } else {
672       index_ = count - 1;
673     }
674   }
675
676   Read();
677 }
678
679 }  // namespace win
680 }  // namespace base