From c1ba2cdb467d964bab593c078f303edb42024e96 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 30 Jan 2019 16:22:27 -0500 Subject: [PATCH 1/3] Switch CoreLib over to using shared Environment from corefx (#22106) This requires merging/adapting the implementation with EnvironmentAugments (which goes away completely), the shared files, what corert has, etc. Signed-off-by: dotnet-bot --- .../Interop.ExpandEnvironmentStrings.cs | 4 +- .../Interop.GetEnvironmentVariable.cs | 23 +++ .../System.Private.CoreLib.Shared.projitems | 48 ++++- .../shared/System/Environment.NoRegistry.cs | 21 +++ .../shared/System/Environment.Unix.cs | 20 +-- .../System/Environment.Variables.Windows.cs | 168 ++++++++++++++++++ .../shared/System/Environment.Win32.cs | 140 ++++++++++++++- .../shared/System/Environment.Windows.cs | 26 +-- .../shared/System/Environment.cs | 157 ++++++++-------- 9 files changed, 488 insertions(+), 119 deletions(-) create mode 100644 src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs create mode 100644 src/System.Private.CoreLib/shared/System/Environment.NoRegistry.cs create mode 100644 src/System.Private.CoreLib/shared/System/Environment.Variables.Windows.cs diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs index ba942ba6ff7..ce3db6f2b45 100644 --- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs @@ -8,7 +8,7 @@ internal partial class Interop { internal partial class Kernel32 { - [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint ExpandEnvironmentStringsW(string lpSrc, ref char lpDst, uint nSize); + [DllImport(Libraries.Kernel32, EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern uint ExpandEnvironmentStrings(string lpSrc, ref char lpDst, uint nSize); } } diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs new file mode 100644 index 00000000000..13adefc2163 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal static unsafe int GetEnvironmentVariable(string lpName, Span buffer) + { + fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer)) + { + return GetEnvironmentVariable(lpName, bufferPtr, buffer.Length); + } + } + + [DllImport(Libraries.Kernel32, EntryPoint = "GetEnvironmentVariableW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern unsafe int GetEnvironmentVariable(string lpName, char* lpBuffer, int nSize); + } +} diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index a10e0c247be..e0a7aa594c1 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -211,6 +211,9 @@ + + + @@ -370,12 +373,15 @@ + + + @@ -915,32 +921,40 @@ + + + - + - + + + + + + @@ -952,8 +966,8 @@ + - @@ -971,9 +985,12 @@ + + + @@ -987,12 +1004,14 @@ + + @@ -1030,6 +1049,7 @@ + @@ -1043,10 +1063,15 @@ + + + + + @@ -1066,15 +1091,23 @@ + + + + + + + + @@ -1084,12 +1117,14 @@ + + @@ -1101,16 +1136,23 @@ + + + + + + + diff --git a/src/System.Private.CoreLib/shared/System/Environment.NoRegistry.cs b/src/System.Private.CoreLib/shared/System/Environment.NoRegistry.cs new file mode 100644 index 00000000000..427d29d818f --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Environment.NoRegistry.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Collections; +using Microsoft.Win32; + +namespace System +{ + public static partial class Environment + { + // Systems without the Windows registry pretend that it's always empty. + + private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) => null; + + private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) { } + + private static IDictionary GetEnvironmentVariablesFromRegistry(bool fromMachine) => new Hashtable(); + } +} diff --git a/src/System.Private.CoreLib/shared/System/Environment.Unix.cs b/src/System.Private.CoreLib/shared/System/Environment.Unix.cs index e18b5951ea1..a69aa63a722 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.Unix.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.Unix.cs @@ -16,17 +16,15 @@ namespace System { public static partial class Environment { - internal static readonly bool IsMac = Interop.Sys.GetUnixName() == "OSX"; + private static readonly bool s_isMac = Interop.Sys.GetUnixName() == "OSX"; private static Func s_directoryCreateDirectory; private static string CurrentDirectoryCore { - get { return Interop.Sys.GetCwd(); } - set { Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirectory: true); } + get => Interop.Sys.GetCwd(); + set => Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirectory: true); } - public static int ExitCode { get { return EnvironmentAugments.ExitCode; } set { EnvironmentAugments.ExitCode = value; } } - private static string ExpandEnvironmentVariablesCore(string name) { Span initialBuffer = stackalloc char[128]; @@ -101,7 +99,7 @@ private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder) case SpecialFolder.CommonApplicationData: return "/usr/share"; case SpecialFolder.CommonTemplates: return "/usr/share/templates"; } - if (IsMac) + if (s_isMac) { switch (folder) { @@ -161,17 +159,17 @@ private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder) return ReadXdgDirectory(home, "XDG_VIDEOS_DIR", "Videos"); case SpecialFolder.MyMusic: - return IsMac ? Path.Combine(home, "Music") : ReadXdgDirectory(home, "XDG_MUSIC_DIR", "Music"); + return s_isMac ? Path.Combine(home, "Music") : ReadXdgDirectory(home, "XDG_MUSIC_DIR", "Music"); case SpecialFolder.MyPictures: - return IsMac ? Path.Combine(home, "Pictures") : ReadXdgDirectory(home, "XDG_PICTURES_DIR", "Pictures"); + return s_isMac ? Path.Combine(home, "Pictures") : ReadXdgDirectory(home, "XDG_PICTURES_DIR", "Pictures"); case SpecialFolder.Fonts: - return IsMac ? Path.Combine(home, "Library", "Fonts") : Path.Combine(home, ".fonts"); + return s_isMac ? Path.Combine(home, "Library", "Fonts") : Path.Combine(home, ".fonts"); case SpecialFolder.Favorites: - if (IsMac) return Path.Combine(home, "Library", "Favorites"); + if (s_isMac) return Path.Combine(home, "Library", "Favorites"); break; case SpecialFolder.InternetCache: - if (IsMac) return Path.Combine(home, "Library", "Caches"); + if (s_isMac) return Path.Combine(home, "Library", "Caches"); break; } diff --git a/src/System.Private.CoreLib/shared/System/Environment.Variables.Windows.cs b/src/System.Private.CoreLib/shared/System/Environment.Variables.Windows.cs new file mode 100644 index 00000000000..7e8bcce6ad1 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Environment.Variables.Windows.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace System +{ + public static partial class Environment + { + private static string GetEnvironmentVariableCore(string variable) + { + Span buffer = stackalloc char[128]; // a somewhat reasonable default size + int requiredSize = Interop.Kernel32.GetEnvironmentVariable(variable, buffer); + + if (requiredSize == 0 && Marshal.GetLastWin32Error() == Interop.Errors.ERROR_ENVVAR_NOT_FOUND) + { + return null; + } + + if (requiredSize <= buffer.Length) + { + return new string(buffer.Slice(0, requiredSize)); + } + + char[] chars = ArrayPool.Shared.Rent(requiredSize); + try + { + buffer = chars; + requiredSize = Interop.Kernel32.GetEnvironmentVariable(variable, buffer); + if ((requiredSize == 0 && Marshal.GetLastWin32Error() == Interop.Errors.ERROR_ENVVAR_NOT_FOUND) || + requiredSize > buffer.Length) + { + return null; + } + + return new string(buffer.Slice(0, requiredSize)); + } + finally + { + ArrayPool.Shared.Return(chars); + } + } + + private static void SetEnvironmentVariableCore(string variable, string value) + { + if (!Interop.Kernel32.SetEnvironmentVariable(variable, value)) + { + int errorCode = Marshal.GetLastWin32Error(); + switch (errorCode) + { + case Interop.Errors.ERROR_ENVVAR_NOT_FOUND: + // Allow user to try to clear a environment variable + return; + + case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: + // The error message from Win32 is "The filename or extension is too long", + // which is not accurate. + throw new ArgumentException(SR.Format(SR.Argument_LongEnvVarValue)); + + case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: + case Interop.Errors.ERROR_NO_SYSTEM_RESOURCES: + throw new OutOfMemoryException(Interop.Kernel32.GetMessage(errorCode)); + + default: + throw new ArgumentException(Interop.Kernel32.GetMessage(errorCode)); + } + } + } + + public static unsafe IDictionary GetEnvironmentVariables() + { + char* pStrings = Interop.Kernel32.GetEnvironmentStrings(); + if (pStrings == null) + { + throw new OutOfMemoryException(); + } + + try + { + // Format for GetEnvironmentStrings is: + // [=HiddenVar=value\0]* [Variable=value\0]* \0 + // See the description of Environment Blocks in MSDN's + // CreateProcess page (null-terminated array of null-terminated strings). + + // Search for terminating \0\0 (two unicode \0's). + char* p = pStrings; + while (!(*p == '\0' && *(p + 1) == '\0')) + { + p++; + } + Span block = new Span(pStrings, (int)(p - pStrings + 1)); + + // Format for GetEnvironmentStrings is: + // (=HiddenVar=value\0 | Variable=value\0)* \0 + // See the description of Environment Blocks in MSDN's + // CreateProcess page (null-terminated array of null-terminated strings). + // Note the =HiddenVar's aren't always at the beginning. + + // Copy strings out, parsing into pairs and inserting into the table. + // The first few environment variable entries start with an '='. + // The current working directory of every drive (except for those drives + // you haven't cd'ed into in your DOS window) are stored in the + // environment block (as =C:=pwd) and the program's exit code is + // as well (=ExitCode=00000000). + + var results = new Hashtable(); + for (int i = 0; i < block.Length; i++) + { + int startKey = i; + + // Skip to key. On some old OS, the environment block can be corrupted. + // Some will not have '=', so we need to check for '\0'. + while (block[i] != '=' && block[i] != '\0') + { + i++; + } + + if (block[i] == '\0') + { + continue; + } + + // Skip over environment variables starting with '=' + if (i - startKey == 0) + { + while (block[i] != 0) + { + i++; + } + + continue; + } + + string key = new string(block.Slice(startKey, i - startKey)); + i++; // skip over '=' + + int startValue = i; + while (block[i] != 0) + { + i++; // Read to end of this entry + } + + string value = new string(block.Slice(startValue, i - startValue)); // skip over 0 handled by for loop's i++ + try + { + results.Add(key, value); + } + catch (ArgumentException) + { + // Throw and catch intentionally to provide non-fatal notification about corrupted environment block + } + } + return results; + } + finally + { + bool success = Interop.Kernel32.FreeEnvironmentStrings(pStrings); + Debug.Assert(success); + } + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Environment.Win32.cs b/src/System.Private.CoreLib/shared/System/Environment.Win32.cs index 3ac7663b50d..f7b87ff7866 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.Win32.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.Win32.cs @@ -2,18 +2,126 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections; +using System.Diagnostics; using System.IO; -using System.Text; +using System.Reflection; using System.Runtime.InteropServices; +using System.Text; +using Internal.Win32; namespace System { public static partial class Environment { + private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) + { + Debug.Assert(variable != null); + +#if FEATURE_APPX + if (ApplicationModel.IsUap) + return null; // Systems without the Windows registry pretend that it's always empty. +#endif + + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: false)) + { + return environmentKey?.GetValue(variable) as string; + } + } + + private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) + { + Debug.Assert(variable != null); + +#if FEATURE_APPX + if (ApplicationModel.IsUap) + return; // Systems without the Windows registry pretend that it's always empty. +#endif + + const int MaxUserEnvVariableLength = 255; // User-wide env vars stored in the registry have names limited to 255 chars + if (!fromMachine && variable.Length >= MaxUserEnvVariableLength) + { + throw new ArgumentException(SR.Argument_LongEnvVarValue, nameof(variable)); + } + + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: true)) + { + if (environmentKey != null) + { + if (value == null) + { + environmentKey.DeleteValue(variable, throwOnMissingValue: false); + } + else + { + environmentKey.SetValue(variable, value); + } + } + } + + // send a WM_SETTINGCHANGE message to all windows + IntPtr r = Interop.User32.SendMessageTimeout(new IntPtr(Interop.User32.HWND_BROADCAST), Interop.User32.WM_SETTINGCHANGE, IntPtr.Zero, "Environment", 0, 1000, IntPtr.Zero); + Debug.Assert(r != IntPtr.Zero, "SetEnvironmentVariable failed: " + Marshal.GetLastWin32Error()); + } + + private static IDictionary GetEnvironmentVariablesFromRegistry(bool fromMachine) + { + var results = new Hashtable(); +#if FEATURE_APPX + if (ApplicationModel.IsUap) // Systems without the Windows registry pretend that it's always empty. + return results; +#endif + + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: false)) + { + if (environmentKey != null) + { + foreach (string name in environmentKey.GetValueNames()) + { + string value = environmentKey.GetValue(name, "").ToString(); + try + { + results.Add(name, value); + } + catch (ArgumentException) + { + // Throw and catch intentionally to provide non-fatal notification about corrupted environment block + } + } + } + } + + return results; + } + + private static RegistryKey OpenEnvironmentKeyIfExists(bool fromMachine, bool writable) + { + RegistryKey baseKey; + string keyName; + + if (fromMachine) + { + baseKey = Registry.LocalMachine; + keyName = @"System\CurrentControlSet\Control\Session Manager\Environment"; + } + else + { + baseKey = Registry.CurrentUser; + keyName = "Environment"; + } + + return baseKey.OpenSubKey(keyName, writable: writable); + } + public static string UserName { get { +#if FEATURE_APPX + if (ApplicationModel.IsUap) + return "Windows User"; +#endif + // 40 should be enough as we're asking for the SAM compatible name (DOMAIN\User). // The max length should be 15 (domain) + 1 (separator) + 20 (name) + null. If for // some reason it isn't, we'll grow the buffer. @@ -60,6 +168,11 @@ public static string UserDomainName { get { +#if FEATURE_APPX + if (ApplicationModel.IsUap) + return "Windows Domain"; +#endif + // See the comment in UserName Span initialBuffer = stackalloc char[40]; var builder = new ValueStringBuilder(initialBuffer); @@ -108,6 +221,11 @@ public static string UserDomainName private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option) { +#if FEATURE_APPX + if (ApplicationModel.IsUap) + return WinRTFolderPaths.GetFolderPath(folder, option); +#endif + // We're using SHGetKnownFolderPath instead of SHGetFolderPath as SHGetFolderPath is // capped at MAX_PATH. // @@ -280,5 +398,25 @@ private static string GetKnownFolderPath(string folderGuid, SpecialFolderOption return path; } + +#if FEATURE_APPX + private static class WinRTFolderPaths + { + private static Func s_winRTFolderPathsGetFolderPath; + + public static string GetFolderPath(SpecialFolder folder, SpecialFolderOption option) + { + if (s_winRTFolderPathsGetFolderPath == null) + { + Type winRtFolderPathsType = Type.GetType("System.WinRTFolderPaths, System.Runtime.WindowsRuntime, Version=4.0.14.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", throwOnError: false); + MethodInfo getFolderPathsMethod = winRtFolderPathsType?.GetMethod("GetFolderPath", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(SpecialFolder), typeof(SpecialFolderOption) }, null); + var d = (Func)getFolderPathsMethod?.CreateDelegate(typeof(Func)); + s_winRTFolderPathsGetFolderPath = d ?? delegate { return null; }; + } + + return s_winRTFolderPathsGetFolderPath(folder, option); + } + } +#endif } } diff --git a/src/System.Private.CoreLib/shared/System/Environment.Windows.cs b/src/System.Private.CoreLib/shared/System/Environment.Windows.cs index 991c69c0230..db56ea1a7aa 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.Windows.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.Windows.cs @@ -3,9 +3,10 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; -using Internal.Runtime.Augments; +using Microsoft.Win32; namespace System { @@ -59,15 +60,13 @@ public static int SystemPageSize } } - public static int ExitCode { get { return EnvironmentAugments.ExitCode; } set { EnvironmentAugments.ExitCode = value; } } - private static string ExpandEnvironmentVariablesCore(string name) { Span initialBuffer = stackalloc char[128]; var builder = new ValueStringBuilder(initialBuffer); uint length; - while ((length = Interop.Kernel32.ExpandEnvironmentStringsW(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) + while ((length = Interop.Kernel32.ExpandEnvironmentStrings(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) { builder.EnsureCapacity((int)length); } @@ -80,21 +79,12 @@ private static string ExpandEnvironmentVariablesCore(string name) return builder.ToString(); } - private static bool Is64BitOperatingSystemWhen32BitProcess - => Interop.Kernel32.IsWow64Process(Interop.Kernel32.GetCurrentProcess(), out bool isWow64) && isWow64; + private static bool Is64BitOperatingSystemWhen32BitProcess => + Interop.Kernel32.IsWow64Process(Interop.Kernel32.GetCurrentProcess(), out bool isWow64) && isWow64; - public static string MachineName - { - get - { - string name = Interop.Kernel32.GetComputerName(); - if (name == null) - { - throw new InvalidOperationException(SR.InvalidOperation_ComputerName); - } - return name; - } - } + public static string MachineName => + Interop.Kernel32.GetComputerName() ?? + throw new InvalidOperationException(SR.InvalidOperation_ComputerName); private static readonly unsafe Lazy s_osVersion = new Lazy(() => { diff --git a/src/System.Private.CoreLib/shared/System/Environment.cs b/src/System.Private.CoreLib/shared/System/Environment.cs index 6daccb8da99..870f22d49a9 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Reflection; -using System.Runtime.CompilerServices; -using Internal.Runtime.Augments; namespace System { @@ -15,157 +13,113 @@ public static partial class Environment { public static string GetEnvironmentVariable(string variable) { - return EnvironmentAugments.GetEnvironmentVariable(variable); + if (variable == null) + throw new ArgumentNullException(nameof(variable)); + + return GetEnvironmentVariableCore(variable); } public static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) { - return EnvironmentAugments.GetEnvironmentVariable(variable, target); - } + if (target == EnvironmentVariableTarget.Process) + return GetEnvironmentVariable(variable); - public static IDictionary GetEnvironmentVariables() - { - // To maintain complete compatibility with prior versions we need to return a Hashtable. - // We did ship a prior version of Core with LowLevelDictionary, which does iterate the - // same (e.g. yields DictionaryEntry), but it is not a public type. - // - // While we could pass Hashtable back from CoreCLR the type is also defined here. We only - // want to surface the local Hashtable. - return EnvironmentAugments.EnumerateEnvironmentVariables().ToHashtable(); + if (variable == null) + throw new ArgumentNullException(nameof(variable)); + + bool fromMachine = ValidateAndConvertRegistryTarget(target); + return GetEnvironmentVariableFromRegistry(variable, fromMachine); } public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target) { - // See comments in GetEnvironmentVariables() - return EnvironmentAugments.EnumerateEnvironmentVariables(target).ToHashtable(); - } + if (target == EnvironmentVariableTarget.Process) + return GetEnvironmentVariables(); - private static Hashtable ToHashtable(this IEnumerable> pairs) - { - Hashtable hashTable = new Hashtable(); - foreach (KeyValuePair pair in pairs) - { - try - { - hashTable.Add(pair.Key, pair.Value); - } - catch (ArgumentException) - { - // Throw and catch intentionally to provide non-fatal notification about corrupted environment block - } - } - return hashTable; + bool fromMachine = ValidateAndConvertRegistryTarget(target); + return GetEnvironmentVariablesFromRegistry(fromMachine); } public static void SetEnvironmentVariable(string variable, string value) { - EnvironmentAugments.SetEnvironmentVariable(variable, value); + ValidateVariableAndValue(variable, ref value); + SetEnvironmentVariableCore(variable, value); } public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target) { - EnvironmentAugments.SetEnvironmentVariable(variable, value, target); - } - - public static string CommandLine - { - get + if (target == EnvironmentVariableTarget.Process) { - return PasteArguments.Paste(GetCommandLineArgs(), pasteFirstArgumentUsingArgV0Rules: true); + SetEnvironmentVariable(variable, value); + return; } + + ValidateVariableAndValue(variable, ref value); + + bool fromMachine = ValidateAndConvertRegistryTarget(target); + SetEnvironmentVariableFromRegistry(variable, value, fromMachine: fromMachine); } + public static string CommandLine => PasteArguments.Paste(GetCommandLineArgs(), pasteFirstArgumentUsingArgV0Rules: true); + public static string CurrentDirectory { - get { return CurrentDirectoryCore; } + get => CurrentDirectoryCore; set { if (value == null) - { throw new ArgumentNullException(nameof(value)); - } if (value.Length == 0) - { throw new ArgumentException(SR.Argument_PathEmpty, nameof(value)); - } CurrentDirectoryCore = value; } } - public static int CurrentManagedThreadId => EnvironmentAugments.CurrentManagedThreadId; - - public static void Exit(int exitCode) => EnvironmentAugments.Exit(exitCode); - - public static void FailFast(string message) => FailFast(message, exception: null); - - public static void FailFast(string message, Exception exception) => EnvironmentAugments.FailFast(message, exception); - public static string ExpandEnvironmentVariables(string name) { if (name == null) - { throw new ArgumentNullException(nameof(name)); - } if (name.Length == 0) - { return name; - } return ExpandEnvironmentVariablesCore(name); } - public static string[] GetCommandLineArgs() => EnvironmentAugments.GetCommandLineArgs(); + private static string[] s_commandLineArgs; + + internal static void SetCommandLineArgs(string[] cmdLineArgs) // invoked from VM + { + s_commandLineArgs = cmdLineArgs; + } public static string GetFolderPath(SpecialFolder folder) => GetFolderPath(folder, SpecialFolderOption.None); public static string GetFolderPath(SpecialFolder folder, SpecialFolderOption option) { if (!Enum.IsDefined(typeof(SpecialFolder), folder)) - { throw new ArgumentOutOfRangeException(nameof(folder), folder, SR.Format(SR.Arg_EnumIllegalVal, folder)); - } if (option != SpecialFolderOption.None && !Enum.IsDefined(typeof(SpecialFolderOption), option)) - { throw new ArgumentOutOfRangeException(nameof(option), option, SR.Format(SR.Arg_EnumIllegalVal, option)); - } return GetFolderPathCore(folder, option); } - public static bool HasShutdownStarted => EnvironmentAugments.HasShutdownStarted; - public static bool Is64BitProcess => IntPtr.Size == 8; public static bool Is64BitOperatingSystem => Is64BitProcess || Is64BitOperatingSystemWhen32BitProcess; public static OperatingSystem OSVersion => s_osVersion.Value; - public static int ProcessorCount => EnvironmentAugments.ProcessorCount; - - public static string StackTrace - { - [MethodImpl(MethodImplOptions.NoInlining)] // Prevent inlining from affecting where the stacktrace starts - get - { - return EnvironmentAugments.StackTrace; - } - } - - public static int TickCount => EnvironmentAugments.TickCount; - public static bool UserInteractive => true; - public static Version Version - { - // Previously this represented the File version of mscorlib.dll. Many other libraries in the framework and outside took dependencies on the first three parts of this version - // remaining constant throughout 4.x. From 4.0 to 4.5.2 this was fine since the file version only incremented the last part. Starting with 4.6 we switched to a file versioning - // scheme that matched the product version. In order to preserve compatibility with existing libraries, this needs to be hard-coded. - get { return new Version(4, 0, 30319, 42000); } - } + // Previously this represented the File version of mscorlib.dll. Many other libraries in the framework and outside took dependencies on the first three parts of this version + // remaining constant throughout 4.x. From 4.0 to 4.5.2 this was fine since the file version only incremented the last part. Starting with 4.6 we switched to a file versioning + // scheme that matched the product version. In order to preserve compatibility with existing libraries, this needs to be hard-coded. + public static Version Version => new Version(4, 0, 30319, 42000); public static long WorkingSet { @@ -185,9 +139,44 @@ public static long WorkingSet if (result is long) return (long)result; } } + // Could not get the current working set. return 0; } } + + private static bool ValidateAndConvertRegistryTarget(EnvironmentVariableTarget target) + { + Debug.Assert(target != EnvironmentVariableTarget.Process); + + if (target == EnvironmentVariableTarget.Machine) + return true; + + if (target == EnvironmentVariableTarget.User) + return false; + + throw new ArgumentOutOfRangeException(nameof(target), target, SR.Format(SR.Arg_EnumIllegalVal, target)); + } + + private static void ValidateVariableAndValue(string variable, ref string value) + { + if (variable == null) + throw new ArgumentNullException(nameof(variable)); + + if (variable.Length == 0) + throw new ArgumentException(SR.Argument_StringZeroLength, nameof(variable)); + + if (variable[0] == '\0') + throw new ArgumentException(SR.Argument_StringFirstCharIsZero, nameof(variable)); + + if (variable.Contains('=')) + throw new ArgumentException(SR.Argument_IllegalEnvVarName, nameof(variable)); + + if (string.IsNullOrEmpty(value) || value[0] == '\0') + { + // Explicitly null out value if it's empty + value = null; + } + } } } From 731b47a8084ddf8403b579ca8d078293f2e900a5 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 30 Jan 2019 12:18:01 -0500 Subject: [PATCH 2/3] Adapt corert to Environment moving from corefx --- .../shared/System/Environment.WinRT.cs | 12 +- .../EnvironmentAugments.CoreRT.Win32.cs | 17 -- .../Augments/EnvironmentAugments.CoreRT.cs | 36 ---- .../EnvironmentAugments.FromRegistry.Win32.cs | 90 ---------- ...ntAugments.FromRegistry.WithoutRegistry.cs | 31 ---- .../Augments/EnvironmentAugments.ProjectN.cs | 21 --- .../Augments/EnvironmentAugments.Windows.cs | 161 ------------------ .../Runtime/Augments/EnvironmentAugments.cs | 161 +++--------------- .../Internal/Runtime/Augments/WinRTInterop.cs | 1 + .../StartupCodeHelpers.Extensions.cs | 12 +- .../src/Resources/Strings.resx | 15 ++ .../src/System.Private.CoreLib.csproj | 21 +-- .../System/Environment.CoreRT.NonProjectN.cs | 22 +++ .../src/System/Environment.CoreRT.ProjectN.cs | 18 ++ .../Environment.CoreRT.Unix.cs} | 44 ++--- .../src/System/Environment.CoreRT.Win32.cs | 13 ++ .../src/System/Environment.CoreRT.Windows.cs | 17 ++ .../src/System/Environment.CoreRT.cs | 87 ++++++++++ .../src/System/Environment.Unix.cs | 31 ---- .../src/System/Environment.Win32.cs | 21 --- .../src/System/Environment.Windows.cs | 38 ----- .../src/System/Environment.cs | 124 -------------- .../src/System.Private.Interop.csproj | 1 - .../src/System.Private.Jit.csproj | 1 - .../src/System.Private.TypeLoader.csproj | 1 - 25 files changed, 248 insertions(+), 748 deletions(-) delete mode 100644 src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.Win32.cs delete mode 100644 src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.cs delete mode 100644 src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.Win32.cs delete mode 100644 src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.WithoutRegistry.cs delete mode 100644 src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.ProjectN.cs delete mode 100644 src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs create mode 100644 src/System.Private.CoreLib/src/System/Environment.CoreRT.NonProjectN.cs create mode 100644 src/System.Private.CoreLib/src/System/Environment.CoreRT.ProjectN.cs rename src/System.Private.CoreLib/src/{Internal/Runtime/Augments/EnvironmentAugments.Unix.cs => System/Environment.CoreRT.Unix.cs} (71%) create mode 100644 src/System.Private.CoreLib/src/System/Environment.CoreRT.Win32.cs create mode 100644 src/System.Private.CoreLib/src/System/Environment.CoreRT.Windows.cs create mode 100644 src/System.Private.CoreLib/src/System/Environment.CoreRT.cs delete mode 100644 src/System.Private.CoreLib/src/System/Environment.Unix.cs delete mode 100644 src/System.Private.CoreLib/src/System/Environment.Win32.cs delete mode 100644 src/System.Private.CoreLib/src/System/Environment.Windows.cs delete mode 100644 src/System.Private.CoreLib/src/System/Environment.cs diff --git a/src/System.Private.CoreLib/shared/System/Environment.WinRT.cs b/src/System.Private.CoreLib/shared/System/Environment.WinRT.cs index 72d66c44a61..a8e3dbced80 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.WinRT.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.WinRT.cs @@ -3,14 +3,22 @@ // See the LICENSE file in the project root for more information. using System.IO; +using Internal.Runtime.Augments; namespace System { public static partial class Environment { public static string UserName => "Windows User"; + public static string UserDomainName => "Windows Domain"; - private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option) => - WinRTFolderPaths.GetFolderPath(folder, option); + + private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option) + { + WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks; + return callbacks != null && callbacks.IsAppxModel() ? + callbacks.GetFolderPath(folder, option) : + null; + } } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.Win32.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.Win32.cs deleted file mode 100644 index 5f6a15f01f1..00000000000 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.Win32.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime; - -namespace Internal.Runtime.Augments -{ - public static partial class EnvironmentAugments - { - private static void ExitRaw() - { - Interop.Kernel32.ExitProcess(s_latchedExitCode); - } - } -} diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.cs deleted file mode 100644 index 1939702e07e..00000000000 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime; - -namespace Internal.Runtime.Augments -{ - /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. - public static partial class EnvironmentAugments - { - public static void Exit(int exitCode) - { - s_latchedExitCode = exitCode; - - ShutdownCore(); - - RuntimeImports.RhpShutdown(); - - ExitRaw(); - } - - private static string[] s_commandLineArgs; - - internal static void SetCommandLineArgs(string[] args) - { - s_commandLineArgs = args; - } - - public static string[] GetCommandLineArgs() - { - return (string[])s_commandLineArgs.Clone(); - } - } -} diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.Win32.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.Win32.cs deleted file mode 100644 index 330cb0264ee..00000000000 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.Win32.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; - -using Internal.Win32; - -namespace Internal.Runtime.Augments -{ - /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. - public static partial class EnvironmentAugments - { - private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) - { - Debug.Assert(variable != null); - using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine: fromMachine, writable: false)) - { - return environmentKey?.GetValue(variable) as string; - } - } - - private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) - { - Debug.Assert(variable != null); - - // User-wide environment variables stored in the registry are limited to 255 chars for the environment variable name. - const int MaxUserEnvVariableLength = 255; - if (variable.Length >= MaxUserEnvVariableLength) - throw new ArgumentException(SR.Argument_LongEnvVarValue, nameof(variable)); - - using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine: fromMachine, writable: true)) - { - if (environmentKey != null) - { - if (value == null) - { - environmentKey.DeleteValue(variable, throwOnMissingValue: false); - } - else - { - environmentKey.SetValue(variable, value); - } - } - } - - // send a WM_SETTINGCHANGE message to all windows - IntPtr r = Interop.User32.SendMessageTimeout(new IntPtr(Interop.User32.HWND_BROADCAST), Interop.User32.WM_SETTINGCHANGE, IntPtr.Zero, "Environment", 0, 1000, IntPtr.Zero); - if (r == IntPtr.Zero) - Debug.Fail("SetEnvironmentVariable failed: " + Marshal.GetLastWin32Error()); - } - - private static IEnumerable> EnumerateEnvironmentVariablesFromRegistry(bool fromMachine) - { - using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine: fromMachine, writable: false)) - { - if (environmentKey != null) - { - foreach (string name in environmentKey.GetValueNames()) - { - string value = environmentKey.GetValue(name, string.Empty).ToString(); - yield return new KeyValuePair(name, value); - } - } - } - } - - private static RegistryKey OpenEnvironmentKeyIfExists(bool fromMachine, bool writable) - { - RegistryKey baseKey; - string keyName; - - if (fromMachine) - { - baseKey = Registry.LocalMachine; - keyName = @"System\CurrentControlSet\Control\Session Manager\Environment"; - } - else - { - baseKey = Registry.CurrentUser; - keyName = "Environment"; - } - - return baseKey.OpenSubKey(keyName, writable: writable); - } - } -} diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.WithoutRegistry.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.WithoutRegistry.cs deleted file mode 100644 index 4867bda854c..00000000000 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.WithoutRegistry.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; - -namespace Internal.Runtime.Augments -{ - /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. - public static partial class EnvironmentAugments - { - private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) - { - Debug.Assert(variable != null); - return null; // Systems without registries pretend that the registry environment subkeys are empty lists. - } - - private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) - { - Debug.Assert(variable != null); - return; // Systems without registries pretend that the registry environment subkeys are empty lists that throw all write requests into a black hole. - } - - private static IEnumerable> EnumerateEnvironmentVariablesFromRegistry(bool fromMachine) - { - return Array.Empty>(); // Systems without registries pretend that the registry environment subkeys are empty lists. - } - } -} diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.ProjectN.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.ProjectN.cs deleted file mode 100644 index f49f9cf9249..00000000000 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.ProjectN.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime; - -namespace Internal.Runtime.Augments -{ - /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. - public static partial class EnvironmentAugments - { - public static void Exit(int exitCode) - { - // This needs to be implemented for ProjectN. - throw new PlatformNotSupportedException(); - } - - public static string[] GetCommandLineArgs() => CommandLine.InternalCreateCommandLine(includeArg0: true); - } -} diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs deleted file mode 100644 index adfe1745992..00000000000 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs +++ /dev/null @@ -1,161 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; - -using Win32Marshal = System.IO.Win32Marshal; - -namespace Internal.Runtime.Augments -{ - /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. - public static partial class EnvironmentAugments - { - private static string GetEnvironmentVariableCore(string variable) - { - Debug.Assert(variable != null); - - // The convention of the API is as follows: - // You call the API with a buffer of a given size. - // If the size of the buffer is insufficient to hold the value, - // the API will return the required size for the buffer. - // In that case we resize the buffer and try again. - - int currentSize = 128; - for (;;) - { - char[] buffer = ArrayPool.Shared.Rent(currentSize); - - int actualSize = Interop.mincore.GetEnvironmentVariable(variable, buffer, buffer.Length); - if (actualSize <= buffer.Length) - { - string result = (actualSize != 0) ? new string(buffer, 0, actualSize) : null; - ArrayPool.Shared.Return(buffer); - return result; - } - - ArrayPool.Shared.Return(buffer); - currentSize = actualSize; - } - } - - private static void SetEnvironmentVariableCore(string variable, string value) - { - Debug.Assert(variable != null); - - if (!Interop.Kernel32.SetEnvironmentVariable(variable, value)) - { - int errorCode = Marshal.GetLastWin32Error(); - switch (errorCode) - { - case Interop.Errors.ERROR_ENVVAR_NOT_FOUND: - // Allow user to try to clear a environment variable - return; - case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: - // The error message from Win32 is "The filename or extension is too long", - // which is not accurate. - throw new ArgumentException(SR.Argument_LongEnvVarValue); - case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: - case Interop.Errors.ERROR_NO_SYSTEM_RESOURCES: - throw new OutOfMemoryException(Interop.Kernel32.GetMessage(errorCode)); - default: - throw new ArgumentException(Interop.Kernel32.GetMessage(errorCode)); - } - } - } - - public static IEnumerable> EnumerateEnvironmentVariables() - { - unsafe - { - char* unsafeBlock = Interop.Kernel32.GetEnvironmentStrings(); - if (unsafeBlock == (char*)0) - throw new OutOfMemoryException(); - try - { - // Format for GetEnvironmentStrings is: - // [=HiddenVar=value\0]* [Variable=value\0]* \0 - // See the description of Environment Blocks in MSDN's - // CreateProcess page (null-terminated array of null-terminated strings). - - // Search for terminating \0\0 (two unicode \0's). - char* p = unsafeBlock; - while (!(*p == '\0' && *(p + 1) == '\0')) - { - p++; - } - - int len = checked((int)(p - unsafeBlock + 1)); - // TODO Perf: Change "block" from char[] to ReadOnlySpan once that becomes available in Project N. - char[] block = new char[len]; - for (int i = 0; i < len; i++) - { - block[i] = unsafeBlock[i]; - } - return EnumerateEnvironmentVariables(block); - } - finally - { - bool success = Interop.Kernel32.FreeEnvironmentStrings(unsafeBlock); - Debug.Assert(success); - } - } - } - - // Format for GetEnvironmentStrings is: - // (=HiddenVar=value\0 | Variable=value\0)* \0 - // See the description of Environment Blocks in MSDN's - // CreateProcess page (null-terminated array of null-terminated strings). - // Note the =HiddenVar's aren't always at the beginning. - - // Copy strings out, parsing into pairs. - // The first few environment variable entries start with an '='. - // The current working directory of every drive (except for those drives - // you haven't cd'ed into in your DOS window) are stored in the - // environment block (as =C:=pwd) and the program's exit code is - // as well (=ExitCode=00000000). - private static IEnumerable> EnumerateEnvironmentVariables(char[] block) - { - // To maintain complete compatibility with prior versions we need to return a Hashtable. - // We did ship a prior version of Core with LowLevelDictionary, which does iterate the - // same (e.g. yields DictionaryEntry), but it is not a public type. - // - // While we could pass Hashtable back from CoreCLR the type is also defined here. We only - // want to surface the local Hashtable. - for (int i = 0; i < block.Length; i++) - { - int startKey = i; - - // Skip to key. On some old OS, the environment block can be corrupted. - // Some will not have '=', so we need to check for '\0'. - while (block[i] != '=' && block[i] != '\0') - i++; - if (block[i] == '\0') - continue; - - // Skip over environment variables starting with '=' - if (i - startKey == 0) - { - while (block[i] != 0) - i++; - continue; - } - - string key = new string(block, startKey, i - startKey); - i++; // skip over '=' - - int startValue = i; - while (block[i] != 0) - i++; // Read to end of this entry - string value = new string(block, startValue, i - startValue); // skip over 0 handled by for loop's i++ - - yield return new KeyValuePair(key, value); - } - } - } -} diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs index b9380a56c24..f753b728ae5 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs @@ -3,153 +3,46 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; +using System.Collections; using System.Collections.Generic; -using System.Runtime; -using System.Runtime.CompilerServices; namespace Internal.Runtime.Augments { - /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. - public static partial class EnvironmentAugments - { - public static string GetEnvironmentVariable(string variable) - { - if (variable == null) - throw new ArgumentNullException(nameof(variable)); - return GetEnvironmentVariableCore(variable); - } + // TODO: Delete this file once corefx has consumed https://github.com/dotnet/coreclr/pull/22106 + // and its corresponding mirrored build from corert, and then the resulting corefx builds + // have been consumed back here, such that the CI tests which currently expect to find + // EnvironmentAugments have been updated to no longer need it. - public static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) - { - if (target == EnvironmentVariableTarget.Process) - return GetEnvironmentVariable(variable); - - if (variable == null) - throw new ArgumentNullException(nameof(variable)); - - bool fromMachine = ValidateAndConvertRegistryTarget(target); - return GetEnvironmentVariableFromRegistry(variable, fromMachine: fromMachine); - } - - public static void SetEnvironmentVariable(string variable, string value) - { - ValidateVariableAndValue(variable, ref value); - - SetEnvironmentVariableCore(variable, value); - } - - public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target) - { - if (target == EnvironmentVariableTarget.Process) - { - SetEnvironmentVariable(variable, value); - return; - } - - ValidateVariableAndValue(variable, ref value); - - bool fromMachine = ValidateAndConvertRegistryTarget(target); - SetEnvironmentVariableFromRegistry(variable, value, fromMachine: fromMachine); - } - - private static void ValidateVariableAndValue(string variable, ref string value) + public static class EnvironmentAugments + { + public static int CurrentManagedThreadId => Environment.CurrentManagedThreadId; + public static void Exit(int exitCode) => Environment.Exit(exitCode); + public static int ExitCode { get { return Environment.ExitCode; } set { Environment.ExitCode = value; } } + public static void FailFast(string message, Exception error) => Environment.FailFast(message, error); + public static string[] GetCommandLineArgs() => Environment.GetCommandLineArgs(); + public static bool HasShutdownStarted => Environment.HasShutdownStarted; + public static int TickCount => Environment.TickCount; + public static string GetEnvironmentVariable(string variable) => Environment.GetEnvironmentVariable(variable); + public static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) => Environment.GetEnvironmentVariable(variable, target); + public static IEnumerable> EnumerateEnvironmentVariables() { - if (variable == null) - throw new ArgumentNullException(nameof(variable)); - - if (variable.Length == 0) - throw new ArgumentException(SR.Argument_StringZeroLength, nameof(variable)); - - if (variable[0] == '\0') - throw new ArgumentException(SR.Argument_StringFirstCharIsZero, nameof(variable)); - - if (variable.IndexOf('=') != -1) - throw new ArgumentException(SR.Argument_IllegalEnvVarName, nameof(variable)); - - if (string.IsNullOrEmpty(value) || value[0] == '\0') + IDictionaryEnumerator de = Environment.GetEnvironmentVariables().GetEnumerator(); + while (de.MoveNext()) { - // Explicitly null out value if it's empty - value = null; + yield return new KeyValuePair((string)de.Key, (string)de.Value); } } - public static IEnumerable> EnumerateEnvironmentVariables(EnvironmentVariableTarget target) { - if (target == EnvironmentVariableTarget.Process) - return EnumerateEnvironmentVariables(); - - bool fromMachine = ValidateAndConvertRegistryTarget(target); - return EnumerateEnvironmentVariablesFromRegistry(fromMachine: fromMachine); - } - - private static bool ValidateAndConvertRegistryTarget(EnvironmentVariableTarget target) - { - Debug.Assert(target != EnvironmentVariableTarget.Process); - if (target == EnvironmentVariableTarget.Machine) - return true; - else if (target == EnvironmentVariableTarget.User) - return false; - else - throw new ArgumentOutOfRangeException(nameof(target), target, SR.Format(SR.Arg_EnumIllegalVal, target)); - } - - public static int CurrentManagedThreadId => System.Threading.ManagedThreadId.Current; - public static void FailFast(string message, Exception error) => RuntimeExceptionHelpers.FailFast(message, error); - - internal static void ShutdownCore() - { - // TODO: shut down threading etc. - -#if !WASM // WASMTODO - AppContext.OnProcessExit(); -#endif - } - - private static int s_latchedExitCode; - public static int ExitCode - { - get + IDictionaryEnumerator de = Environment.GetEnvironmentVariables(target).GetEnumerator(); + while (de.MoveNext()) { - return s_latchedExitCode; - } - set - { - s_latchedExitCode = value; - } - } - - public static bool HasShutdownStarted => false; // .NET Core does not have shutdown finalization - - public static string StackTrace - { - // Disable inlining to have predictable stack frame to skip - [MethodImpl(MethodImplOptions.NoInlining)] - get - { - // RhGetCurrentThreadStackTrace returns the number of frames(cFrames) added to input buffer. - // It returns a negative value, -cFrames which is the required array size, if the buffer is too small. - // Initial array length is deliberately chosen to be 0 so that we reallocate to exactly the right size - // for StackFrameHelper.FormatStackTrace call. If we want to do this optimistically with one call change - // FormatStackTrace to accept an explicit length. - IntPtr[] frameIPs = Array.Empty(); - int cFrames = RuntimeImports.RhGetCurrentThreadStackTrace(frameIPs); - if (cFrames < 0) - { - frameIPs = new IntPtr[-cFrames]; - cFrames = RuntimeImports.RhGetCurrentThreadStackTrace(frameIPs); - if (cFrames < 0) - { - return ""; - } - } - - return Internal.Diagnostics.StackTraceHelper.FormatStackTrace(frameIPs, 1, true); + yield return new KeyValuePair((string)de.Key, (string)de.Value); } } - - public static int TickCount => Environment.TickCount; - public static int ProcessorCount => Environment.ProcessorCount; + public static void SetEnvironmentVariable(string variable, string value) => Environment.SetEnvironmentVariable(variable, value); + public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target) => Environment.SetEnvironmentVariable(variable, value, target); + public static string StackTrace => Environment.StackTrace; // this will temporarily result in an extra frame in Environment.StackTrace calls } -} +} \ No newline at end of file diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs index 1369617b0fc..80c1c8851f8 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs @@ -68,6 +68,7 @@ public abstract class WinRTInteropCallbacks public abstract Object GetUserDefaultCulture(); public abstract void SetGlobalDefaultCulture(Object culture); public abstract Object GetCurrentWinRTDispatcher(); + public abstract string GetFolderPath(Environment.SpecialFolder specialFolder, Environment.SpecialFolderOption specialFolderOption); public abstract void PostToWinRTDispatcher(Object dispatcher, Action action, object state); public abstract bool IsAppxModel(); public abstract bool ReportUnhandledError(Exception ex); diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Extensions.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Extensions.cs index b1d39b11a74..9adce917dbb 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Extensions.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Extensions.cs @@ -19,7 +19,7 @@ internal static unsafe void InitializeCommandLineArgsW(int argc, char** argv) { args[i] = new string(argv[i]); } - EnvironmentAugments.SetCommandLineArgs(args); + Environment.SetCommandLineArgs(args); } internal static unsafe void InitializeCommandLineArgs(int argc, sbyte** argv) @@ -29,13 +29,13 @@ internal static unsafe void InitializeCommandLineArgs(int argc, sbyte** argv) { args[i] = new string(argv[i]); } - EnvironmentAugments.SetCommandLineArgs(args); + Environment.SetCommandLineArgs(args); } private static string[] GetMainMethodArguments() { // GetCommandLineArgs includes the executable name, Main() arguments do not. - string[] args = EnvironmentAugments.GetCommandLineArgs(); + string[] args = Environment.GetCommandLineArgs(); Debug.Assert(args.Length > 0); @@ -47,15 +47,15 @@ private static string[] GetMainMethodArguments() private static void SetLatchedExitCode(int exitCode) { - EnvironmentAugments.ExitCode = exitCode; + Environment.ExitCode = exitCode; } // Shuts down the class library and returns the process exit code. private static int Shutdown() { - EnvironmentAugments.ShutdownCore(); + Environment.ShutdownCore(); - return EnvironmentAugments.ExitCode; + return Environment.ExitCode; } } } diff --git a/src/System.Private.CoreLib/src/Resources/Strings.resx b/src/System.Private.CoreLib/src/Resources/Strings.resx index 1c7db80f569..32ae909a511 100644 --- a/src/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/System.Private.CoreLib/src/Resources/Strings.resx @@ -312,6 +312,9 @@ Object must be of type Double. + + Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C'). + Type provided must be an Enum. @@ -699,6 +702,9 @@ Object contains non-primitive or non-blittable data. + + Path cannot be the empty string or all whitespace. + Array size exceeds addressing limitations. @@ -1149,6 +1155,9 @@ Enumeration has either not started or has already finished. + + OSVersion's call to GetVersionEx failed. + Handle is not initialized. @@ -1509,6 +1518,9 @@ Postcondition failed after throwing an exception: {0} + + The home directory of the current user could not be determined. + Invariant failed. @@ -2797,6 +2809,9 @@ The target method returned a null reference. + + Computer name could not be obtained. + Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct. diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 64ac952c752..6ad03175d35 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -127,13 +127,6 @@ - - - - - - - @@ -219,7 +212,8 @@ - + + @@ -327,8 +321,8 @@ - - + + Interop\Windows\kernel32\Interop.FileTimeToSystemTime.cs @@ -397,8 +391,9 @@ Interop\Windows\mincore\Interop.CommandLine.cs - - + + + Interop\Windows\mincore\Interop.GetTickCount64.cs @@ -468,7 +463,7 @@ - + diff --git a/src/System.Private.CoreLib/src/System/Environment.CoreRT.NonProjectN.cs b/src/System.Private.CoreLib/src/System/Environment.CoreRT.NonProjectN.cs new file mode 100644 index 00000000000..5d110e7031b --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.CoreRT.NonProjectN.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime; + +namespace System +{ + public static partial class Environment + { + public static void Exit(int exitCode) + { + s_latchedExitCode = exitCode; + ShutdownCore(); + RuntimeImports.RhpShutdown(); + ExitRaw(); + } + + public static string[] GetCommandLineArgs() => (string[])s_commandLineArgs.Clone(); + } +} diff --git a/src/System.Private.CoreLib/src/System/Environment.CoreRT.ProjectN.cs b/src/System.Private.CoreLib/src/System/Environment.CoreRT.ProjectN.cs new file mode 100644 index 00000000000..68f9f3c58e7 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.CoreRT.ProjectN.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime; + +namespace System +{ + public static partial class Environment + { + public static void Exit(int exitCode) => + throw new PlatformNotSupportedException(); // This needs to be implemented for ProjectN. + + public static string[] GetCommandLineArgs() => + CommandLine.InternalCreateCommandLine(includeArg0: true); + } +} diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs b/src/System.Private.CoreLib/src/System/Environment.CoreRT.Unix.cs similarity index 71% rename from src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs rename to src/System.Private.CoreLib/src/System/Environment.CoreRT.Unix.cs index 76ead8cb797..33a5c287e69 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs +++ b/src/System.Private.CoreLib/src/System/Environment.CoreRT.Unix.cs @@ -2,17 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections; -using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Text; +using System.Threading; -namespace Internal.Runtime.Augments +namespace System { - /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. - public static partial class EnvironmentAugments + public static partial class Environment { + internal static int CurrentNativeThreadId => ManagedThreadId.Current; + private static string GetEnvironmentVariableCore(string variable) { Debug.Assert(variable != null); @@ -25,23 +26,27 @@ private static void SetEnvironmentVariableCore(string variable, string value) throw new NotImplementedException(); } - public static IEnumerable> EnumerateEnvironmentVariables() + public static IDictionary GetEnvironmentVariables() { - IntPtr block = Interop.Sys.GetEnviron(); - if (block == IntPtr.Zero) - yield break; + var results = new Hashtable(); - // Per man page, environment variables come back as an array of pointers to strings - // Parse each pointer of strings individually - while (ParseEntry(block, out string key, out string value)) + IntPtr block = Interop.Sys.GetEnviron(); + if (block != IntPtr.Zero) { - if (key != null && value != null) - yield return new KeyValuePair(key, value); + // Per man page, environment variables come back as an array of pointers to strings + // Parse each pointer of strings individually + while (ParseEntry(block, out string key, out string value)) + { + if (key != null && value != null) + results.Add(key, value); - // Increment to next environment variable entry - block += IntPtr.Size; + // Increment to next environment variable entry + block += IntPtr.Size; + } } + return results; + // Use a local, unsafe function since we cannot use `yield return` inside of an `unsafe` block unsafe bool ParseEntry(IntPtr current, out string key, out string value) { @@ -78,9 +83,8 @@ unsafe bool ParseEntry(IntPtr current, out string key, out string value) } } - private static void ExitRaw() - { - Interop.Sys.Exit(s_latchedExitCode); - } + private static void ExitRaw() => Interop.Sys.Exit(s_latchedExitCode); + + internal static long TickCount64 => (long)Interop.Sys.GetTickCount64(); } } diff --git a/src/System.Private.CoreLib/src/System/Environment.CoreRT.Win32.cs b/src/System.Private.CoreLib/src/System/Environment.CoreRT.Win32.cs new file mode 100644 index 00000000000..f1e07879721 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.CoreRT.Win32.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace System +{ + public static partial class Environment + { + private static void ExitRaw() => Interop.Kernel32.ExitProcess(s_latchedExitCode); + } +} diff --git a/src/System.Private.CoreLib/src/System/Environment.CoreRT.Windows.cs b/src/System.Private.CoreLib/src/System/Environment.CoreRT.Windows.cs new file mode 100644 index 00000000000..6ed6c0eb2e0 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.CoreRT.Windows.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System +{ + public static partial class Environment + { + internal static int CurrentNativeThreadId => unchecked((int)Interop.mincore.GetCurrentThreadId()); + + internal static long TickCount64 => (long)Interop.mincore.GetTickCount64(); + } +} diff --git a/src/System.Private.CoreLib/src/System/Environment.CoreRT.cs b/src/System.Private.CoreLib/src/System/Environment.CoreRT.cs new file mode 100644 index 00000000000..f0e3c2e1784 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.CoreRT.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Threading; +using Internal.DeveloperExperience; +using System.Runtime; + +namespace System +{ + public static partial class Environment + { + public static int CurrentManagedThreadId => ManagedThreadId.Current; + + private static int s_latchedExitCode; + + public static int ExitCode + { + get => s_latchedExitCode; + set => s_latchedExitCode = value; + } + + // Note: The CLR's Watson bucketization code looks at the caller of the FCALL method + // to assign blame for crashes. Don't mess with this, such as by making it call + // another managed helper method, unless you consult with some CLR Watson experts. + public static void FailFast(string message) => + RuntimeExceptionHelpers.FailFast(message); + + public static void FailFast(string message, Exception exception) => + RuntimeExceptionHelpers.FailFast(message, exception); + + internal static void FailFast(string message, Exception exception, string errorSource) + { + // TODO: errorSource originates from CoreCLR (See: https://github.com/dotnet/coreclr/pull/15895) + // For now, we ignore errorSource on CoreRT but we should distinguish the way FailFast prints exception message using errorSource + bool result = DeveloperExperience.Default.OnContractFailure(exception.StackTrace, ContractFailureKind.Assert, message, null, null, null); + if (!result) + { + RuntimeExceptionHelpers.FailFast(message, exception); + } + } + + public static bool HasShutdownStarted => false; // .NET Core does not have shutdown finalization + + public static int ProcessorCount => Runtime.RuntimeImports.RhGetProcessCpuCount(); + + internal static void ShutdownCore() + { + // TODO: shut down threading etc. + +#if !WASM // WASMTODO + AppContext.OnProcessExit(); +#endif + } + + public static string StackTrace + { + // Disable inlining to have predictable stack frame to skip + [MethodImpl(MethodImplOptions.NoInlining)] + get + { + // RhGetCurrentThreadStackTrace returns the number of frames(cFrames) added to input buffer. + // It returns a negative value, -cFrames which is the required array size, if the buffer is too small. + // Initial array length is deliberately chosen to be 0 so that we reallocate to exactly the right size + // for StackFrameHelper.FormatStackTrace call. If we want to do this optimistically with one call change + // FormatStackTrace to accept an explicit length. + IntPtr[] frameIPs = Array.Empty(); + int cFrames = RuntimeImports.RhGetCurrentThreadStackTrace(frameIPs); + if (cFrames < 0) + { + frameIPs = new IntPtr[-cFrames]; + cFrames = RuntimeImports.RhGetCurrentThreadStackTrace(frameIPs); + if (cFrames < 0) + { + return ""; + } + } + + return Internal.Diagnostics.StackTraceHelper.FormatStackTrace(frameIPs, 0, true); + } + } + + public static int TickCount => (int)TickCount64; + } +} diff --git a/src/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/System.Private.CoreLib/src/System/Environment.Unix.cs deleted file mode 100644 index af2e5517a16..00000000000 --- a/src/System.Private.CoreLib/src/System/Environment.Unix.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text; -using System.Runtime.InteropServices; -using System.Threading; - -namespace System -{ - internal static partial class Environment - { - internal static int CurrentNativeThreadId => ManagedThreadId.Current; - - internal static long TickCount64 - { - get - { - return (long)Interop.Sys.GetTickCount64(); - } - } - -#if DEBUG - [Obsolete("ExpandEnvironmentVariables() only called on Windows so not implemented on Unix.")] - public static string ExpandEnvironmentVariables(string name) - { - throw new PlatformNotSupportedException("ExpandEnvironmentVariables() only called on Windows so not implemented on Unix."); - } -#endif - } -} diff --git a/src/System.Private.CoreLib/src/System/Environment.Win32.cs b/src/System.Private.CoreLib/src/System/Environment.Win32.cs deleted file mode 100644 index 4d6dd60db93..00000000000 --- a/src/System.Private.CoreLib/src/System/Environment.Win32.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace System -{ - internal static partial class Environment - { - internal static string SystemDirectory - { - get - { - StringBuilder sb = new StringBuilder(Interop.Kernel32.MAX_PATH); - int r = Interop.mincore.GetSystemDirectory(sb, Interop.Kernel32.MAX_PATH); - return sb.ToString(); - } - } - } -} diff --git a/src/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/System.Private.CoreLib/src/System/Environment.Windows.cs deleted file mode 100644 index 71c9e06a477..00000000000 --- a/src/System.Private.CoreLib/src/System/Environment.Windows.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; - -namespace System -{ - internal static partial class Environment - { - internal static int CurrentNativeThreadId => unchecked((int)Interop.mincore.GetCurrentThreadId()); - - internal static long TickCount64 => (long)Interop.mincore.GetTickCount64(); - - public static string ExpandEnvironmentVariables(string name) - { - if (name == null) - throw new ArgumentNullException(nameof(name)); - - int currentSize = 128; - for (;;) - { - char[] buffer = ArrayPool.Shared.Rent(currentSize); - - int actualSize = Interop.Kernel32.ExpandEnvironmentStrings(name, buffer, buffer.Length); - if (actualSize <= buffer.Length) - { - string result = (actualSize != 0) ? new string(buffer, 0, actualSize) : null; - ArrayPool.Shared.Return(buffer); - return result; - } - - ArrayPool.Shared.Return(buffer); - currentSize = actualSize; - } - } - } -} diff --git a/src/System.Private.CoreLib/src/System/Environment.cs b/src/System.Private.CoreLib/src/System/Environment.cs deleted file mode 100644 index 3a3780dacc8..00000000000 --- a/src/System.Private.CoreLib/src/System/Environment.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** Purpose: Provides some basic access to some environment -** functionality. -** -** -============================================================*/ - -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Threading; - -using Internal.Runtime.Augments; -using Internal.DeveloperExperience; - -namespace System -{ - internal static partial class Environment - { - /*==================================TickCount=================================== - **Action: Gets the number of ticks since the system was started. - **Returns: The number of ticks since the system was started. - **Arguments: None - **Exceptions: None - ==============================================================================*/ - public static int TickCount - { - get - { - return (int)TickCount64; - } - } - - //// Note: The CLR's Watson bucketization code looks at the caller of the FCALL method - //// to assign blame for crashes. Don't mess with this, such as by making it call - //// another managed helper method, unless you consult with some CLR Watson experts. - - - public static void FailFast(string message) - { - RuntimeExceptionHelpers.FailFast(message); - } - - public static void FailFast(string message, Exception exception) - { - RuntimeExceptionHelpers.FailFast(message, exception); - } - - internal static void FailFast(string message, Exception exception, string errorSource) - { - // TODO: errorSource originates from CoreCLR (See: https://github.com/dotnet/coreclr/pull/15895) - // For now, we ignore errorSource on CoreRT but we should distinguish the way FailFast prints exception message using errorSource - bool result = DeveloperExperience.Default.OnContractFailure(exception.StackTrace, ContractFailureKind.Assert, message, null, null, null); - if (!result) - { - RuntimeExceptionHelpers.FailFast(message, exception); - } - } - - // Still needed by shared\System\Diagnostics\DebugProvider.Unix.cs - public static string GetEnvironmentVariable(string variable) => EnvironmentAugments.GetEnvironmentVariable(variable); - - public static int CurrentManagedThreadId - { - get - { - return ManagedThreadId.Current; - } - } - - public static bool HasShutdownStarted - { - get - { - // .NET Core does not have shutdown finalization - return false; - } - } - - /*===================================NewLine==================================== - **Action: A property which returns the appropriate newline string for the given - ** platform. - **Returns: \r\n on Win32. - **Arguments: None. - **Exceptions: None. - ==============================================================================*/ - public static string NewLine - { - get - { -#if !PLATFORM_UNIX - return "\r\n"; -#else - return "\n"; -#endif // !PLATFORM_UNIX - } - } - - public static string StackTrace - { - // Disable inlining to have predictable stack frame that EnvironmentAugments can skip - [MethodImpl(MethodImplOptions.NoInlining)] - get - { - return EnvironmentAugments.StackTrace; - } - } - - public static int ProcessorCount - { - get - { - return Runtime.RuntimeImports.RhGetProcessCpuCount(); - } - } - } -} diff --git a/src/System.Private.Interop/src/System.Private.Interop.csproj b/src/System.Private.Interop/src/System.Private.Interop.csproj index 249bae8654a..d50eed857db 100644 --- a/src/System.Private.Interop/src/System.Private.Interop.csproj +++ b/src/System.Private.Interop/src/System.Private.Interop.csproj @@ -13,7 +13,6 @@ - diff --git a/src/System.Private.Jit/src/System.Private.Jit.csproj b/src/System.Private.Jit/src/System.Private.Jit.csproj index ce72e634804..f35c06c8cdc 100644 --- a/src/System.Private.Jit/src/System.Private.Jit.csproj +++ b/src/System.Private.Jit/src/System.Private.Jit.csproj @@ -24,7 +24,6 @@ true - global,System_Private_CoreLib diff --git a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index e5ab82cad10..292926350a3 100644 --- a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -42,7 +42,6 @@ - From 46411b52c1f16dfbac5a6799fedc3ee15252ac5f Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 31 Jan 2019 09:35:33 -0500 Subject: [PATCH 3/3] Replace Environment.s_macOS with use of PLATFORM_OSX --- .../shared/System/Environment.Unix.cs | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Environment.Unix.cs b/src/System.Private.CoreLib/shared/System/Environment.Unix.cs index a69aa63a722..3d54dfa8844 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.Unix.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.Unix.cs @@ -16,7 +16,6 @@ namespace System { public static partial class Environment { - private static readonly bool s_isMac = Interop.Sys.GetUnixName() == "OSX"; private static Func s_directoryCreateDirectory; private static string CurrentDirectoryCore @@ -98,14 +97,10 @@ private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder) { case SpecialFolder.CommonApplicationData: return "/usr/share"; case SpecialFolder.CommonTemplates: return "/usr/share/templates"; - } - if (s_isMac) - { - switch (folder) - { - case SpecialFolder.ProgramFiles: return "/Applications"; - case SpecialFolder.System: return "/System"; - } +#if PLATFORM_OSX + case SpecialFolder.ProgramFiles: return "/Applications"; + case SpecialFolder.System: return "/System"; +#endif } // All other paths are based on the XDG Base Directory Specification: @@ -158,19 +153,25 @@ private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder) case SpecialFolder.MyVideos: return ReadXdgDirectory(home, "XDG_VIDEOS_DIR", "Videos"); +#if PLATFORM_OSX case SpecialFolder.MyMusic: - return s_isMac ? Path.Combine(home, "Music") : ReadXdgDirectory(home, "XDG_MUSIC_DIR", "Music"); + return Path.Combine(home, "Music"); case SpecialFolder.MyPictures: - return s_isMac ? Path.Combine(home, "Pictures") : ReadXdgDirectory(home, "XDG_PICTURES_DIR", "Pictures"); + return Path.Combine(home, "Pictures"); case SpecialFolder.Fonts: - return s_isMac ? Path.Combine(home, "Library", "Fonts") : Path.Combine(home, ".fonts"); - + return Path.Combine(home, "Library", "Fonts"); case SpecialFolder.Favorites: - if (s_isMac) return Path.Combine(home, "Library", "Favorites"); - break; + return Path.Combine(home, "Library", "Favorites"); case SpecialFolder.InternetCache: - if (s_isMac) return Path.Combine(home, "Library", "Caches"); - break; + return Path.Combine(home, "Library", "Caches"); +#else + case SpecialFolder.MyMusic: + return ReadXdgDirectory(home, "XDG_MUSIC_DIR", "Music"); + case SpecialFolder.MyPictures: + return ReadXdgDirectory(home, "XDG_PICTURES_DIR", "Pictures"); + case SpecialFolder.Fonts: + return Path.Combine(home, ".fonts"); +#endif } // No known path for the SpecialFolder