| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/win/registry.h" | 
 |  | 
 | #include <shlwapi.h> | 
 | #include <stddef.h> | 
 | #include <algorithm> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/macros.h" | 
 | #include "base/strings/string_util.h" | 
 |  | 
 | namespace base { | 
 | namespace win { | 
 |  | 
 | namespace { | 
 |  | 
 | // RegEnumValue() reports the number of characters from the name that were | 
 | // written to the buffer, not how many there are. This constant is the maximum | 
 | // name size, such that a buffer with this size should read any name. | 
 | const DWORD MAX_REGISTRY_NAME_SIZE = 16384; | 
 |  | 
 | // Registry values are read as BYTE* but can have wchar_t* data whose last | 
 | // wchar_t is truncated. This function converts the reported |byte_size| to | 
 | // a size in wchar_t that can store a truncated wchar_t if necessary. | 
 | inline DWORD to_wchar_size(DWORD byte_size) { | 
 |   return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t); | 
 | } | 
 |  | 
 | // Mask to pull WOW64 access flags out of REGSAM access. | 
 | const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // RegKey ---------------------------------------------------------------------- | 
 |  | 
 | RegKey::RegKey() : key_(NULL), wow64access_(0) {} | 
 |  | 
 | RegKey::RegKey(HKEY key) : key_(key), wow64access_(0) {} | 
 |  | 
 | RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) | 
 |     : key_(NULL), wow64access_(0) { | 
 |   if (rootkey) { | 
 |     if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) | 
 |       Create(rootkey, subkey, access); | 
 |     else | 
 |       Open(rootkey, subkey, access); | 
 |   } else { | 
 |     DCHECK(!subkey); | 
 |     wow64access_ = access & kWow64AccessMask; | 
 |   } | 
 | } | 
 |  | 
 | RegKey::~RegKey() { | 
 |   Close(); | 
 | } | 
 |  | 
 | LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { | 
 |   DWORD disposition_value; | 
 |   return CreateWithDisposition(rootkey, subkey, &disposition_value, access); | 
 | } | 
 |  | 
 | LONG RegKey::CreateWithDisposition(HKEY rootkey, | 
 |                                    const wchar_t* subkey, | 
 |                                    DWORD* disposition, | 
 |                                    REGSAM access) { | 
 |   DCHECK(rootkey && subkey && access && disposition); | 
 |   HKEY subhkey = NULL; | 
 |   LONG result = | 
 |       RegCreateKeyEx(rootkey, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, access, | 
 |                      NULL, &subhkey, disposition); | 
 |   if (result == ERROR_SUCCESS) { | 
 |     Close(); | 
 |     key_ = subhkey; | 
 |     wow64access_ = access & kWow64AccessMask; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) { | 
 |   DCHECK(name && access); | 
 |   // After the application has accessed an alternate registry view using one of | 
 |   // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations | 
 |   // (create, delete, or open) on child registry keys must explicitly use the | 
 |   // same flag. Otherwise, there can be unexpected behavior. | 
 |   // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx. | 
 |   if ((access & kWow64AccessMask) != wow64access_) { | 
 |     NOTREACHED(); | 
 |     return ERROR_INVALID_PARAMETER; | 
 |   } | 
 |   HKEY subkey = NULL; | 
 |   LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE, | 
 |                                access, NULL, &subkey, NULL); | 
 |   if (result == ERROR_SUCCESS) { | 
 |     Close(); | 
 |     key_ = subkey; | 
 |     wow64access_ = access & kWow64AccessMask; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { | 
 |   DCHECK(rootkey && subkey && access); | 
 |   HKEY subhkey = NULL; | 
 |  | 
 |   LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey); | 
 |   if (result == ERROR_SUCCESS) { | 
 |     Close(); | 
 |     key_ = subhkey; | 
 |     wow64access_ = access & kWow64AccessMask; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) { | 
 |   DCHECK(relative_key_name && access); | 
 |   // After the application has accessed an alternate registry view using one of | 
 |   // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations | 
 |   // (create, delete, or open) on child registry keys must explicitly use the | 
 |   // same flag. Otherwise, there can be unexpected behavior. | 
 |   // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx. | 
 |   if ((access & kWow64AccessMask) != wow64access_) { | 
 |     NOTREACHED(); | 
 |     return ERROR_INVALID_PARAMETER; | 
 |   } | 
 |   HKEY subkey = NULL; | 
 |   LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey); | 
 |  | 
 |   // We have to close the current opened key before replacing it with the new | 
 |   // one. | 
 |   if (result == ERROR_SUCCESS) { | 
 |     Close(); | 
 |     key_ = subkey; | 
 |     wow64access_ = access & kWow64AccessMask; | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | void RegKey::Close() { | 
 |   if (key_) { | 
 |     ::RegCloseKey(key_); | 
 |     key_ = NULL; | 
 |     wow64access_ = 0; | 
 |   } | 
 | } | 
 |  | 
 | // TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400 | 
 | void RegKey::Set(HKEY key) { | 
 |   if (key_ != key) { | 
 |     Close(); | 
 |     key_ = key; | 
 |   } | 
 | } | 
 |  | 
 | HKEY RegKey::Take() { | 
 |   DCHECK_EQ(wow64access_, 0u); | 
 |   HKEY key = key_; | 
 |   key_ = NULL; | 
 |   return key; | 
 | } | 
 |  | 
 | bool RegKey::HasValue(const wchar_t* name) const { | 
 |   return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS; | 
 | } | 
 |  | 
 | DWORD RegKey::GetValueCount() const { | 
 |   DWORD count = 0; | 
 |   LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, | 
 |                                 NULL, NULL, NULL, NULL); | 
 |   return (result == ERROR_SUCCESS) ? count : 0; | 
 | } | 
 |  | 
 | LONG RegKey::GetValueNameAt(int index, std::wstring* name) const { | 
 |   wchar_t buf[256]; | 
 |   DWORD bufsize = arraysize(buf); | 
 |   LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL); | 
 |   if (r == ERROR_SUCCESS) | 
 |     *name = buf; | 
 |  | 
 |   return r; | 
 | } | 
 |  | 
 | LONG RegKey::DeleteKey(const wchar_t* name) { | 
 |   DCHECK(key_); | 
 |   DCHECK(name); | 
 |   HKEY subkey = NULL; | 
 |  | 
 |   // Verify the key exists before attempting delete to replicate previous | 
 |   // behavior. | 
 |   LONG result = | 
 |       RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey); | 
 |   if (result != ERROR_SUCCESS) | 
 |     return result; | 
 |   RegCloseKey(subkey); | 
 |  | 
 |   return RegDelRecurse(key_, std::wstring(name), wow64access_); | 
 | } | 
 |  | 
 | LONG RegKey::DeleteEmptyKey(const wchar_t* name) { | 
 |   DCHECK(key_); | 
 |   DCHECK(name); | 
 |  | 
 |   HKEY target_key = NULL; | 
 |   LONG result = | 
 |       RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_, &target_key); | 
 |  | 
 |   if (result != ERROR_SUCCESS) | 
 |     return result; | 
 |  | 
 |   DWORD count = 0; | 
 |   result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count, | 
 |                            NULL, NULL, NULL, NULL); | 
 |  | 
 |   RegCloseKey(target_key); | 
 |  | 
 |   if (result != ERROR_SUCCESS) | 
 |     return result; | 
 |  | 
 |   if (count == 0) | 
 |     return RegDeleteKeyExWrapper(key_, name, wow64access_, 0); | 
 |  | 
 |   return ERROR_DIR_NOT_EMPTY; | 
 | } | 
 |  | 
 | LONG RegKey::DeleteValue(const wchar_t* value_name) { | 
 |   DCHECK(key_); | 
 |   LONG result = RegDeleteValue(key_, value_name); | 
 |   return result; | 
 | } | 
 |  | 
 | LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const { | 
 |   DCHECK(out_value); | 
 |   DWORD type = REG_DWORD; | 
 |   DWORD size = sizeof(DWORD); | 
 |   DWORD local_value = 0; | 
 |   LONG result = ReadValue(name, &local_value, &size, &type); | 
 |   if (result == ERROR_SUCCESS) { | 
 |     if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD)) | 
 |       *out_value = local_value; | 
 |     else | 
 |       result = ERROR_CANTREAD; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | LONG RegKey::ReadInt64(const wchar_t* name, int64_t* out_value) const { | 
 |   DCHECK(out_value); | 
 |   DWORD type = REG_QWORD; | 
 |   int64_t local_value = 0; | 
 |   DWORD size = sizeof(local_value); | 
 |   LONG result = ReadValue(name, &local_value, &size, &type); | 
 |   if (result == ERROR_SUCCESS) { | 
 |     if ((type == REG_QWORD || type == REG_BINARY) && | 
 |         size == sizeof(local_value)) | 
 |       *out_value = local_value; | 
 |     else | 
 |       result = ERROR_CANTREAD; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const { | 
 |   DCHECK(out_value); | 
 |   const size_t kMaxStringLength = 1024;  // This is after expansion. | 
 |   // Use the one of the other forms of ReadValue if 1024 is too small for you. | 
 |   wchar_t raw_value[kMaxStringLength]; | 
 |   DWORD type = REG_SZ, size = sizeof(raw_value); | 
 |   LONG result = ReadValue(name, raw_value, &size, &type); | 
 |   if (result == ERROR_SUCCESS) { | 
 |     if (type == REG_SZ) { | 
 |       *out_value = raw_value; | 
 |     } else if (type == REG_EXPAND_SZ) { | 
 |       wchar_t expanded[kMaxStringLength]; | 
 |       size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength); | 
 |       // Success: returns the number of wchar_t's copied | 
 |       // Fail: buffer too small, returns the size required | 
 |       // Fail: other, returns 0 | 
 |       if (size == 0 || size > kMaxStringLength) { | 
 |         result = ERROR_MORE_DATA; | 
 |       } else { | 
 |         *out_value = expanded; | 
 |       } | 
 |     } else { | 
 |       // Not a string. Oops. | 
 |       result = ERROR_CANTREAD; | 
 |     } | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | LONG RegKey::ReadValue(const wchar_t* name, | 
 |                        void* data, | 
 |                        DWORD* dsize, | 
 |                        DWORD* dtype) const { | 
 |   LONG result = RegQueryValueEx(key_, name, 0, dtype, | 
 |                                 reinterpret_cast<LPBYTE>(data), dsize); | 
 |   return result; | 
 | } | 
 |  | 
 | LONG RegKey::ReadValues(const wchar_t* name, | 
 |                         std::vector<std::wstring>* values) { | 
 |   values->clear(); | 
 |  | 
 |   DWORD type = REG_MULTI_SZ; | 
 |   DWORD size = 0; | 
 |   LONG result = ReadValue(name, NULL, &size, &type); | 
 |   if (result != ERROR_SUCCESS || size == 0) | 
 |     return result; | 
 |  | 
 |   if (type != REG_MULTI_SZ) | 
 |     return ERROR_CANTREAD; | 
 |  | 
 |   std::vector<wchar_t> buffer(size / sizeof(wchar_t)); | 
 |   result = ReadValue(name, &buffer[0], &size, NULL); | 
 |   if (result != ERROR_SUCCESS || size == 0) | 
 |     return result; | 
 |  | 
 |   // Parse the double-null-terminated list of strings. | 
 |   // Note: This code is paranoid to not read outside of |buf|, in the case where | 
 |   // it may not be properly terminated. | 
 |   const wchar_t* entry = &buffer[0]; | 
 |   const wchar_t* buffer_end = entry + (size / sizeof(wchar_t)); | 
 |   while (entry < buffer_end && entry[0] != '\0') { | 
 |     const wchar_t* entry_end = std::find(entry, buffer_end, L'\0'); | 
 |     values->push_back(std::wstring(entry, entry_end)); | 
 |     entry = entry_end + 1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) { | 
 |   return WriteValue(name, &in_value, static_cast<DWORD>(sizeof(in_value)), | 
 |                     REG_DWORD); | 
 | } | 
 |  | 
 | LONG RegKey::WriteValue(const wchar_t* name, const wchar_t* in_value) { | 
 |   return WriteValue( | 
 |       name, in_value, | 
 |       static_cast<DWORD>(sizeof(*in_value) * (wcslen(in_value) + 1)), REG_SZ); | 
 | } | 
 |  | 
 | LONG RegKey::WriteValue(const wchar_t* name, | 
 |                         const void* data, | 
 |                         DWORD dsize, | 
 |                         DWORD dtype) { | 
 |   DCHECK(data || !dsize); | 
 |  | 
 |   LONG result = | 
 |       RegSetValueEx(key_, name, 0, dtype, | 
 |                     reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize); | 
 |   return result; | 
 | } | 
 |  | 
 | // static | 
 | LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey, | 
 |                                    const wchar_t* lpSubKey, | 
 |                                    REGSAM samDesired, | 
 |                                    DWORD Reserved) { | 
 |   typedef LSTATUS(WINAPI * RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD); | 
 |  | 
 |   RegDeleteKeyExPtr reg_delete_key_ex_func = | 
 |       reinterpret_cast<RegDeleteKeyExPtr>( | 
 |           GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW")); | 
 |  | 
 |   if (reg_delete_key_ex_func) | 
 |     return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved); | 
 |  | 
 |   // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey. | 
 |   return RegDeleteKey(hKey, lpSubKey); | 
 | } | 
 |  | 
 | // static | 
 | LONG RegKey::RegDelRecurse(HKEY root_key, | 
 |                            const std::wstring& name, | 
 |                            REGSAM access) { | 
 |   // First, see if the key can be deleted without having to recurse. | 
 |   LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0); | 
 |   if (result == ERROR_SUCCESS) | 
 |     return result; | 
 |  | 
 |   HKEY target_key = NULL; | 
 |   result = RegOpenKeyEx(root_key, name.c_str(), 0, | 
 |                         KEY_ENUMERATE_SUB_KEYS | access, &target_key); | 
 |  | 
 |   if (result == ERROR_FILE_NOT_FOUND) | 
 |     return ERROR_SUCCESS; | 
 |   if (result != ERROR_SUCCESS) | 
 |     return result; | 
 |  | 
 |   std::wstring subkey_name(name); | 
 |  | 
 |   // Check for an ending slash and add one if it is missing. | 
 |   if (!name.empty() && subkey_name[name.length() - 1] != L'\\') | 
 |     subkey_name += L"\\"; | 
 |  | 
 |   // Enumerate the keys | 
 |   result = ERROR_SUCCESS; | 
 |   const DWORD kMaxKeyNameLength = MAX_PATH; | 
 |   const size_t base_key_length = subkey_name.length(); | 
 |   std::wstring key_name; | 
 |   while (result == ERROR_SUCCESS) { | 
 |     DWORD key_size = kMaxKeyNameLength; | 
 |     result = | 
 |         RegEnumKeyEx(target_key, 0, WriteInto(&key_name, kMaxKeyNameLength), | 
 |                      &key_size, NULL, NULL, NULL, NULL); | 
 |  | 
 |     if (result != ERROR_SUCCESS) | 
 |       break; | 
 |  | 
 |     key_name.resize(key_size); | 
 |     subkey_name.resize(base_key_length); | 
 |     subkey_name += key_name; | 
 |  | 
 |     if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS) | 
 |       break; | 
 |   } | 
 |  | 
 |   RegCloseKey(target_key); | 
 |  | 
 |   // Try again to delete the key. | 
 |   result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | // RegistryValueIterator ------------------------------------------------------ | 
 |  | 
 | RegistryValueIterator::RegistryValueIterator(HKEY root_key, | 
 |                                              const wchar_t* folder_key, | 
 |                                              REGSAM wow64access) | 
 |     : name_(MAX_PATH, L'\0'), value_(MAX_PATH, L'\0') { | 
 |   Initialize(root_key, folder_key, wow64access); | 
 | } | 
 |  | 
 | RegistryValueIterator::RegistryValueIterator(HKEY root_key, | 
 |                                              const wchar_t* folder_key) | 
 |     : name_(MAX_PATH, L'\0'), value_(MAX_PATH, L'\0') { | 
 |   Initialize(root_key, folder_key, 0); | 
 | } | 
 |  | 
 | void RegistryValueIterator::Initialize(HKEY root_key, | 
 |                                        const wchar_t* folder_key, | 
 |                                        REGSAM wow64access) { | 
 |   DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0)); | 
 |   LONG result = | 
 |       RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_); | 
 |   if (result != ERROR_SUCCESS) { | 
 |     key_ = NULL; | 
 |   } else { | 
 |     DWORD count = 0; | 
 |     result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, | 
 |                                NULL, NULL, NULL, NULL); | 
 |  | 
 |     if (result != ERROR_SUCCESS) { | 
 |       ::RegCloseKey(key_); | 
 |       key_ = NULL; | 
 |     } else { | 
 |       index_ = count - 1; | 
 |     } | 
 |   } | 
 |  | 
 |   Read(); | 
 | } | 
 |  | 
 | RegistryValueIterator::~RegistryValueIterator() { | 
 |   if (key_) | 
 |     ::RegCloseKey(key_); | 
 | } | 
 |  | 
 | DWORD RegistryValueIterator::ValueCount() const { | 
 |   DWORD count = 0; | 
 |   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, | 
 |                                   NULL, NULL, NULL, NULL); | 
 |   if (result != ERROR_SUCCESS) | 
 |     return 0; | 
 |  | 
 |   return count; | 
 | } | 
 |  | 
 | bool RegistryValueIterator::Valid() const { | 
 |   return key_ != NULL && index_ >= 0; | 
 | } | 
 |  | 
 | void RegistryValueIterator::operator++() { | 
 |   --index_; | 
 |   Read(); | 
 | } | 
 |  | 
 | bool RegistryValueIterator::Read() { | 
 |   if (Valid()) { | 
 |     DWORD capacity = static_cast<DWORD>(name_.capacity()); | 
 |     DWORD name_size = capacity; | 
 |     // |value_size_| is in bytes. Reserve the last character for a NUL. | 
 |     value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t)); | 
 |     LONG result = ::RegEnumValue( | 
 |         key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_, | 
 |         reinterpret_cast<BYTE*>(value_.data()), &value_size_); | 
 |  | 
 |     if (result == ERROR_MORE_DATA) { | 
 |       // Registry key names are limited to 255 characters and fit within | 
 |       // MAX_PATH (which is 260) but registry value names can use up to 16,383 | 
 |       // characters and the value itself is not limited | 
 |       // (from http://msdn.microsoft.com/en-us/library/windows/desktop/ | 
 |       // ms724872(v=vs.85).aspx). | 
 |       // Resize the buffers and retry if their size caused the failure. | 
 |       DWORD value_size_in_wchars = to_wchar_size(value_size_); | 
 |       if (value_size_in_wchars + 1 > value_.size()) | 
 |         value_.resize(value_size_in_wchars + 1, L'\0'); | 
 |       value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t)); | 
 |       name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity; | 
 |       result = ::RegEnumValue( | 
 |           key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_, | 
 |           reinterpret_cast<BYTE*>(value_.data()), &value_size_); | 
 |     } | 
 |  | 
 |     if (result == ERROR_SUCCESS) { | 
 |       DCHECK_LT(to_wchar_size(value_size_), value_.size()); | 
 |       value_[to_wchar_size(value_size_)] = L'\0'; | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   name_[0] = L'\0'; | 
 |   value_[0] = L'\0'; | 
 |   value_size_ = 0; | 
 |   return false; | 
 | } | 
 |  | 
 | // RegistryKeyIterator -------------------------------------------------------- | 
 |  | 
 | RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, | 
 |                                          const wchar_t* folder_key) { | 
 |   Initialize(root_key, folder_key, 0); | 
 | } | 
 |  | 
 | RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, | 
 |                                          const wchar_t* folder_key, | 
 |                                          REGSAM wow64access) { | 
 |   Initialize(root_key, folder_key, wow64access); | 
 | } | 
 |  | 
 | RegistryKeyIterator::~RegistryKeyIterator() { | 
 |   if (key_) | 
 |     ::RegCloseKey(key_); | 
 | } | 
 |  | 
 | DWORD RegistryKeyIterator::SubkeyCount() const { | 
 |   DWORD count = 0; | 
 |   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL, | 
 |                                   NULL, NULL, NULL, NULL); | 
 |   if (result != ERROR_SUCCESS) | 
 |     return 0; | 
 |  | 
 |   return count; | 
 | } | 
 |  | 
 | bool RegistryKeyIterator::Valid() const { | 
 |   return key_ != NULL && index_ >= 0; | 
 | } | 
 |  | 
 | void RegistryKeyIterator::operator++() { | 
 |   --index_; | 
 |   Read(); | 
 | } | 
 |  | 
 | bool RegistryKeyIterator::Read() { | 
 |   if (Valid()) { | 
 |     DWORD ncount = arraysize(name_); | 
 |     FILETIME written; | 
 |     LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL, NULL, | 
 |                             &written); | 
 |     if (ERROR_SUCCESS == r) | 
 |       return true; | 
 |   } | 
 |  | 
 |   name_[0] = '\0'; | 
 |   return false; | 
 | } | 
 |  | 
 | void RegistryKeyIterator::Initialize(HKEY root_key, | 
 |                                      const wchar_t* folder_key, | 
 |                                      REGSAM wow64access) { | 
 |   DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0)); | 
 |   LONG result = | 
 |       RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_); | 
 |   if (result != ERROR_SUCCESS) { | 
 |     key_ = NULL; | 
 |   } else { | 
 |     DWORD count = 0; | 
 |     result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL, | 
 |                                NULL, NULL, NULL, NULL); | 
 |  | 
 |     if (result != ERROR_SUCCESS) { | 
 |       ::RegCloseKey(key_); | 
 |       key_ = NULL; | 
 |     } else { | 
 |       index_ = count - 1; | 
 |     } | 
 |   } | 
 |  | 
 |   Read(); | 
 | } | 
 |  | 
 | }  // namespace win | 
 | }  // namespace base |