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