diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/Helpers/EnvironmentVariablesHelper.cs b/src/modules/EnvironmentVariables/EnvironmentVariables/Helpers/EnvironmentVariablesHelper.cs index 4933050e82..1979e9b14f 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariables/Helpers/EnvironmentVariablesHelper.cs +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/Helpers/EnvironmentVariablesHelper.cs @@ -65,6 +65,9 @@ namespace EnvironmentVariables.Helpers return baseKey.OpenSubKey(keyName, writable: writable); } + // Code taken from https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs + // Set variables directly to registry instead of using Environment API - Environment.SetEnvironmentVariable() has 1 second timeout for SendNotifyMessage(WM_SETTINGSCHANGED). + // When applying profile, this would take num_of_variables * 1s to propagate the changes. We do manually SendNotifyMessage with no timeout where needed. private static void SetEnvironmentVariableFromRegistryWithoutNotify(string variable, string value, bool fromMachine) { const int MaxUserEnvVariableLength = 255; // User-wide env vars stored in the registry have names limited to 255 chars @@ -84,7 +87,15 @@ namespace EnvironmentVariables.Helpers } else { - environmentKey.SetValue(variable, value); + // If a variable contains %, we save it as a REG_EXPAND_SZ, which is the same behavior as the Windows default environment variables editor. + if (value.Contains('%')) + { + environmentKey.SetValue(variable, value, RegistryValueKind.ExpandString); + } + else + { + environmentKey.SetValue(variable, value, RegistryValueKind.String); + } } } } @@ -102,23 +113,32 @@ namespace EnvironmentVariables.Helpers } } + // Code taken from https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs + // Reading variables from registry instead of using Environment API, because Environment API expands variables by default. internal static void GetVariables(EnvironmentVariableTarget target, VariablesSet set) { - var variables = Environment.GetEnvironmentVariables(target); var sortedList = new SortedList(); - foreach (DictionaryEntry variable in variables) + bool fromMachine = target == EnvironmentVariableTarget.Machine ? true : false; + + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: false)) { - string key = variable.Key as string; - string value = variable.Value as string; - - if (string.IsNullOrEmpty(key)) + if (environmentKey != null) { - continue; + foreach (string name in environmentKey.GetValueNames()) + { + string value = environmentKey.GetValue(name, string.Empty, RegistryValueOptions.DoNotExpandEnvironmentNames).ToString(); + try + { + Variable entry = new Variable(name, value, set.Type); + sortedList.Add(name, entry); + } + catch (ArgumentException) + { + // Throw and catch intentionally to provide non-fatal notification about corrupted environment block + } + } } - - Variable entry = new Variable(key, value, set.Type); - sortedList.Add(key, entry); } set.Variables = new System.Collections.ObjectModel.ObservableCollection(sortedList.Values); diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/ViewModels/MainViewModel.cs b/src/modules/EnvironmentVariables/EnvironmentVariables/ViewModels/MainViewModel.cs index 164325b4bb..428d3db517 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariables/ViewModels/MainViewModel.cs +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/ViewModels/MainViewModel.cs @@ -132,10 +132,13 @@ namespace EnvironmentVariables.ViewModels var variables = new List(); if (AppliedProfile != null) { - variables = variables.Concat(AppliedProfile.Variables.OrderBy(x => x.Name)).ToList(); + variables = variables.Concat(AppliedProfile.Variables.Select(x => new Variable(x.Name, Environment.ExpandEnvironmentVariables(x.Values), VariablesSetType.Profile)).OrderBy(x => x.Name)).ToList(); } - variables = variables.Concat(UserDefaultSet.Variables.OrderBy(x => x.Name)).Concat(SystemDefaultSet.Variables.OrderBy(x => x.Name)).ToList(); + // Variables are expanded to be shown in the applied variables section, so the user sees their actual values. + variables = variables.Concat(UserDefaultSet.Variables.Select(x => new Variable(x.Name, Environment.ExpandEnvironmentVariables(x.Values), VariablesSetType.User)).OrderBy(x => x.Name)) + .Concat(SystemDefaultSet.Variables.Select(x => new Variable(x.Name, Environment.ExpandEnvironmentVariables(x.Values), VariablesSetType.System)).OrderBy(x => x.Name)) + .ToList(); // Handle PATH variable - add USER value to the end of the SYSTEM value var profilePath = variables.Where(x => x.Name.Equals("PATH", StringComparison.OrdinalIgnoreCase) && x.ParentType == VariablesSetType.Profile).FirstOrDefault();