1// Copyright (c) 2011 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 9#include "base/logging.h" 10#include "base/threading/thread_restrictions.h" 11 12#pragma comment(lib, "shlwapi.lib") // for SHDeleteKey 13 14namespace base { 15namespace win { 16 17// RegKey ---------------------------------------------------------------------- 18 19RegKey::RegKey() 20 : key_(NULL), 21 watch_event_(0) { 22} 23 24RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) 25 : key_(NULL), 26 watch_event_(0) { 27 base::ThreadRestrictions::AssertIOAllowed(); 28 if (rootkey) { 29 if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) 30 Create(rootkey, subkey, access); 31 else 32 Open(rootkey, subkey, access); 33 } else { 34 DCHECK(!subkey); 35 } 36} 37 38RegKey::~RegKey() { 39 Close(); 40} 41 42LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { 43 DWORD disposition_value; 44 return CreateWithDisposition(rootkey, subkey, &disposition_value, access); 45} 46 47LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, 48 DWORD* disposition, REGSAM access) { 49 base::ThreadRestrictions::AssertIOAllowed(); 50 DCHECK(rootkey && subkey && access && disposition); 51 Close(); 52 53 LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL, 54 REG_OPTION_NON_VOLATILE, access, NULL, &key_, 55 disposition); 56 return result; 57} 58 59LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { 60 base::ThreadRestrictions::AssertIOAllowed(); 61 DCHECK(rootkey && subkey && access); 62 Close(); 63 64 LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_); 65 return result; 66} 67 68LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) { 69 base::ThreadRestrictions::AssertIOAllowed(); 70 DCHECK(name && access); 71 72 HKEY subkey = NULL; 73 LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE, 74 access, NULL, &subkey, NULL); 75 Close(); 76 77 key_ = subkey; 78 return result; 79} 80 81LONG RegKey::OpenKey(const wchar_t* name, REGSAM access) { 82 base::ThreadRestrictions::AssertIOAllowed(); 83 DCHECK(name && access); 84 85 HKEY subkey = NULL; 86 LONG result = RegOpenKeyEx(key_, name, 0, access, &subkey); 87 88 Close(); 89 90 key_ = subkey; 91 return result; 92} 93 94void RegKey::Close() { 95 base::ThreadRestrictions::AssertIOAllowed(); 96 StopWatching(); 97 if (key_) { 98 ::RegCloseKey(key_); 99 key_ = NULL; 100 } 101} 102 103DWORD RegKey::ValueCount() const { 104 base::ThreadRestrictions::AssertIOAllowed(); 105 DWORD count = 0; 106 LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, 107 NULL, NULL, NULL, NULL); 108 return (result != ERROR_SUCCESS) ? 0 : count; 109} 110 111LONG RegKey::ReadName(int index, std::wstring* name) const { 112 base::ThreadRestrictions::AssertIOAllowed(); 113 wchar_t buf[256]; 114 DWORD bufsize = arraysize(buf); 115 LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL); 116 if (r == ERROR_SUCCESS) 117 *name = buf; 118 119 return r; 120} 121 122LONG RegKey::DeleteKey(const wchar_t* name) { 123 base::ThreadRestrictions::AssertIOAllowed(); 124 DCHECK(key_); 125 DCHECK(name); 126 LONG result = SHDeleteKey(key_, name); 127 return result; 128} 129 130LONG RegKey::DeleteValue(const wchar_t* value_name) { 131 base::ThreadRestrictions::AssertIOAllowed(); 132 DCHECK(key_); 133 DCHECK(value_name); 134 LONG result = RegDeleteValue(key_, value_name); 135 return result; 136} 137 138bool RegKey::ValueExists(const wchar_t* name) const { 139 base::ThreadRestrictions::AssertIOAllowed(); 140 LONG result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL); 141 return result == ERROR_SUCCESS; 142} 143 144LONG RegKey::ReadValue(const wchar_t* name, void* data, DWORD* dsize, 145 DWORD* dtype) const { 146 base::ThreadRestrictions::AssertIOAllowed(); 147 LONG result = RegQueryValueEx(key_, name, 0, dtype, 148 reinterpret_cast<LPBYTE>(data), dsize); 149 return result; 150} 151 152LONG RegKey::ReadValue(const wchar_t* name, std::wstring* value) const { 153 base::ThreadRestrictions::AssertIOAllowed(); 154 DCHECK(value); 155 const size_t kMaxStringLength = 1024; // This is after expansion. 156 // Use the one of the other forms of ReadValue if 1024 is too small for you. 157 wchar_t raw_value[kMaxStringLength]; 158 DWORD type = REG_SZ, size = sizeof(raw_value); 159 LONG result = ReadValue(name, raw_value, &size, &type); 160 if (result == ERROR_SUCCESS) { 161 if (type == REG_SZ) { 162 *value = raw_value; 163 } else if (type == REG_EXPAND_SZ) { 164 wchar_t expanded[kMaxStringLength]; 165 size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength); 166 // Success: returns the number of wchar_t's copied 167 // Fail: buffer too small, returns the size required 168 // Fail: other, returns 0 169 if (size == 0 || size > kMaxStringLength) { 170 result = ERROR_MORE_DATA; 171 } else { 172 *value = expanded; 173 } 174 } else { 175 // Not a string. Oops. 176 result = ERROR_CANTREAD; 177 } 178 } 179 180 return result; 181} 182 183LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* value) const { 184 DCHECK(value); 185 DWORD type = REG_DWORD; 186 DWORD size = sizeof(DWORD); 187 DWORD local_value = 0; 188 LONG result = ReadValue(name, &local_value, &size, &type); 189 if (result == ERROR_SUCCESS) { 190 if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD)) { 191 *value = local_value; 192 } else { 193 result = ERROR_CANTREAD; 194 } 195 } 196 197 return result; 198} 199 200LONG RegKey::ReadInt64(const wchar_t* name, int64* value) const { 201 DCHECK(value); 202 DWORD type = REG_QWORD; 203 int64 local_value = 0; 204 DWORD size = sizeof(local_value); 205 LONG result = ReadValue(name, &local_value, &size, &type); 206 if (result == ERROR_SUCCESS) { 207 if ((type == REG_QWORD || type == REG_BINARY) && 208 size == sizeof(local_value)) { 209 *value = local_value; 210 } else { 211 result = ERROR_CANTREAD; 212 } 213 } 214 215 return result; 216} 217 218LONG RegKey::WriteValue(const wchar_t* name, const void * data, 219 DWORD dsize, DWORD dtype) { 220 base::ThreadRestrictions::AssertIOAllowed(); 221 DCHECK(data || !dsize); 222 223 LONG result = RegSetValueEx(key_, name, 0, dtype, 224 reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize); 225 return result; 226} 227 228LONG RegKey::WriteValue(const wchar_t * name, const wchar_t* value) { 229 return WriteValue(name, value, 230 static_cast<DWORD>(sizeof(*value) * (wcslen(value) + 1)), REG_SZ); 231} 232 233LONG RegKey::WriteValue(const wchar_t* name, DWORD value) { 234 return WriteValue(name, &value, static_cast<DWORD>(sizeof(value)), REG_DWORD); 235} 236 237LONG RegKey::StartWatching() { 238 DCHECK(key_); 239 if (!watch_event_) 240 watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); 241 242 DWORD filter = REG_NOTIFY_CHANGE_NAME | 243 REG_NOTIFY_CHANGE_ATTRIBUTES | 244 REG_NOTIFY_CHANGE_LAST_SET | 245 REG_NOTIFY_CHANGE_SECURITY; 246 247 // Watch the registry key for a change of value. 248 LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE); 249 if (result != ERROR_SUCCESS) { 250 CloseHandle(watch_event_); 251 watch_event_ = 0; 252 } 253 254 return result; 255} 256 257bool RegKey::HasChanged() { 258 if (watch_event_) { 259 if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) { 260 StartWatching(); 261 return true; 262 } 263 } 264 return false; 265} 266 267LONG RegKey::StopWatching() { 268 LONG result = ERROR_INVALID_HANDLE; 269 if (watch_event_) { 270 CloseHandle(watch_event_); 271 watch_event_ = 0; 272 result = ERROR_SUCCESS; 273 } 274 return result; 275} 276 277// RegistryValueIterator ------------------------------------------------------ 278 279RegistryValueIterator::RegistryValueIterator(HKEY root_key, 280 const wchar_t* folder_key) { 281 base::ThreadRestrictions::AssertIOAllowed(); 282 283 LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); 284 if (result != ERROR_SUCCESS) { 285 key_ = NULL; 286 } else { 287 DWORD count = 0; 288 result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, 289 NULL, NULL, NULL, NULL); 290 291 if (result != ERROR_SUCCESS) { 292 ::RegCloseKey(key_); 293 key_ = NULL; 294 } else { 295 index_ = count - 1; 296 } 297 } 298 299 Read(); 300} 301 302RegistryValueIterator::~RegistryValueIterator() { 303 base::ThreadRestrictions::AssertIOAllowed(); 304 if (key_) 305 ::RegCloseKey(key_); 306} 307 308DWORD RegistryValueIterator::ValueCount() const { 309 base::ThreadRestrictions::AssertIOAllowed(); 310 DWORD count = 0; 311 LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, 312 &count, NULL, NULL, NULL, NULL); 313 if (result != ERROR_SUCCESS) 314 return 0; 315 316 return count; 317} 318 319bool RegistryValueIterator::Valid() const { 320 return key_ != NULL && index_ >= 0; 321} 322 323void RegistryValueIterator::operator++() { 324 --index_; 325 Read(); 326} 327 328bool RegistryValueIterator::Read() { 329 base::ThreadRestrictions::AssertIOAllowed(); 330 if (Valid()) { 331 DWORD ncount = arraysize(name_); 332 value_size_ = sizeof(value_); 333 LONG r = ::RegEnumValue(key_, index_, name_, &ncount, NULL, &type_, 334 reinterpret_cast<BYTE*>(value_), &value_size_); 335 if (ERROR_SUCCESS == r) 336 return true; 337 } 338 339 name_[0] = '\0'; 340 value_[0] = '\0'; 341 value_size_ = 0; 342 return false; 343} 344 345// RegistryKeyIterator -------------------------------------------------------- 346 347RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, 348 const wchar_t* folder_key) { 349 base::ThreadRestrictions::AssertIOAllowed(); 350 LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); 351 if (result != ERROR_SUCCESS) { 352 key_ = NULL; 353 } else { 354 DWORD count = 0; 355 LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, 356 NULL, NULL, NULL, NULL, NULL); 357 358 if (result != ERROR_SUCCESS) { 359 ::RegCloseKey(key_); 360 key_ = NULL; 361 } else { 362 index_ = count - 1; 363 } 364 } 365 366 Read(); 367} 368 369RegistryKeyIterator::~RegistryKeyIterator() { 370 base::ThreadRestrictions::AssertIOAllowed(); 371 if (key_) 372 ::RegCloseKey(key_); 373} 374 375DWORD RegistryKeyIterator::SubkeyCount() const { 376 base::ThreadRestrictions::AssertIOAllowed(); 377 DWORD count = 0; 378 LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, 379 NULL, NULL, NULL, NULL, NULL); 380 if (result != ERROR_SUCCESS) 381 return 0; 382 383 return count; 384} 385 386bool RegistryKeyIterator::Valid() const { 387 return key_ != NULL && index_ >= 0; 388} 389 390void RegistryKeyIterator::operator++() { 391 --index_; 392 Read(); 393} 394 395bool RegistryKeyIterator::Read() { 396 base::ThreadRestrictions::AssertIOAllowed(); 397 if (Valid()) { 398 DWORD ncount = arraysize(name_); 399 FILETIME written; 400 LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL, 401 NULL, &written); 402 if (ERROR_SUCCESS == r) 403 return true; 404 } 405 406 name_[0] = '\0'; 407 return false; 408} 409 410} // namespace win 411} // namespace base 412