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 <stddef.h>
9
10 #include <algorithm>
11 #include <iterator>
12
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/strings/string_util.h"
16 #include "base/win/win_util.h"
17
18 namespace base {
19 namespace win {
20
21 namespace {
22
23 // RegEnumValue() reports the number of characters from the name that were
24 // written to the buffer, not how many there are. This constant is the maximum
25 // name size, such that a buffer with this size should read any name.
26 const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
27
28 // Registry values are read as BYTE* but can have char16_t* data whose last
29 // char16_t is truncated. This function converts the reported |byte_size| to
30 // a size in char16_t that can store a truncated char16_t if necessary.
to_wchar_size(DWORD byte_size)31 inline DWORD to_wchar_size(DWORD byte_size) {
32 return (byte_size + sizeof(char16_t) - 1) / sizeof(char16_t);
33 }
34
35 // Mask to pull WOW64 access flags out of REGSAM access.
36 const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
37
38 } // namespace
39
40 // RegKey ----------------------------------------------------------------------
41
RegKey()42 RegKey::RegKey() : key_(NULL), wow64access_(0) {}
43
RegKey(HKEY key)44 RegKey::RegKey(HKEY key) : key_(key), wow64access_(0) {}
45
RegKey(HKEY rootkey,const char16_t * subkey,REGSAM access)46 RegKey::RegKey(HKEY rootkey, const char16_t* subkey, REGSAM access)
47 : key_(NULL), wow64access_(0) {
48 if (rootkey) {
49 if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
50 Create(rootkey, subkey, access);
51 else
52 Open(rootkey, subkey, access);
53 } else {
54 DCHECK(!subkey);
55 wow64access_ = access & kWow64AccessMask;
56 }
57 }
58
~RegKey()59 RegKey::~RegKey() {
60 Close();
61 }
62
Create(HKEY rootkey,const char16_t * subkey,REGSAM access)63 LONG RegKey::Create(HKEY rootkey, const char16_t* subkey, REGSAM access) {
64 DWORD disposition_value;
65 return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
66 }
67
CreateWithDisposition(HKEY rootkey,const char16_t * subkey,DWORD * disposition,REGSAM access)68 LONG RegKey::CreateWithDisposition(HKEY rootkey,
69 const char16_t* subkey,
70 DWORD* disposition,
71 REGSAM access) {
72 DCHECK(rootkey && subkey && access && disposition);
73 HKEY subhkey = NULL;
74 LONG result = RegCreateKeyEx(rootkey, ToWCharT(subkey), 0, NULL,
75 REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
76 disposition);
77 if (result == ERROR_SUCCESS) {
78 Close();
79 key_ = subhkey;
80 wow64access_ = access & kWow64AccessMask;
81 }
82
83 return result;
84 }
85
CreateKey(const char16_t * name,REGSAM access)86 LONG RegKey::CreateKey(const char16_t* name, REGSAM access) {
87 DCHECK(name && access);
88 // After the application has accessed an alternate registry view using one of
89 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
90 // (create, delete, or open) on child registry keys must explicitly use the
91 // same flag. Otherwise, there can be unexpected behavior.
92 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
93 if ((access & kWow64AccessMask) != wow64access_) {
94 NOTREACHED();
95 return ERROR_INVALID_PARAMETER;
96 }
97 HKEY subkey = NULL;
98 LONG result =
99 RegCreateKeyEx(key_, ToWCharT(name), 0, NULL, REG_OPTION_NON_VOLATILE,
100 access, NULL, &subkey, NULL);
101 if (result == ERROR_SUCCESS) {
102 Close();
103 key_ = subkey;
104 wow64access_ = access & kWow64AccessMask;
105 }
106
107 return result;
108 }
109
Open(HKEY rootkey,const char16_t * subkey,REGSAM access)110 LONG RegKey::Open(HKEY rootkey, const char16_t* subkey, REGSAM access) {
111 DCHECK(rootkey && subkey && access);
112 HKEY subhkey = NULL;
113
114 LONG result = RegOpenKeyEx(rootkey, ToWCharT(subkey), 0, access, &subhkey);
115 if (result == ERROR_SUCCESS) {
116 Close();
117 key_ = subhkey;
118 wow64access_ = access & kWow64AccessMask;
119 }
120
121 return result;
122 }
123
OpenKey(const char16_t * relative_key_name,REGSAM access)124 LONG RegKey::OpenKey(const char16_t* relative_key_name, REGSAM access) {
125 DCHECK(relative_key_name && access);
126 // After the application has accessed an alternate registry view using one of
127 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
128 // (create, delete, or open) on child registry keys must explicitly use the
129 // same flag. Otherwise, there can be unexpected behavior.
130 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
131 if ((access & kWow64AccessMask) != wow64access_) {
132 NOTREACHED();
133 return ERROR_INVALID_PARAMETER;
134 }
135 HKEY subkey = NULL;
136 LONG result =
137 RegOpenKeyEx(key_, ToWCharT(relative_key_name), 0, access, &subkey);
138
139 // We have to close the current opened key before replacing it with the new
140 // one.
141 if (result == ERROR_SUCCESS) {
142 Close();
143 key_ = subkey;
144 wow64access_ = access & kWow64AccessMask;
145 }
146 return result;
147 }
148
Close()149 void RegKey::Close() {
150 if (key_) {
151 ::RegCloseKey(key_);
152 key_ = NULL;
153 wow64access_ = 0;
154 }
155 }
156
157 // TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
Set(HKEY key)158 void RegKey::Set(HKEY key) {
159 if (key_ != key) {
160 Close();
161 key_ = key;
162 }
163 }
164
Take()165 HKEY RegKey::Take() {
166 DCHECK_EQ(wow64access_, 0u);
167 HKEY key = key_;
168 key_ = NULL;
169 return key;
170 }
171
HasValue(const char16_t * name) const172 bool RegKey::HasValue(const char16_t* name) const {
173 return RegQueryValueEx(key_, ToWCharT(name), 0, NULL, NULL, NULL) ==
174 ERROR_SUCCESS;
175 }
176
GetValueCount() const177 DWORD RegKey::GetValueCount() const {
178 DWORD count = 0;
179 LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
180 NULL, NULL, NULL, NULL);
181 return (result == ERROR_SUCCESS) ? count : 0;
182 }
183
GetValueNameAt(int index,std::u16string * name) const184 LONG RegKey::GetValueNameAt(int index, std::u16string* name) const {
185 char16_t buf[256];
186 DWORD bufsize = std::size(buf);
187 LONG r = ::RegEnumValue(key_, index, ToWCharT(buf), &bufsize, NULL, NULL,
188 NULL, NULL);
189 if (r == ERROR_SUCCESS)
190 *name = buf;
191
192 return r;
193 }
194
DeleteKey(const char16_t * name)195 LONG RegKey::DeleteKey(const char16_t* name) {
196 DCHECK(key_);
197 DCHECK(name);
198 HKEY subkey = NULL;
199
200 // Verify the key exists before attempting delete to replicate previous
201 // behavior.
202 LONG result = RegOpenKeyEx(key_, ToWCharT(name), 0,
203 READ_CONTROL | wow64access_, &subkey);
204 if (result != ERROR_SUCCESS)
205 return result;
206 RegCloseKey(subkey);
207
208 return RegDelRecurse(key_, std::u16string(name), wow64access_);
209 }
210
DeleteEmptyKey(const char16_t * name)211 LONG RegKey::DeleteEmptyKey(const char16_t* name) {
212 DCHECK(key_);
213 DCHECK(name);
214
215 HKEY target_key = NULL;
216 LONG result = RegOpenKeyEx(key_, ToWCharT(name), 0, KEY_READ | wow64access_,
217 &target_key);
218
219 if (result != ERROR_SUCCESS)
220 return result;
221
222 DWORD count = 0;
223 result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
224 NULL, NULL, NULL, NULL);
225
226 RegCloseKey(target_key);
227
228 if (result != ERROR_SUCCESS)
229 return result;
230
231 if (count == 0)
232 return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
233
234 return ERROR_DIR_NOT_EMPTY;
235 }
236
DeleteValue(const char16_t * value_name)237 LONG RegKey::DeleteValue(const char16_t* value_name) {
238 DCHECK(key_);
239 LONG result = RegDeleteValue(key_, ToWCharT(value_name));
240 return result;
241 }
242
ReadValueDW(const char16_t * name,DWORD * out_value) const243 LONG RegKey::ReadValueDW(const char16_t* name, DWORD* out_value) const {
244 DCHECK(out_value);
245 DWORD type = REG_DWORD;
246 DWORD size = sizeof(DWORD);
247 DWORD local_value = 0;
248 LONG result = ReadValue(name, &local_value, &size, &type);
249 if (result == ERROR_SUCCESS) {
250 if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD))
251 *out_value = local_value;
252 else
253 result = ERROR_CANTREAD;
254 }
255
256 return result;
257 }
258
ReadInt64(const char16_t * name,int64_t * out_value) const259 LONG RegKey::ReadInt64(const char16_t* name, int64_t* out_value) const {
260 DCHECK(out_value);
261 DWORD type = REG_QWORD;
262 int64_t local_value = 0;
263 DWORD size = sizeof(local_value);
264 LONG result = ReadValue(name, &local_value, &size, &type);
265 if (result == ERROR_SUCCESS) {
266 if ((type == REG_QWORD || type == REG_BINARY) &&
267 size == sizeof(local_value))
268 *out_value = local_value;
269 else
270 result = ERROR_CANTREAD;
271 }
272
273 return result;
274 }
275
ReadValue(const char16_t * name,std::u16string * out_value) const276 LONG RegKey::ReadValue(const char16_t* name, std::u16string* out_value) const {
277 DCHECK(out_value);
278 const size_t kMaxStringLength = 1024; // This is after expansion.
279 // Use the one of the other forms of ReadValue if 1024 is too small for you.
280 char16_t raw_value[kMaxStringLength];
281 DWORD type = REG_SZ, size = sizeof(raw_value);
282 LONG result = ReadValue(name, raw_value, &size, &type);
283 if (result == ERROR_SUCCESS) {
284 if (type == REG_SZ) {
285 *out_value = raw_value;
286 } else if (type == REG_EXPAND_SZ) {
287 char16_t expanded[kMaxStringLength];
288 size = ExpandEnvironmentStrings(ToWCharT(raw_value), ToWCharT(expanded),
289 kMaxStringLength);
290 // Success: returns the number of char16_t's copied
291 // Fail: buffer too small, returns the size required
292 // Fail: other, returns 0
293 if (size == 0 || size > kMaxStringLength) {
294 result = ERROR_MORE_DATA;
295 } else {
296 *out_value = expanded;
297 }
298 } else {
299 // Not a string. Oops.
300 result = ERROR_CANTREAD;
301 }
302 }
303
304 return result;
305 }
306
ReadValue(const char16_t * name,void * data,DWORD * dsize,DWORD * dtype) const307 LONG RegKey::ReadValue(const char16_t* name,
308 void* data,
309 DWORD* dsize,
310 DWORD* dtype) const {
311 LONG result = RegQueryValueEx(key_, ToWCharT(name), 0, dtype,
312 reinterpret_cast<LPBYTE>(data), dsize);
313 return result;
314 }
315
ReadValues(const char16_t * name,std::vector<std::u16string> * values)316 LONG RegKey::ReadValues(const char16_t* name,
317 std::vector<std::u16string>* values) {
318 values->clear();
319
320 DWORD type = REG_MULTI_SZ;
321 DWORD size = 0;
322 LONG result = ReadValue(name, NULL, &size, &type);
323 if (result != ERROR_SUCCESS || size == 0)
324 return result;
325
326 if (type != REG_MULTI_SZ)
327 return ERROR_CANTREAD;
328
329 std::vector<char16_t> buffer(size / sizeof(char16_t));
330 result = ReadValue(name, &buffer[0], &size, NULL);
331 if (result != ERROR_SUCCESS || size == 0)
332 return result;
333
334 // Parse the double-null-terminated list of strings.
335 // Note: This code is paranoid to not read outside of |buf|, in the case where
336 // it may not be properly terminated.
337 const char16_t* entry = &buffer[0];
338 const char16_t* buffer_end = entry + (size / sizeof(char16_t));
339 while (entry < buffer_end && entry[0] != '\0') {
340 const char16_t* entry_end = std::find(entry, buffer_end, L'\0');
341 values->push_back(std::u16string(entry, entry_end));
342 entry = entry_end + 1;
343 }
344 return 0;
345 }
346
WriteValue(const char16_t * name,DWORD in_value)347 LONG RegKey::WriteValue(const char16_t* name, DWORD in_value) {
348 return WriteValue(name, &in_value, static_cast<DWORD>(sizeof(in_value)),
349 REG_DWORD);
350 }
351
WriteValue(const char16_t * name,const char16_t * in_value)352 LONG RegKey::WriteValue(const char16_t* name, const char16_t* in_value) {
353 return WriteValue(
354 name, in_value,
355 static_cast<DWORD>(sizeof(*in_value) * (wcslen(ToWCharT(in_value)) + 1)),
356 REG_SZ);
357 }
358
WriteValue(const char16_t * name,const void * data,DWORD dsize,DWORD dtype)359 LONG RegKey::WriteValue(const char16_t* name,
360 const void* data,
361 DWORD dsize,
362 DWORD dtype) {
363 DCHECK(data || !dsize);
364
365 LONG result =
366 RegSetValueEx(key_, ToWCharT(name), 0, dtype,
367 reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
368 return result;
369 }
370
371 // static
RegDeleteKeyExWrapper(HKEY hKey,const char16_t * lpSubKey,REGSAM samDesired,DWORD Reserved)372 LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
373 const char16_t* lpSubKey,
374 REGSAM samDesired,
375 DWORD Reserved) {
376 typedef LSTATUS(WINAPI * RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
377
378 RegDeleteKeyExPtr reg_delete_key_ex_func =
379 reinterpret_cast<RegDeleteKeyExPtr>(
380 GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
381
382 if (reg_delete_key_ex_func)
383 return reg_delete_key_ex_func(hKey, ToWCharT(lpSubKey), samDesired,
384 Reserved);
385
386 // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
387 return RegDeleteKey(hKey, ToWCharT(lpSubKey));
388 }
389
390 // static
RegDelRecurse(HKEY root_key,const std::u16string & name,REGSAM access)391 LONG RegKey::RegDelRecurse(HKEY root_key,
392 const std::u16string& name,
393 REGSAM access) {
394 // First, see if the key can be deleted without having to recurse.
395 LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
396 if (result == ERROR_SUCCESS)
397 return result;
398
399 HKEY target_key = NULL;
400 result = RegOpenKeyEx(root_key, ToWCharT(&name), 0,
401 KEY_ENUMERATE_SUB_KEYS | access, &target_key);
402
403 if (result == ERROR_FILE_NOT_FOUND)
404 return ERROR_SUCCESS;
405 if (result != ERROR_SUCCESS)
406 return result;
407
408 std::u16string subkey_name(name);
409
410 // Check for an ending slash and add one if it is missing.
411 if (!name.empty() && subkey_name[name.length() - 1] != '\\')
412 subkey_name += u"\\";
413
414 // Enumerate the keys
415 result = ERROR_SUCCESS;
416 const DWORD kMaxKeyNameLength = MAX_PATH;
417 const size_t base_key_length = subkey_name.length();
418 std::u16string key_name;
419 while (result == ERROR_SUCCESS) {
420 DWORD key_size = kMaxKeyNameLength;
421 result = RegEnumKeyEx(target_key, 0,
422 ToWCharT(WriteInto(&key_name, kMaxKeyNameLength)),
423 &key_size, NULL, NULL, NULL, NULL);
424
425 if (result != ERROR_SUCCESS)
426 break;
427
428 key_name.resize(key_size);
429 subkey_name.resize(base_key_length);
430 subkey_name += key_name;
431
432 if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
433 break;
434 }
435
436 RegCloseKey(target_key);
437
438 // Try again to delete the key.
439 result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
440
441 return result;
442 }
443
444 // RegistryValueIterator ------------------------------------------------------
445
RegistryValueIterator(HKEY root_key,const char16_t * folder_key,REGSAM wow64access)446 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
447 const char16_t* folder_key,
448 REGSAM wow64access)
449 : name_(MAX_PATH, L'\0'), value_(MAX_PATH, L'\0') {
450 Initialize(root_key, folder_key, wow64access);
451 }
452
RegistryValueIterator(HKEY root_key,const char16_t * folder_key)453 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
454 const char16_t* folder_key)
455 : name_(MAX_PATH, L'\0'), value_(MAX_PATH, L'\0') {
456 Initialize(root_key, folder_key, 0);
457 }
458
Initialize(HKEY root_key,const char16_t * folder_key,REGSAM wow64access)459 void RegistryValueIterator::Initialize(HKEY root_key,
460 const char16_t* folder_key,
461 REGSAM wow64access) {
462 DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
463 LONG result = RegOpenKeyEx(root_key, ToWCharT(folder_key), 0,
464 KEY_READ | wow64access, &key_);
465 if (result != ERROR_SUCCESS) {
466 key_ = NULL;
467 } else {
468 DWORD count = 0;
469 result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
470 NULL, NULL, NULL, NULL);
471
472 if (result != ERROR_SUCCESS) {
473 ::RegCloseKey(key_);
474 key_ = NULL;
475 } else {
476 index_ = count - 1;
477 }
478 }
479
480 Read();
481 }
482
~RegistryValueIterator()483 RegistryValueIterator::~RegistryValueIterator() {
484 if (key_)
485 ::RegCloseKey(key_);
486 }
487
ValueCount() const488 DWORD RegistryValueIterator::ValueCount() const {
489 DWORD count = 0;
490 LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
491 NULL, NULL, NULL, NULL);
492 if (result != ERROR_SUCCESS)
493 return 0;
494
495 return count;
496 }
497
Valid() const498 bool RegistryValueIterator::Valid() const {
499 return key_ != NULL && index_ >= 0;
500 }
501
operator ++()502 void RegistryValueIterator::operator++() {
503 --index_;
504 Read();
505 }
506
Read()507 bool RegistryValueIterator::Read() {
508 if (Valid()) {
509 DWORD capacity = static_cast<DWORD>(name_.capacity());
510 DWORD name_size = capacity;
511 // |value_size_| is in bytes. Reserve the last character for a NUL.
512 value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(char16_t));
513 LONG result = ::RegEnumValue(
514 key_, index_, ToWCharT(WriteInto(&name_, name_size)), &name_size, NULL,
515 &type_, reinterpret_cast<BYTE*>(value_.data()), &value_size_);
516
517 if (result == ERROR_MORE_DATA) {
518 // Registry key names are limited to 255 characters and fit within
519 // MAX_PATH (which is 260) but registry value names can use up to 16,383
520 // characters and the value itself is not limited
521 // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
522 // ms724872(v=vs.85).aspx).
523 // Resize the buffers and retry if their size caused the failure.
524 DWORD value_size_in_wchars = to_wchar_size(value_size_);
525 if (value_size_in_wchars + 1 > value_.size())
526 value_.resize(value_size_in_wchars + 1, L'\0');
527 value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(char16_t));
528 name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
529 result = ::RegEnumValue(
530 key_, index_, ToWCharT(WriteInto(&name_, name_size)), &name_size,
531 NULL, &type_, reinterpret_cast<BYTE*>(value_.data()), &value_size_);
532 }
533
534 if (result == ERROR_SUCCESS) {
535 DCHECK_LT(to_wchar_size(value_size_), value_.size());
536 value_[to_wchar_size(value_size_)] = L'\0';
537 return true;
538 }
539 }
540
541 name_[0] = L'\0';
542 value_[0] = L'\0';
543 value_size_ = 0;
544 return false;
545 }
546
547 // RegistryKeyIterator --------------------------------------------------------
548
RegistryKeyIterator(HKEY root_key,const char16_t * folder_key)549 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
550 const char16_t* folder_key) {
551 Initialize(root_key, folder_key, 0);
552 }
553
RegistryKeyIterator(HKEY root_key,const char16_t * folder_key,REGSAM wow64access)554 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
555 const char16_t* folder_key,
556 REGSAM wow64access) {
557 Initialize(root_key, folder_key, wow64access);
558 }
559
~RegistryKeyIterator()560 RegistryKeyIterator::~RegistryKeyIterator() {
561 if (key_)
562 ::RegCloseKey(key_);
563 }
564
SubkeyCount() const565 DWORD RegistryKeyIterator::SubkeyCount() const {
566 DWORD count = 0;
567 LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL,
568 NULL, NULL, NULL, NULL);
569 if (result != ERROR_SUCCESS)
570 return 0;
571
572 return count;
573 }
574
Valid() const575 bool RegistryKeyIterator::Valid() const {
576 return key_ != NULL && index_ >= 0;
577 }
578
operator ++()579 void RegistryKeyIterator::operator++() {
580 --index_;
581 Read();
582 }
583
Read()584 bool RegistryKeyIterator::Read() {
585 if (Valid()) {
586 DWORD ncount = std::size(name_);
587 FILETIME written;
588 LONG r = ::RegEnumKeyEx(key_, index_, ToWCharT(name_), &ncount, NULL, NULL,
589 NULL, &written);
590 if (ERROR_SUCCESS == r)
591 return true;
592 }
593
594 name_[0] = '\0';
595 return false;
596 }
597
Initialize(HKEY root_key,const char16_t * folder_key,REGSAM wow64access)598 void RegistryKeyIterator::Initialize(HKEY root_key,
599 const char16_t* folder_key,
600 REGSAM wow64access) {
601 DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
602 LONG result = RegOpenKeyEx(root_key, ToWCharT(folder_key), 0,
603 KEY_READ | wow64access, &key_);
604 if (result != ERROR_SUCCESS) {
605 key_ = NULL;
606 } else {
607 DWORD count = 0;
608 result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL,
609 NULL, NULL, NULL, NULL);
610
611 if (result != ERROR_SUCCESS) {
612 ::RegCloseKey(key_);
613 key_ = NULL;
614 } else {
615 index_ = count - 1;
616 }
617 }
618
619 Read();
620 }
621
622 } // namespace win
623 } // namespace base
624