PathVariable.cpp (4643B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "winget/PathVariable.h" 5 #include <winget/Filesystem.h> 6 7 using namespace AppInstaller::Utility; 8 9 namespace AppInstaller::Registry::Environment 10 { 11 namespace 12 { 13 constexpr std::wstring_view s_PathName = L"Path"; 14 constexpr std::wstring_view s_PathSubkey_User = L"Environment"; 15 constexpr std::wstring_view s_PathSubkey_Machine = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; 16 17 void EnsurePathValueEndsWithSemicolon(std::string& value) 18 { 19 if (value.back() != ';') 20 { 21 value += ';'; 22 } 23 } 24 25 std::string ExpandPathValue(const std::string& value) 26 { 27 std::string result; 28 std::vector<std::string> pathEntries = Split(value, ';'); 29 for (std::string& pathEntry : pathEntries) 30 { 31 if (!pathEntry.empty()) 32 { 33 result += AppInstaller::Filesystem::GetExpandedPath(pathEntry).u8string(); 34 result += ';'; 35 } 36 } 37 return result; 38 } 39 } 40 41 PathVariable::PathVariable(Manifest::ScopeEnum scope, bool readOnly) : m_scope(scope), m_readOnly(readOnly) 42 { 43 if (m_readOnly) 44 { 45 if (m_scope == Manifest::ScopeEnum::Machine) 46 { 47 m_key = Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, std::wstring{ s_PathSubkey_Machine }); 48 } 49 else 50 { 51 m_key = Registry::Key::OpenIfExists(HKEY_CURRENT_USER, std::wstring{ s_PathSubkey_User }); 52 } 53 } 54 else 55 { 56 if (m_scope == Manifest::ScopeEnum::Machine) 57 { 58 m_key = Registry::Key::Create(HKEY_LOCAL_MACHINE, std::wstring{ s_PathSubkey_Machine }); 59 } 60 else 61 { 62 m_key = Registry::Key::Create(HKEY_CURRENT_USER, std::wstring{ s_PathSubkey_User }); 63 } 64 } 65 } 66 67 std::string PathVariable::GetPathValue() 68 { 69 std::wstring pathName = std::wstring{ s_PathName }; 70 return Normalize(m_key[pathName]->GetValue<Value::Type::String>()); 71 } 72 73 bool PathVariable::Contains(const std::filesystem::path& target) 74 { 75 std::string targetString = Normalize(target.u8string()); 76 return (GetPathValue().find(targetString) != std::string::npos); 77 } 78 79 bool PathVariable::Remove(const std::filesystem::path& target) 80 { 81 THROW_HR_IF(E_ACCESSDENIED, m_readOnly); 82 83 if (Contains(target)) 84 { 85 std::string targetString = Normalize(target.u8string()); 86 std::string pathValue = GetPathValue(); 87 FindAndReplace(pathValue, targetString, ""); 88 FindAndReplace(pathValue, ";;", ";"); 89 SetPathValue(pathValue); 90 return true; 91 } 92 else 93 { 94 return false; 95 } 96 } 97 98 bool PathVariable::Append(const std::filesystem::path& target) 99 { 100 THROW_HR_IF(E_ACCESSDENIED, m_readOnly); 101 102 if (!Contains(target)) 103 { 104 std::string targetString = Normalize(target.u8string()); 105 std::string pathValue = GetPathValue(); 106 EnsurePathValueEndsWithSemicolon(pathValue); 107 pathValue += targetString; 108 EnsurePathValueEndsWithSemicolon(pathValue); 109 SetPathValue(pathValue); 110 return true; 111 } 112 else 113 { 114 return false; 115 } 116 } 117 118 void PathVariable::SetPathValue(const std::string& value) 119 { 120 THROW_HR_IF(E_ACCESSDENIED, m_readOnly); 121 122 std::wstring pathName = std::wstring{ s_PathName }; 123 m_key.SetValue(pathName, ConvertToUTF16(value), REG_EXPAND_SZ); 124 SendNotifyMessageW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Environment")); 125 126 } 127 128 bool RefreshPathVariableForCurrentProcess() 129 { 130 // Path values must be expanded before assigning to process environment for proper refresh. 131 std::string systemPathValue = ExpandPathValue(PathVariable(Manifest::ScopeEnum::Machine, true).GetPathValue()); 132 std::string userPathValue = ExpandPathValue(PathVariable(Manifest::ScopeEnum::User, true).GetPathValue()); 133 std::wstring pathValue = ConvertToUTF16(systemPathValue + userPathValue); 134 return _wputenv_s(L"PATH", pathValue.c_str()) == 0; 135 } 136 }