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