Registry.cpp (17664B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "Public/winget/Registry.h" 5 #include "Public/AppInstallerStrings.h" 6 #include "Public/AppInstallerLogging.h" 7 8 9 namespace AppInstaller::Registry 10 { 11 namespace 12 { 13 std::wstring_view ConvertBytesToWideStringView(const std::vector<BYTE>& data) 14 { 15 // Remove any extra bytes because the data could just be dirty; better to not have a bad character than outright fail. 16 std::wstring_view result{ reinterpret_cast<const wchar_t*>(data.data()), data.size() / sizeof(wchar_t) }; 17 18 // Registry values may or may not be null terminated; we will remove any trailing nulls 19 while (!result.empty() && result.back() == L'\0') 20 { 21 result = result.substr(0, result.size() - 1); 22 } 23 24 return result; 25 } 26 27 std::wstring ConvertBytesToWideString(const std::vector<BYTE>& data) 28 { 29 return std::wstring{ ConvertBytesToWideStringView(data) }; 30 } 31 32 std::string ConvertBytesToString(const std::vector<BYTE>& data) 33 { 34 return Utility::ConvertToUTF8(ConvertBytesToWideStringView(data)); 35 } 36 37 uint32_t ConvertBytesToUInt32LE(const std::vector<BYTE>& data) 38 { 39 THROW_HR_IF(E_NOT_VALID_STATE, data.size() != sizeof(uint32_t)); 40 uint32_t result = 0; 41 uint32_t shift = 0; 42 43 for (const BYTE datum : data) 44 { 45 result |= ((static_cast<uint32_t>(datum) & 0xFF) << shift); 46 shift += 8; 47 } 48 49 return result; 50 } 51 52 bool TryGetRegistryValueNameFromIndex(const wil::shared_hkey& key, DWORD index, std::wstring& valueName) 53 { 54 constexpr DWORD MaxNameLength = 32767; 55 LSTATUS status = ERROR_SUCCESS; 56 DWORD charCount = 0; 57 valueName = L'\0'; 58 59 while (valueName.size() <= MaxNameLength) 60 { 61 charCount = wil::safe_cast<DWORD>(valueName.size()); 62 63 // We could also get the type and data here, but we read only the name instead 64 // to prevent duplication with the code that gets the data from the name. 65 status = RegEnumValueW(key.get(), index, &valueName[0], &charCount, nullptr, nullptr, nullptr, nullptr); 66 67 if (status == ERROR_MORE_DATA) 68 { 69 // See if we can get away with the current capacity 70 if (valueName.size() < valueName.capacity()) 71 { 72 valueName.resize(valueName.capacity()); 73 } 74 else 75 { 76 valueName.resize(valueName.capacity() * 2); 77 } 78 } 79 else 80 { 81 break; 82 } 83 } 84 85 if (status == ERROR_SUCCESS) 86 { 87 valueName.resize(wil::safe_cast<size_t>(charCount)); 88 return true; 89 } 90 else if (status == ERROR_NO_MORE_ITEMS) 91 { 92 return false; 93 } 94 else 95 { 96 THROW_IF_WIN32_ERROR(status); 97 return false; 98 } 99 } 100 101 bool TryGetRegistryValueData(const wil::shared_hkey& key, const std::wstring& valueName, DWORD& type, std::vector<BYTE>& data) 102 { 103 data.resize(64); 104 105 LSTATUS status = ERROR_SUCCESS; 106 DWORD byteCount = 0; 107 108 while (data.size() < (64 << 20)) 109 { 110 byteCount = wil::safe_cast<DWORD>(data.size()); 111 status = RegGetValueW(key.get(), nullptr, valueName.c_str(), RRF_RT_ANY | RRF_NOEXPAND, &type, data.data(), &byteCount); 112 113 if (status == ERROR_MORE_DATA && byteCount > data.size()) 114 { 115 data.resize(byteCount); 116 } 117 else 118 { 119 break; 120 } 121 } 122 123 if (status == ERROR_FILE_NOT_FOUND) 124 { 125 return false; 126 } 127 128 THROW_IF_WIN32_ERROR(status); 129 130 // Resize to actual data size 131 data.resize(byteCount); 132 133 return true; 134 } 135 } 136 137 namespace details 138 { 139 ValueTypeSpecifics<REG_NONE>::value_t ValueTypeSpecifics<REG_NONE>::Convert(const std::vector<BYTE>& data) 140 { 141 return data; 142 } 143 144 ValueTypeSpecifics<REG_SZ>::value_t ValueTypeSpecifics<REG_SZ>::Convert(const std::vector<BYTE>& data) 145 { 146 return ConvertBytesToString(data); 147 } 148 149 ValueTypeSpecifics<REG_SZ | AICLI_REGISTRY_UTF16_FLAG>::value_t ValueTypeSpecifics<REG_SZ | AICLI_REGISTRY_UTF16_FLAG>::Convert(const std::vector<BYTE>& data) 150 { 151 return ConvertBytesToWideString(data); 152 } 153 154 ValueTypeSpecifics<REG_EXPAND_SZ>::value_t ValueTypeSpecifics<REG_EXPAND_SZ>::Convert(const std::vector<BYTE>& data) 155 { 156 return Utility::ConvertToUTF8(Utility::ExpandEnvironmentVariables(ConvertBytesToWideString(data))); 157 } 158 159 ValueTypeSpecifics<REG_EXPAND_SZ | AICLI_REGISTRY_UTF16_FLAG>::value_t ValueTypeSpecifics<REG_EXPAND_SZ | AICLI_REGISTRY_UTF16_FLAG>::Convert(const std::vector<BYTE>& data) 160 { 161 return ConvertBytesToWideString(data); 162 } 163 164 ValueTypeSpecifics<REG_BINARY>::value_t ValueTypeSpecifics<REG_BINARY>::Convert(const std::vector<BYTE>& data) 165 { 166 return data; 167 } 168 169 ValueTypeSpecifics<REG_DWORD_LITTLE_ENDIAN>::value_t ValueTypeSpecifics<REG_DWORD_LITTLE_ENDIAN>::Convert(const std::vector<BYTE>& data) 170 { 171 return ConvertBytesToUInt32LE(data); 172 } 173 } 174 175 Value::Value(DWORD type, std::vector<BYTE>&& data) : m_type(static_cast<Type>(type)), m_data(std::move(data)) 176 { 177 } 178 179 bool Value::HasCompatibleType(Type type) const 180 { 181 // Allow interop between String and ExpandString 182 if ((m_type == Type::String || m_type == Type::ExpandString || m_type == Type::UTF16String || m_type == Type::UTF16ExpandString) && 183 (type == Type::String || type == Type::ExpandString || type == Type::UTF16String || type == Type::UTF16ExpandString)) 184 { 185 return true; 186 } 187 188 return m_type == type; 189 } 190 191 ValueList::ValueRef::ValueRef(wil::shared_hkey key, std::wstring&& valueName) : m_key(std::move(key)), m_valueName(std::move(valueName)) {} 192 193 std::string ValueList::ValueRef::Name() const 194 { 195 return Utility::ConvertToUTF8(m_valueName); 196 } 197 198 std::optional<Value> ValueList::ValueRef::Value() const 199 { 200 DWORD type; 201 std::vector<BYTE> data; 202 if (!TryGetRegistryValueData(m_key, m_valueName, type, data)) 203 { 204 return std::nullopt; 205 } 206 207 return Registry::Value{ type, std::move(data) }; 208 } 209 210 ValueList::const_iterator& ValueList::const_iterator::operator++() 211 { 212 ++m_index; 213 GetValue(); 214 return *this; 215 } 216 217 ValueList::const_iterator ValueList::const_iterator::operator++(int) 218 { 219 const_iterator result; 220 result.m_key = m_key; 221 result.m_index = m_index++; 222 result.m_value = std::nullopt; 223 std::swap(m_value, result.m_value); 224 GetValue(); 225 return result; 226 } 227 228 bool ValueList::const_iterator::operator==(const const_iterator& other) const 229 { 230 return (!m_key && !other.m_key) || (m_key.get() == other.m_key.get() && m_index == other.m_index); 231 } 232 233 bool ValueList::const_iterator::operator!=(const const_iterator& other) const 234 { 235 return !operator==(other); 236 } 237 238 void ValueList::const_iterator::GetValue() 239 { 240 std::wstring valueName; 241 if (!TryGetRegistryValueNameFromIndex(m_key, m_index, valueName)) 242 { 243 m_key.reset(); 244 return; 245 } 246 247 m_value = ValueRef{ m_key, std::move(valueName) }; 248 } 249 250 const ValueList::ValueRef& ValueList::const_iterator::operator*() const 251 { 252 return m_value.value(); 253 } 254 255 const ValueList::ValueRef* ValueList::const_iterator::operator->() const 256 { 257 return &m_value.value(); 258 } 259 260 ValueList::const_iterator::const_iterator(const wil::shared_hkey& key, DWORD index) : m_key(key), m_index(index) 261 { 262 GetValue(); 263 } 264 265 ValueList::const_iterator ValueList::begin() const 266 { 267 return { m_key }; 268 } 269 270 ValueList::const_iterator ValueList::end() const 271 { 272 return {}; 273 } 274 275 ValueList::ValueList(wil::shared_hkey key) : m_key(key) {} 276 277 Key::Key(HKEY key) 278 { 279 Initialize(key, {}, 0, KEY_READ, false); 280 } 281 282 Key::Key(HKEY key, std::string_view subKey, DWORD options, REGSAM access) 283 { 284 Initialize(key, Utility::ConvertToUTF16(subKey), options, access, false); 285 } 286 287 Key::Key(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access) 288 { 289 Initialize(key, subKey, options, access, false); 290 } 291 292 std::string Key::SubKeyRef::Name() const 293 { 294 return Utility::ConvertToUTF8(m_subKeyName); 295 } 296 297 Key Key::SubKeyRef::Open() const 298 { 299 return { m_parentKey.get(), m_subKeyName, 0, m_access }; 300 } 301 302 Key::SubKeyRef::SubKeyRef(const wil::shared_hkey& key, REGSAM access) : 303 m_parentKey(key), m_access(access), m_subKeyName(64, L'\0') 304 { 305 Enum(0); 306 } 307 308 void Key::SubKeyRef::Enum(DWORD index) 309 { 310 LSTATUS status = ERROR_SUCCESS; 311 DWORD charCount = 0; 312 313 while (m_subKeyName.size() < 4096) 314 { 315 charCount = wil::safe_cast<DWORD>(m_subKeyName.size()); 316 status = RegEnumKeyExW(m_parentKey.get(), index, &m_subKeyName[0], &charCount, nullptr, nullptr, nullptr, nullptr); 317 318 if (status == ERROR_MORE_DATA) 319 { 320 // See if we can get away with the current capacity 321 if (m_subKeyName.size() < m_subKeyName.capacity()) 322 { 323 m_subKeyName.resize(m_subKeyName.capacity()); 324 } 325 else 326 { 327 m_subKeyName.resize(m_subKeyName.capacity() * 2); 328 } 329 } 330 else 331 { 332 break; 333 } 334 } 335 336 if (status == ERROR_SUCCESS) 337 { 338 m_subKeyName.resize(wil::safe_cast<size_t>(charCount)); 339 } 340 else if (status == ERROR_NO_MORE_ITEMS) 341 { 342 m_parentKey.reset(); 343 } 344 else 345 { 346 THROW_IF_WIN32_ERROR(status); 347 } 348 } 349 350 Key::const_iterator& Key::const_iterator::operator++() 351 { 352 m_subkey.Enum(++m_index); 353 return *this; 354 } 355 356 Key::const_iterator Key::const_iterator::operator++(int) 357 { 358 const_iterator result = *this; 359 m_subkey.Enum(++m_index); 360 return result; 361 } 362 363 bool Key::const_iterator::operator==(const const_iterator& other) const 364 { 365 return (!m_subkey.m_parentKey && !other.m_subkey.m_parentKey) || (m_subkey.m_parentKey.get() == other.m_subkey.m_parentKey.get() && m_index == other.m_index); 366 } 367 368 bool Key::const_iterator::operator!=(const const_iterator& other) const 369 { 370 return !operator==(other); 371 } 372 373 const Key::SubKeyRef& Key::const_iterator::operator*() const 374 { 375 return m_subkey; 376 } 377 378 const Key::SubKeyRef* Key::const_iterator::operator->() const 379 { 380 return &m_subkey; 381 } 382 383 Key::const_iterator::const_iterator(const wil::shared_hkey& key, REGSAM access) : 384 m_subkey(key, access) 385 { 386 } 387 388 Key::const_iterator Key::begin() const 389 { 390 return { m_key, m_access }; 391 } 392 393 Key::const_iterator Key::end() const 394 { 395 return {}; 396 } 397 398 std::optional<Value> Key::operator[](std::string_view name) const 399 { 400 return operator[](Utility::ConvertToUTF16(name)); 401 } 402 403 std::optional<Value> Key::operator[](const std::wstring& name) const 404 { 405 DWORD type; 406 std::vector<BYTE> data; 407 408 if (TryGetRegistryValueData(m_key, name, type, data)) 409 { 410 return Value{ type, std::move(data) }; 411 } 412 else 413 { 414 return {}; 415 } 416 } 417 418 std::optional<Key> Key::SubKey(std::string_view subKey, DWORD options) const 419 { 420 return SubKey(Utility::ConvertToUTF16(subKey), options); 421 } 422 423 std::optional<Key> Key::SubKey(const std::wstring& subKey, DWORD options) const 424 { 425 if (!m_key) 426 { 427 return std::nullopt; 428 } 429 430 Key result; 431 if (result.Initialize(m_key.get(), subKey, options, m_access, true)) 432 { 433 return result; 434 } 435 else 436 { 437 return std::nullopt; 438 } 439 } 440 441 void Key::SetValue(const std::wstring& name, const std::wstring& value, DWORD type) const 442 { 443 THROW_IF_WIN32_ERROR(RegSetValueExW(m_key.get(), name.c_str(), 0, type, reinterpret_cast<const BYTE*>(value.c_str()), static_cast<DWORD>(sizeof(wchar_t) * (value.size() + 1)))); 444 AICLI_LOG(Core, Verbose, << "Setting '" << Utility::ConvertToUTF8(name) << "' with the value: " << Utility::ConvertToUTF8(value)); 445 } 446 447 void Key::SetValue(const std::wstring& name, const std::vector<BYTE>& value, DWORD type) const 448 { 449 THROW_IF_WIN32_ERROR(RegSetValueExW(m_key.get(), name.c_str(), 0, type, reinterpret_cast<const BYTE*>(value.data()), static_cast<DWORD>(value.size()))); 450 AICLI_LOG(Core, Verbose, << "Setting '" << Utility::ConvertToUTF8(name) << "' with the value: " << ConvertBytesToString(value)); 451 452 } 453 454 void Key::SetValue(const std::wstring& name, DWORD value) const 455 { 456 THROW_IF_WIN32_ERROR(RegSetValueExW(m_key.get(), name.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(DWORD))); 457 AICLI_LOG(Core, Verbose, << "Setting '" << Utility::ConvertToUTF8(name) << "' with the value: " << value); 458 459 } 460 461 ValueList Key::Values() const 462 { 463 return { m_key }; 464 } 465 466 Key Key::OpenIfExists(HKEY key, std::string_view subKey, DWORD options, REGSAM access) 467 { 468 return OpenIfExists(key, Utility::ConvertToUTF16(subKey), options, access); 469 } 470 471 Key Key::OpenIfExists(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access) 472 { 473 Key result; 474 result.Initialize(key, subKey, options, access, true); 475 return result; 476 } 477 478 Key Key::Create(HKEY key, std::string_view subKey, DWORD options, REGSAM access) 479 { 480 return Create(key, Utility::ConvertToUTF16(subKey), options, access); 481 } 482 483 Key Key::Create(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access) 484 { 485 Key result; 486 result.CreateAndOpen(key, subKey, options, access); 487 return result; 488 } 489 490 bool Key::Delete(HKEY key, std::string_view subKey, DWORD samDesired) 491 { 492 return Delete(key, Utility::ConvertToUTF16(subKey), samDesired); 493 } 494 495 bool Key::Delete(HKEY key, const std::wstring& subKey, DWORD samDesired) 496 { 497 LSTATUS status = RegDeleteKeyExW(key, subKey.c_str(), samDesired, 0); 498 if (status == ERROR_SUCCESS) 499 { 500 AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was deleted successfully."); 501 return true; 502 } 503 else if (status == ERROR_FILE_NOT_FOUND) 504 { 505 AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was not found."); 506 } 507 else 508 { 509 THROW_IF_WIN32_ERROR(status); 510 } 511 512 return false; 513 } 514 515 bool Key::CreateAndOpen(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access) 516 { 517 m_access = access; 518 LPDWORD disposition = {}; 519 LSTATUS status = RegCreateKeyExW(key, subKey.c_str(), 0, nullptr, options, access, NULL, &m_key, disposition); 520 521 if (disposition == (LPDWORD)REG_CREATED_NEW_KEY) 522 { 523 AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was created."); 524 } 525 else if (disposition == (LPDWORD)REG_OPENED_EXISTING_KEY) 526 { 527 AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' already existed and was opened."); 528 } 529 530 THROW_IF_WIN32_ERROR(status); 531 return true; 532 } 533 534 bool Key::Initialize(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access, bool ignoreErrorIfDoesNotExist) 535 { 536 m_access = access; 537 LSTATUS status = RegOpenKeyExW(key, subKey.c_str(), options, access, &m_key); 538 539 if (ignoreErrorIfDoesNotExist && status == ERROR_FILE_NOT_FOUND) 540 { 541 AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was not found"); 542 return false; 543 } 544 545 THROW_IF_WIN32_ERROR(status); 546 return true; 547 } 548 }