Home | History | Annotate | Download | only in win
      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 
     14 #pragma comment(lib, "shlwapi.lib")  // for SHDeleteKey
     15 
     16 namespace base {
     17 namespace win {
     18 
     19 namespace {
     20 
     21 // RegEnumValue() reports the number of characters from the name that were
     22 // written to the buffer, not how many there are. This constant is the maximum
     23 // name size, such that a buffer with this size should read any name.
     24 const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
     25 
     26 // Registry values are read as BYTE* but can have wchar_t* data whose last
     27 // wchar_t is truncated. This function converts the reported |byte_size| to
     28 // a size in wchar_t that can store a truncated wchar_t if necessary.
     29 inline DWORD to_wchar_size(DWORD byte_size) {
     30   return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
     31 }
     32 
     33 }  // namespace
     34 
     35 // RegKey ----------------------------------------------------------------------
     36 
     37 RegKey::RegKey()
     38     : key_(NULL),
     39       watch_event_(0) {
     40 }
     41 
     42 RegKey::RegKey(HKEY key)
     43     : key_(key),
     44       watch_event_(0) {
     45 }
     46 
     47 RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
     48     : key_(NULL),
     49       watch_event_(0) {
     50   if (rootkey) {
     51     if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
     52       Create(rootkey, subkey, access);
     53     else
     54       Open(rootkey, subkey, access);
     55   } else {
     56     DCHECK(!subkey);
     57   }
     58 }
     59 
     60 RegKey::~RegKey() {
     61   Close();
     62 }
     63 
     64 LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
     65   DWORD disposition_value;
     66   return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
     67 }
     68 
     69 LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
     70                                    DWORD* disposition, REGSAM access) {
     71   DCHECK(rootkey && subkey && access && disposition);
     72   Close();
     73 
     74   LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
     75                                REG_OPTION_NON_VOLATILE, access, NULL, &key_,
     76                                disposition);
     77   return result;
     78 }
     79 
     80 LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
     81   DCHECK(name && access);
     82   HKEY subkey = NULL;
     83   LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
     84                                access, NULL, &subkey, NULL);
     85   Close();
     86 
     87   key_ = subkey;
     88   return result;
     89 }
     90 
     91 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
     92   DCHECK(rootkey && subkey && access);
     93   Close();
     94 
     95   LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_);
     96   return result;
     97 }
     98 
     99 LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
    100   DCHECK(relative_key_name && access);
    101   HKEY subkey = NULL;
    102   LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
    103 
    104   // We have to close the current opened key before replacing it with the new
    105   // one.
    106   Close();
    107 
    108   key_ = subkey;
    109   return result;
    110 }
    111 
    112 void RegKey::Close() {
    113   StopWatching();
    114   if (key_) {
    115     ::RegCloseKey(key_);
    116     key_ = NULL;
    117   }
    118 }
    119 
    120 void RegKey::Set(HKEY key) {
    121   if (key_ != key) {
    122     Close();
    123     key_ = key;
    124   }
    125 }
    126 
    127 HKEY RegKey::Take() {
    128   StopWatching();
    129   HKEY key = key_;
    130   key_ = NULL;
    131   return key;
    132 }
    133 
    134 bool RegKey::HasValue(const wchar_t* name) const {
    135   return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
    136 }
    137 
    138 DWORD RegKey::GetValueCount() const {
    139   DWORD count = 0;
    140   LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
    141                                 NULL, NULL, NULL, NULL);
    142   return (result == ERROR_SUCCESS) ? count : 0;
    143 }
    144 
    145 LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
    146   wchar_t buf[256];
    147   DWORD bufsize = arraysize(buf);
    148   LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
    149   if (r == ERROR_SUCCESS)
    150     *name = buf;
    151 
    152   return r;
    153 }
    154 
    155 LONG RegKey::DeleteKey(const wchar_t* name) {
    156   DCHECK(key_);
    157   DCHECK(name);
    158   LONG result = SHDeleteKey(key_, name);
    159   return result;
    160 }
    161 
    162 LONG RegKey::DeleteValue(const wchar_t* value_name) {
    163   DCHECK(key_);
    164   LONG result = RegDeleteValue(key_, value_name);
    165   return result;
    166 }
    167 
    168 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
    169   DCHECK(out_value);
    170   DWORD type = REG_DWORD;
    171   DWORD size = sizeof(DWORD);
    172   DWORD local_value = 0;
    173   LONG result = ReadValue(name, &local_value, &size, &type);
    174   if (result == ERROR_SUCCESS) {
    175     if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD))
    176       *out_value = local_value;
    177     else
    178       result = ERROR_CANTREAD;
    179   }
    180 
    181   return result;
    182 }
    183 
    184 LONG RegKey::ReadInt64(const wchar_t* name, int64* out_value) const {
    185   DCHECK(out_value);
    186   DWORD type = REG_QWORD;
    187   int64 local_value = 0;
    188   DWORD size = sizeof(local_value);
    189   LONG result = ReadValue(name, &local_value, &size, &type);
    190   if (result == ERROR_SUCCESS) {
    191     if ((type == REG_QWORD || type == REG_BINARY) &&
    192         size == sizeof(local_value))
    193       *out_value = local_value;
    194     else
    195       result = ERROR_CANTREAD;
    196   }
    197 
    198   return result;
    199 }
    200 
    201 LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
    202   DCHECK(out_value);
    203   const size_t kMaxStringLength = 1024;  // This is after expansion.
    204   // Use the one of the other forms of ReadValue if 1024 is too small for you.
    205   wchar_t raw_value[kMaxStringLength];
    206   DWORD type = REG_SZ, size = sizeof(raw_value);
    207   LONG result = ReadValue(name, raw_value, &size, &type);
    208   if (result == ERROR_SUCCESS) {
    209     if (type == REG_SZ) {
    210       *out_value = raw_value;
    211     } else if (type == REG_EXPAND_SZ) {
    212       wchar_t expanded[kMaxStringLength];
    213       size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
    214       // Success: returns the number of wchar_t's copied
    215       // Fail: buffer too small, returns the size required
    216       // Fail: other, returns 0
    217       if (size == 0 || size > kMaxStringLength) {
    218         result = ERROR_MORE_DATA;
    219       } else {
    220         *out_value = expanded;
    221       }
    222     } else {
    223       // Not a string. Oops.
    224       result = ERROR_CANTREAD;
    225     }
    226   }
    227 
    228   return result;
    229 }
    230 
    231 LONG RegKey::ReadValue(const wchar_t* name,
    232                        void* data,
    233                        DWORD* dsize,
    234                        DWORD* dtype) const {
    235   LONG result = RegQueryValueEx(key_, name, 0, dtype,
    236                                 reinterpret_cast<LPBYTE>(data), dsize);
    237   return result;
    238 }
    239 
    240 LONG RegKey::ReadValues(const wchar_t* name,
    241                         std::vector<std::wstring>* values) {
    242   values->clear();
    243 
    244   DWORD type = REG_MULTI_SZ;
    245   DWORD size = 0;
    246   LONG result = ReadValue(name, NULL, &size, &type);
    247   if (FAILED(result) || size == 0)
    248     return result;
    249 
    250   if (type != REG_MULTI_SZ)
    251     return ERROR_CANTREAD;
    252 
    253   std::vector<wchar_t> buffer(size / sizeof(wchar_t));
    254   result = ReadValue(name, &buffer[0], &size, NULL);
    255   if (FAILED(result) || size == 0)
    256     return result;
    257 
    258   // Parse the double-null-terminated list of strings.
    259   // Note: This code is paranoid to not read outside of |buf|, in the case where
    260   // it may not be properly terminated.
    261   const wchar_t* entry = &buffer[0];
    262   const wchar_t* buffer_end = entry + (size / sizeof(wchar_t));
    263   while (entry < buffer_end && entry[0] != '\0') {
    264     const wchar_t* entry_end = std::find(entry, buffer_end, L'\0');
    265     values->push_back(std::wstring(entry, entry_end));
    266     entry = entry_end + 1;
    267   }
    268   return 0;
    269 }
    270 
    271 LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
    272   return WriteValue(
    273       name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD);
    274 }
    275 
    276 LONG RegKey::WriteValue(const wchar_t * name, const wchar_t* in_value) {
    277   return WriteValue(name, in_value,
    278       static_cast<DWORD>(sizeof(*in_value) * (wcslen(in_value) + 1)), REG_SZ);
    279 }
    280 
    281 LONG RegKey::WriteValue(const wchar_t* name,
    282                         const void* data,
    283                         DWORD dsize,
    284                         DWORD dtype) {
    285   DCHECK(data || !dsize);
    286 
    287   LONG result = RegSetValueEx(key_, name, 0, dtype,
    288       reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
    289   return result;
    290 }
    291 
    292 LONG RegKey::StartWatching() {
    293   DCHECK(key_);
    294   if (!watch_event_)
    295     watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
    296 
    297   DWORD filter = REG_NOTIFY_CHANGE_NAME |
    298                  REG_NOTIFY_CHANGE_ATTRIBUTES |
    299                  REG_NOTIFY_CHANGE_LAST_SET |
    300                  REG_NOTIFY_CHANGE_SECURITY;
    301 
    302   // Watch the registry key for a change of value.
    303   LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE);
    304   if (result != ERROR_SUCCESS) {
    305     CloseHandle(watch_event_);
    306     watch_event_ = 0;
    307   }
    308 
    309   return result;
    310 }
    311 
    312 bool RegKey::HasChanged() {
    313   if (watch_event_) {
    314     if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
    315       StartWatching();
    316       return true;
    317     }
    318   }
    319   return false;
    320 }
    321 
    322 LONG RegKey::StopWatching() {
    323   LONG result = ERROR_INVALID_HANDLE;
    324   if (watch_event_) {
    325     CloseHandle(watch_event_);
    326     watch_event_ = 0;
    327     result = ERROR_SUCCESS;
    328   }
    329   return result;
    330 }
    331 
    332 // RegistryValueIterator ------------------------------------------------------
    333 
    334 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
    335                                              const wchar_t* folder_key)
    336     : name_(MAX_PATH, L'\0'),
    337       value_(MAX_PATH, L'\0') {
    338   LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
    339   if (result != ERROR_SUCCESS) {
    340     key_ = NULL;
    341   } else {
    342     DWORD count = 0;
    343     result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
    344                                NULL, NULL, NULL, NULL);
    345 
    346     if (result != ERROR_SUCCESS) {
    347       ::RegCloseKey(key_);
    348       key_ = NULL;
    349     } else {
    350       index_ = count - 1;
    351     }
    352   }
    353 
    354   Read();
    355 }
    356 
    357 RegistryValueIterator::~RegistryValueIterator() {
    358   if (key_)
    359     ::RegCloseKey(key_);
    360 }
    361 
    362 DWORD RegistryValueIterator::ValueCount() const {
    363   DWORD count = 0;
    364   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
    365                                   &count, NULL, NULL, NULL, NULL);
    366   if (result != ERROR_SUCCESS)
    367     return 0;
    368 
    369   return count;
    370 }
    371 
    372 bool RegistryValueIterator::Valid() const {
    373   return key_ != NULL && index_ >= 0;
    374 }
    375 
    376 void RegistryValueIterator::operator++() {
    377   --index_;
    378   Read();
    379 }
    380 
    381 bool RegistryValueIterator::Read() {
    382   if (Valid()) {
    383     DWORD capacity = static_cast<DWORD>(name_.capacity());
    384     DWORD name_size = capacity;
    385     // |value_size_| is in bytes. Reserve the last character for a NUL.
    386     value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
    387     LONG result = ::RegEnumValue(
    388         key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
    389         reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
    390 
    391     if (result == ERROR_MORE_DATA) {
    392       // Registry key names are limited to 255 characters and fit within
    393       // MAX_PATH (which is 260) but registry value names can use up to 16,383
    394       // characters and the value itself is not limited
    395       // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
    396       // ms724872(v=vs.85).aspx).
    397       // Resize the buffers and retry if their size caused the failure.
    398       DWORD value_size_in_wchars = to_wchar_size(value_size_);
    399       if (value_size_in_wchars + 1 > value_.size())
    400         value_.resize(value_size_in_wchars + 1, L'\0');
    401       value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
    402       name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
    403       result = ::RegEnumValue(
    404           key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
    405           reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
    406     }
    407 
    408     if (result == ERROR_SUCCESS) {
    409       DCHECK_LT(to_wchar_size(value_size_), value_.size());
    410       value_[to_wchar_size(value_size_)] = L'\0';
    411       return true;
    412     }
    413   }
    414 
    415   name_[0] = L'\0';
    416   value_[0] = L'\0';
    417   value_size_ = 0;
    418   return false;
    419 }
    420 
    421 // RegistryKeyIterator --------------------------------------------------------
    422 
    423 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
    424                                          const wchar_t* folder_key) {
    425   LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
    426   if (result != ERROR_SUCCESS) {
    427     key_ = NULL;
    428   } else {
    429     DWORD count = 0;
    430     LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
    431                                     NULL, NULL, NULL, NULL, NULL);
    432 
    433     if (result != ERROR_SUCCESS) {
    434       ::RegCloseKey(key_);
    435       key_ = NULL;
    436     } else {
    437       index_ = count - 1;
    438     }
    439   }
    440 
    441   Read();
    442 }
    443 
    444 RegistryKeyIterator::~RegistryKeyIterator() {
    445   if (key_)
    446     ::RegCloseKey(key_);
    447 }
    448 
    449 DWORD RegistryKeyIterator::SubkeyCount() const {
    450   DWORD count = 0;
    451   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
    452                                   NULL, NULL, NULL, NULL, NULL);
    453   if (result != ERROR_SUCCESS)
    454     return 0;
    455 
    456   return count;
    457 }
    458 
    459 bool RegistryKeyIterator::Valid() const {
    460   return key_ != NULL && index_ >= 0;
    461 }
    462 
    463 void RegistryKeyIterator::operator++() {
    464   --index_;
    465   Read();
    466 }
    467 
    468 bool RegistryKeyIterator::Read() {
    469   if (Valid()) {
    470     DWORD ncount = arraysize(name_);
    471     FILETIME written;
    472     LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
    473                             NULL, &written);
    474     if (ERROR_SUCCESS == r)
    475       return true;
    476   }
    477 
    478   name_[0] = '\0';
    479   return false;
    480 }
    481 
    482 }  // namespace win
    483 }  // namespace base
    484