winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }