Change Environment.Version to return product version (#22664)
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / shared / System / Environment.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Reflection;
9 using System.Threading;
10
11 namespace System
12 {
13     public static partial class Environment
14     {
15         public static string GetEnvironmentVariable(string variable)
16         {
17             if (variable == null)
18                 throw new ArgumentNullException(nameof(variable));
19
20             return GetEnvironmentVariableCore(variable);
21         }
22
23         public static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target)
24         {
25             if (target == EnvironmentVariableTarget.Process)
26                 return GetEnvironmentVariable(variable);
27
28             if (variable == null)
29                 throw new ArgumentNullException(nameof(variable));
30
31             bool fromMachine = ValidateAndConvertRegistryTarget(target);
32             return GetEnvironmentVariableFromRegistry(variable, fromMachine);
33         }
34
35         public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target)
36         {
37             if (target == EnvironmentVariableTarget.Process)
38                 return GetEnvironmentVariables();
39
40             bool fromMachine = ValidateAndConvertRegistryTarget(target);
41             return GetEnvironmentVariablesFromRegistry(fromMachine);
42         }
43
44         public static void SetEnvironmentVariable(string variable, string value)
45         {
46             ValidateVariableAndValue(variable, ref value);
47             SetEnvironmentVariableCore(variable, value);
48         }
49
50         public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target)
51         {
52             if (target == EnvironmentVariableTarget.Process)
53             {
54                 SetEnvironmentVariable(variable, value);
55                 return;
56             }
57
58             ValidateVariableAndValue(variable, ref value);
59
60             bool fromMachine = ValidateAndConvertRegistryTarget(target);
61             SetEnvironmentVariableFromRegistry(variable, value, fromMachine: fromMachine);
62         }
63
64         public static string CommandLine => PasteArguments.Paste(GetCommandLineArgs(), pasteFirstArgumentUsingArgV0Rules: true);
65
66         public static string CurrentDirectory
67         {
68             get => CurrentDirectoryCore;
69             set
70             {
71                 if (value == null)
72                     throw new ArgumentNullException(nameof(value));
73
74                 if (value.Length == 0)
75                     throw new ArgumentException(SR.Argument_PathEmpty, nameof(value));
76
77                 CurrentDirectoryCore = value;
78             }
79         }
80
81         public static string ExpandEnvironmentVariables(string name)
82         {
83             if (name == null)
84                 throw new ArgumentNullException(nameof(name));
85
86             if (name.Length == 0)
87                 return name;
88
89             return ExpandEnvironmentVariablesCore(name);
90         }
91
92         private static string[] s_commandLineArgs;
93
94         internal static void SetCommandLineArgs(string[] cmdLineArgs) // invoked from VM
95         {
96             s_commandLineArgs = cmdLineArgs;
97         }
98
99         public static string GetFolderPath(SpecialFolder folder) => GetFolderPath(folder, SpecialFolderOption.None);
100
101         public static string GetFolderPath(SpecialFolder folder, SpecialFolderOption option)
102         {
103             if (!Enum.IsDefined(typeof(SpecialFolder), folder))
104                 throw new ArgumentOutOfRangeException(nameof(folder), folder, SR.Format(SR.Arg_EnumIllegalVal, folder));
105
106             if (option != SpecialFolderOption.None && !Enum.IsDefined(typeof(SpecialFolderOption), option))
107                 throw new ArgumentOutOfRangeException(nameof(option), option, SR.Format(SR.Arg_EnumIllegalVal, option));
108
109             return GetFolderPathCore(folder, option);
110         }
111
112         public static bool Is64BitProcess => IntPtr.Size == 8;
113
114         public static bool Is64BitOperatingSystem => Is64BitProcess || Is64BitOperatingSystemWhen32BitProcess;
115
116         private static OperatingSystem s_osVersion;
117
118         public static OperatingSystem OSVersion
119         {
120             get
121             {
122                 if (s_osVersion == null)
123                 {
124                     Interlocked.CompareExchange(ref s_osVersion, GetOSVersion(), null);
125                 }
126                 return s_osVersion;
127             }
128         }
129
130         public static bool UserInteractive => true;
131
132         public static Version Version
133         {
134             get
135             {
136                 // FX_PRODUCT_VERSION is expected to be set by the host
137                 string versionString = (string)AppContext.GetData("FX_PRODUCT_VERSION");
138
139                 if (versionString == null)
140                 {
141                     // Use AssemblyInformationalVersionAttribute as fallback if the exact product version is not specified by the host
142                     versionString = typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
143                 }
144
145                 ReadOnlySpan<char> versionSpan = versionString.AsSpan();
146
147                 // Strip optional suffixes
148                 int separatorIndex = versionSpan.IndexOfAny("-+ ");
149                 if (separatorIndex != -1)
150                     versionSpan = versionSpan.Slice(0, separatorIndex);
151
152                 // Return zeros rather then failing if the version string fails to parse
153                 return Version.TryParse(versionSpan, out Version version) ? version : new Version();
154             }
155         }
156
157         public static long WorkingSet
158         {
159             get
160             {
161                 // Use reflection to access the implementation in System.Diagnostics.Process.dll.  While far from ideal,
162                 // we do this to avoid duplicating the Windows, Linux, macOS, and potentially other platform-specific implementations
163                 // present in Process.  If it proves important, we could look at separating that functionality out of Process into
164                 // Common files which could also be included here.
165                 Type processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
166                 IDisposable currentProcess = processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) as IDisposable;
167                 if (currentProcess != null)
168                 {
169                     using (currentProcess)
170                     {
171                         object result = processType.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null);
172                         if (result is long) return (long)result;
173                     }
174                 }
175
176                 // Could not get the current working set.
177                 return 0;
178             }
179         }
180
181         private static bool ValidateAndConvertRegistryTarget(EnvironmentVariableTarget target)
182         {
183             Debug.Assert(target != EnvironmentVariableTarget.Process);
184
185             if (target == EnvironmentVariableTarget.Machine)
186                 return true;
187
188             if (target == EnvironmentVariableTarget.User)
189                 return false;
190
191             throw new ArgumentOutOfRangeException(nameof(target), target, SR.Format(SR.Arg_EnumIllegalVal, target));
192         }
193
194         private static void ValidateVariableAndValue(string variable, ref string value)
195         {
196             if (variable == null)
197                 throw new ArgumentNullException(nameof(variable));
198
199             if (variable.Length == 0)
200                 throw new ArgumentException(SR.Argument_StringZeroLength, nameof(variable));
201
202             if (variable[0] == '\0')
203                 throw new ArgumentException(SR.Argument_StringFirstCharIsZero, nameof(variable));
204
205             if (variable.Contains('='))
206                 throw new ArgumentException(SR.Argument_IllegalEnvVarName, nameof(variable));
207
208             if (string.IsNullOrEmpty(value) || value[0] == '\0')
209             {
210                 // Explicitly null out value if it's empty
211                 value = null;
212             }
213         }
214     }
215 }