/// On Windows operating system the name of environment variables is case insensitive. This means if we have a user and machine variable with differences in their name casing (eg. test vs Test), the name casing from machine level is used and won't be overwritten by the user var.
/// Example for Window's behavior: test=ValueMachine (Machine level) + TEST=ValueUser (User level) => test=ValueUser (merged)
/// To get the same behavior we use "StringComparer.OrdinalIgnoreCase" as compare property for the HashSet and Dictionaries where we merge machine and user variable names.
/// This method is called from <see cref="MainWindow.OnSourceInitialized"/> to initialize a list of protected environment variables right after the PT Run process has been invoked.
/// Protected variables are environment variables that must not be changed on process level when updating the environment variables with changes on machine and/or user level.
/// We cache the relevant variable names in the private, static and readonly variable <see cref="_protectedProcessVariables"/> of this class.
// We must compare case insensitive (see dictionary assignment) to avoid false positives when the variable name has changed (Example: "path" -> "Path")
// The name of environment variables must not be null, empty or have a length of zero.
// But if the value of the environment variable is null or an empty string then the variable is explicit defined for deletion. => Here we don't need to check anything.
// => We added the if statement (next line) because of the issue #13172 where a user reported System.ArgumentNullException from "Environment.SetEnvironmentVariable()".
if(!string.IsNullOrEmpty(kv.Key)&varNameLength>0)
{
try
{
// If the variable is not listed as protected/don't override on process level, then update it. (See method "GetProtectedEnvironmentVariables" of this class.)
if(!_protectedProcessVariables.Contains(kv.Key))
{
// We have to delete the variables first that we can update their name if changed by the user. (Example: "path" => "Path")
// The machine and user variables that have been deleted by the user having an empty string as variable value. Because of this we check the values of the variables in our dictionary against "null" and "string.Empty". This check prevents us from invoking a (second) delete command.
// The dotnet method doesn't throw an exception if the variable which should be deleted doesn't exist.
// Don't log for the variable "USERNAME" if the variable's value is "System". (Then it is a false positive because per default the variable only exists on machine level with the value "System".)
Log.Warn($"Skipping update of the environment variable [{kv.Key}] for the PT Run process. This variable is listed as protected process variable and changing them can cause unexpected behavior. (The variable value has a length of [{varValueLength}].)",typeof(PowerLauncher.Helper.EnvironmentHelper));
}
}
}
#pragmawarningdisableCA1031// Do not catch general exception types
catch(Exceptionex)
#pragmawarningrestoreCA1031// Do not catch general exception types
{
// The dotnet method "System.Environment.SetEnvironmentVariable" has it's own internal method to check the input parameters. Here we catch the exceptions that we don't check before updating the environment variable and log it to avoid crashes of PT Run.
Log.Exception($"Unhandled exception while updating the environment variable [{kv.Key}] for the PT Run process. (The variable value has a length of [{varValueLength}].)",ex,typeof(PowerLauncher.Helper.EnvironmentHelper));
}
}
else
{
// Log the error when variable name is null, empty or has a length of zero.
Log.Error($"Failed to update the environment variable [{kv.Key}] for the PT Run process. Their name is null or empty. (The variable value has a length of [{varValueLength}].)",typeof(PowerLauncher.Helper.EnvironmentHelper));
}
}
});
}
/// <summary>
/// This method returns a Dictionary with a merged set of machine and user environment variables. If we run as "system" only machine variables are returned.
/// </summary>
/// <param name="environment">The dictionary that should be filled with the merged variables.</param>
// Checking if the list of (machine) variables contains a path variable
if(environment.ContainsKey(PathVariableName))
{
// When we merging the PATH variables we can't simply overwrite machine layer's value. The path variable must be joined by appending the user value to the machine value.
// This is the official behavior and checked by trying it out on the physical machine.
// Log warning and only write user value into dictionary
Log.Warn("The List of machine variables doesn't contain a path variable! The merged list won't contain any machine paths in the path variable.",typeof(PowerLauncher.Helper.EnvironmentHelper));
Log.Exception($"Unhandled exception while getting the environment variables for target '{target}'.",ex,typeof(PowerLauncher.Helper.EnvironmentHelper));