Skip to content

Commit

Permalink
[EnvVar]Fix how expanded variables are processed (microsoft#29418)
Browse files Browse the repository at this point in the history
* Read variables from registry (without expanding), not by using Environment API
Fix seting variables to registry

* Expand in Applied variables list

* Remove using

* Add comments
  • Loading branch information
stefansjfw authored Oct 25, 2023
1 parent ef6b17c commit 04a7adf
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ private static RegistryKey OpenEnvironmentKeyIfExists(bool fromMachine, bool wri
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
Expand All @@ -84,7 +87,15 @@ private static void SetEnvironmentVariableFromRegistryWithoutNotify(string varia
}
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);
}
}
}
}
Expand All @@ -102,23 +113,32 @@ internal static void NotifyEnvironmentChange()
}
}

// 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<string, Variable>();

foreach (DictionaryEntry variable in variables)
{
string key = variable.Key as string;
string value = variable.Value as string;
bool fromMachine = target == EnvironmentVariableTarget.Machine ? true : false;

if (string.IsNullOrEmpty(key))
using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: false))
{
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<Variable>(sortedList.Values);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,13 @@ private void PopulateAppliedVariables()
var variables = new List<Variable>();
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();
Expand Down

0 comments on commit 04a7adf

Please sign in to comment.