Switch CoreLib over to using shared Environment from corefx (dotnet/coreclr#22106)
authorStephen Toub <stoub@microsoft.com>
Wed, 30 Jan 2019 21:22:27 +0000 (16:22 -0500)
committerGitHub <noreply@github.com>
Wed, 30 Jan 2019 21:22:27 +0000 (16:22 -0500)
This requires merging/adapting the implementation with EnvironmentAugments (which goes away completely), the shared files, what corert has, etc.

Commit migrated from https://github.com/dotnet/coreclr/commit/91e1ffccc38fc87f6f496eb056396a1b775f08af

18 files changed:
src/coreclr/src/System.Private.CoreLib/Resources/Strings.resx
src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/coreclr/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs
src/coreclr/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs
src/coreclr/src/System.Private.CoreLib/src/System/CLRConfig.cs
src/coreclr/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs [new file with mode: 0644]
src/coreclr/src/System.Private.CoreLib/src/System/Environment.Windows.cs [deleted file]
src/coreclr/src/System.Private.CoreLib/src/System/Environment.cs [deleted file]
src/coreclr/src/System.Private.CoreLib/src/System/Exception.cs
src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs
src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Environment.NoRegistry.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs
src/libraries/System.Private.CoreLib/src/System/Environment.Variables.Windows.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs
src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs
src/libraries/System.Private.CoreLib/src/System/Environment.cs

index 98a3d9e..6800d8e 100644 (file)
   <data name="Arg_MustBeDouble" xml:space="preserve">
     <value>Object must be of type Double.</value>
   </data>
+  <data name="Arg_MustBeDriveLetterOrRootDir" xml:space="preserve">
+    <value>Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C').</value>
+  </data>
   <data name="Arg_MustBeEnum" xml:space="preserve">
     <value>Type provided must be an Enum.</value>
   </data>
   <data name="InvalidOperation_CollectionCorrupted" xml:space="preserve">
     <value>A prior operation on this collection was interrupted by an exception. Collection's state is no longer trusted.</value>
   </data>
+  <data name="InvalidOperation_ComputerName" xml:space="preserve">
+    <value>Computer name could not be obtained.</value>
+  </data>
   <data name="InvalidOperation_ConcurrentOperationsNotSupported" xml:space="preserve">
     <value>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.</value>
   </data>
   <data name="PreconditionFailed_Cnd" xml:space="preserve">
     <value>Precondition failed: {0}</value>
   </data>
+  <data name="PersistedFiles_NoHomeDirectory" xml:space="preserve">
+    <value>The home directory of the current user could not be determined.</value>
+  </data>
   <data name="Rank_MultiDimNotSupported" xml:space="preserve">
     <value>Only single dimension arrays are supported here.</value>
   </data>
index db83064..d8bde72 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\SymAddressKind.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\Token.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Enum.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Environment.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\Environment.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Exception.cs" />
     <Compile Include="$(BclSourcesRoot)\System\GC.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.cs" />
     <Compile Include="$(BclSourcesRoot)\System\ApplicationModel.Windows.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.Windows.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolBoundHandle.Windows.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Environment.Windows.cs" />
   </ItemGroup>
   <ItemGroup>
     <!--
   </ItemGroup>
   <Import Project="ILLink.targets" />
   <Import Project="GenerateCompilerResponseFile.targets" />
-</Project>
\ No newline at end of file
+</Project>
index 8bed354..a501f87 100644 (file)
@@ -3,13 +3,16 @@
 // 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.CompilerServices;
 
 namespace Internal.Runtime.Augments
 {
-    /// <summary>For internal use only.  Exposes runtime functionality to the Environments implementation in corefx.</summary>
+    // 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 class EnvironmentAugments
     {
         public static int CurrentManagedThreadId => Environment.CurrentManagedThreadId;
@@ -21,20 +24,25 @@ namespace Internal.Runtime.Augments
         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<KeyValuePair<string, string>> EnumerateEnvironmentVariables() => Environment.EnumerateEnvironmentVariables();
-        public static IEnumerable<KeyValuePair<string, string>> EnumerateEnvironmentVariables(EnvironmentVariableTarget target) => Environment.EnumerateEnvironmentVariables(target);
-        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
+        public static IEnumerable<KeyValuePair<string, string>> EnumerateEnvironmentVariables()
         {
-            [MethodImpl(MethodImplOptions.NoInlining)] // Prevent inlining from affecting where the stacktrace starts
-            get
+            IDictionaryEnumerator de = Environment.GetEnvironmentVariables().GetEnumerator();
+            while (de.MoveNext())
             {
-                return new StackTrace(1 /* skip this one frame */, true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
+                yield return new KeyValuePair<string, string>((string)de.Key, (string)de.Value);
             }
         }
+        public static IEnumerable<KeyValuePair<string, string>> EnumerateEnvironmentVariables(EnvironmentVariableTarget target)
+        {
+            IDictionaryEnumerator de = Environment.GetEnvironmentVariables(target).GetEnumerator();
+            while (de.MoveNext())
+            {
+                yield return new KeyValuePair<string, string>((string)de.Key, (string)de.Value);
+            }
+        }
+        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
     }
 }
index 4374c73..9d2cc9a 100644 (file)
@@ -154,26 +154,6 @@ namespace Microsoft.Win32
         [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
         internal static extern IntPtr GetStdHandle(int nStdHandle);  // param is NOT a handle, but it returns one!
 
-        [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
-        internal static extern bool SetEnvironmentVariable(string lpName, string lpValue);
-
-        [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
-        private static extern unsafe int GetEnvironmentVariable(string lpName, char* lpValue, int size);
-
-        internal static unsafe int GetEnvironmentVariable(string lpName, Span<char> lpValue)
-        {
-            fixed (char* lpValuePtr = &MemoryMarshal.GetReference(lpValue))
-            {
-                return GetEnvironmentVariable(lpName, lpValuePtr, lpValue.Length);
-            }
-        }
-
-        [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode)]
-        internal static extern unsafe char* GetEnvironmentStrings();
-
-        [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode)]
-        internal static extern unsafe bool FreeEnvironmentStrings(char* pStrings);
-
         [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto)]
         internal static extern int GetCurrentThreadId();
 
@@ -189,9 +169,6 @@ namespace Microsoft.Win32
         [DllImport(Interop.Libraries.Ole32)]
         internal static extern IntPtr CoTaskMemRealloc(IntPtr pv, UIntPtr cb);
 
-        [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
-        internal static extern uint ExpandEnvironmentStringsW(string lpSrc, ref char lpDst, uint nSize);
-
         [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
         [return: MarshalAs(UnmanagedType.Bool)]
         internal static extern bool QueryUnbiasedInterruptTime(out ulong UnbiasedTime);
index 1885c57..5b14f28 100644 (file)
@@ -30,8 +30,7 @@ namespace System
             // abstractions where reasonably possible.
 
             Span<char> buffer = stackalloc char[32];
-
-            int length = Win32Native.GetEnvironmentVariable(environmentName, buffer);
+            int length = Interop.Kernel32.GetEnvironmentVariable(environmentName, buffer);
             switch (length)
             {
                 case 1:
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs
new file mode 100644 (file)
index 0000000..8d92a7e
--- /dev/null
@@ -0,0 +1,158 @@
+// 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.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.Win32;
+
+namespace System
+{
+    public static partial class Environment
+    {
+        public static int CurrentManagedThreadId => Thread.CurrentThread.ManagedThreadId;
+
+        // Terminates this process with the given exit code.
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        private static extern void _Exit(int exitCode);
+
+        public static void Exit(int exitCode) => _Exit(exitCode);
+
+        public static extern int ExitCode
+        {
+            [MethodImpl(MethodImplOptions.InternalCall)]
+            get;
+            [MethodImpl(MethodImplOptions.InternalCall)]
+            set;
+        }
+
+        // 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.
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern void FailFast(string message);
+
+        // This overload of FailFast will allow you to specify the exception object
+        // whose bucket details *could* be used when undergoing the failfast process.
+        // To be specific:
+        //
+        // 1) When invoked from within a managed EH clause (fault/finally/catch),
+        //    if the exception object is preallocated, the runtime will try to find its buckets
+        //    and use them. If the exception object is not preallocated, it will use the bucket
+        //    details contained in the object (if any).
+        //
+        // 2) When invoked from outside the managed EH clauses (fault/finally/catch),
+        //    if the exception object is preallocated, the runtime will use the callsite's
+        //    IP for bucketing. If the exception object is not preallocated, it will use the bucket
+        //    details contained in the object (if any).
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern void FailFast(string message, Exception exception);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern void FailFast(string message, Exception exception, string errorMessage);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string[] GetCommandLineArgsNative();
+
+        public static string[] GetCommandLineArgs()
+        {
+            // There are multiple entry points to a hosted app. The host could
+            // use ::ExecuteAssembly() or ::CreateDelegate option:
+            //
+            // ::ExecuteAssembly() -> In this particular case, the runtime invokes the main 
+            // method based on the arguments set by the host, and we return those arguments
+            //
+            // ::CreateDelegate() -> In this particular case, the host is asked to create a 
+            // delegate based on the appDomain, assembly and methodDesc passed to it.
+            // which the caller uses to invoke the method. In this particular case we do not have
+            // any information on what arguments would be passed to the delegate.
+            // So our best bet is to simply use the commandLine that was used to invoke the process.
+            // in case it is present.
+
+            return s_commandLineArgs != null ?
+                (string[])s_commandLineArgs.Clone() :
+                GetCommandLineArgsNative();
+        }
+
+        public static extern bool HasShutdownStarted
+        {
+            [MethodImpl(MethodImplOptions.InternalCall)]
+            get;
+        }
+
+        public static int ProcessorCount => GetProcessorCount();
+
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        private static extern int GetProcessorCount();
+
+        // If you change this method's signature then you must change the code that calls it
+        // in excep.cpp and probably you will have to visit mscorlib.h to add the new signature
+        // as well as metasig.h to create the new signature type
+        internal static string GetResourceStringLocal(string key) => SR.GetResourceString(key);
+
+        public static string StackTrace
+        {
+            [MethodImpl(MethodImplOptions.NoInlining)] // Prevent inlining from affecting where the stacktrace starts
+            get => new StackTrace(true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
+        }
+
+        public static extern int TickCount
+        {
+            [MethodImpl(MethodImplOptions.InternalCall)]
+            get;
+        }
+
+#if !FEATURE_PAL
+        internal static bool IsWindows8OrAbove => WindowsVersion.IsWindows8OrAbove;
+
+        // Seperate type so a .cctor is not created for Enviroment which then would be triggered during startup
+        private static class WindowsVersion
+        {
+            // Cache the value in readonly static that can be optimized out by the JIT
+            internal readonly static bool IsWindows8OrAbove = GetIsWindows8OrAbove();
+
+            private static bool GetIsWindows8OrAbove()
+            {
+                ulong conditionMask = Win32Native.VerSetConditionMask(0, Win32Native.VER_MAJORVERSION, Win32Native.VER_GREATER_EQUAL);
+                conditionMask = Win32Native.VerSetConditionMask(conditionMask, Win32Native.VER_MINORVERSION, Win32Native.VER_GREATER_EQUAL);
+                conditionMask = Win32Native.VerSetConditionMask(conditionMask, Win32Native.VER_SERVICEPACKMAJOR, Win32Native.VER_GREATER_EQUAL);
+                conditionMask = Win32Native.VerSetConditionMask(conditionMask, Win32Native.VER_SERVICEPACKMINOR, Win32Native.VER_GREATER_EQUAL);
+
+                // Windows 8 version is 6.2
+                var version = new Win32Native.OSVERSIONINFOEX();
+                unsafe
+                {
+                    version.dwOSVersionInfoSize = sizeof(Win32Native.OSVERSIONINFOEX);
+                }
+                version.dwMajorVersion = 6;
+                version.dwMinorVersion = 2;
+                version.wServicePackMajor = 0;
+                version.wServicePackMinor = 0;
+
+                return Win32Native.VerifyVersionInfoW(ref version,
+                    Win32Native.VER_MAJORVERSION | Win32Native.VER_MINORVERSION | Win32Native.VER_SERVICEPACKMAJOR | Win32Native.VER_SERVICEPACKMINOR,
+                    conditionMask);
+            }
+        }
+#endif
+
+#if FEATURE_COMINTEROP
+        // Seperate type so a .cctor is not created for Enviroment which then would be triggered during startup
+        private static class WinRT
+        {
+            // Cache the value in readonly static that can be optimized out by the JIT
+            public readonly static bool IsSupported = WinRTSupported();
+        }
+
+        // Does the current version of Windows have Windows Runtime suppport?
+        internal static bool IsWinRTSupported => WinRT.IsSupported;
+
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        private static extern bool WinRTSupported();
+#endif // FEATURE_COMINTEROP
+    }
+}
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Environment.Windows.cs
deleted file mode 100644 (file)
index 5bf7bea..0000000
+++ /dev/null
@@ -1,34 +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.IO;
-
-namespace System
-{
-    internal static partial class Environment
-    {
-        internal static string SystemDirectory
-        {
-            get
-            {
-                // The path will likely be under 32 characters, e.g. C:\Windows\system32
-                Span<char> buffer = stackalloc char[32];
-                int requiredSize = Interop.Kernel32.GetSystemDirectoryW(buffer);
-
-                if (requiredSize > buffer.Length)
-                {
-                    buffer = new char[requiredSize];
-                    requiredSize = Interop.Kernel32.GetSystemDirectoryW(buffer);
-                }
-
-                if (requiredSize == 0)
-                {
-                    throw Win32Marshal.GetExceptionForLastWin32Error();
-                }
-
-                return new string(buffer.Slice(0, requiredSize));
-            }
-        }
-    }
-}
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Environment.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Environment.cs
deleted file mode 100644 (file)
index 04bd7d0..0000000
+++ /dev/null
@@ -1,708 +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 Microsoft.Win32;
-using System.Buffers;
-using System.Collections.Generic;
-using System.Text;
-using System.Runtime.InteropServices;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Threading;
-
-#if FEATURE_WIN32_REGISTRY
-using Internal.Win32;
-#endif
-
-namespace System
-{
-    internal static partial class Environment
-    {
-        // Assume the following constants include the terminating '\0' - use <, not <=
-
-        // System environment variables are stored in the registry, and have 
-        // a size restriction that is separate from both normal environment 
-        // variables and registry value name lengths, according to MSDN.
-        // MSDN doesn't detail whether the name is limited to 1024, or whether
-        // that includes the contents of the environment variable.
-        private const int MaxSystemEnvVariableLength = 1024;
-        private const int MaxUserEnvVariableLength = 255;
-        private const int MaxMachineNameLength = 256;
-
-        // Looks up the resource string value for key.
-        // 
-        // if you change this method's signature then you must change the code that calls it
-        // in excep.cpp and probably you will have to visit mscorlib.h to add the new signature
-        // as well as metasig.h to create the new signature type
-        internal static string GetResourceStringLocal(string key)
-        {
-            return SR.GetResourceString(key);
-        }
-
-        /*==================================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 extern int TickCount
-        {
-            [MethodImplAttribute(MethodImplOptions.InternalCall)]
-            get;
-        }
-
-        // Terminates this process with the given exit code.
-        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        internal static extern void _Exit(int exitCode);
-
-        public static void Exit(int exitCode)
-        {
-            _Exit(exitCode);
-        }
-
-
-        public static extern int ExitCode
-        {
-            [MethodImplAttribute(MethodImplOptions.InternalCall)]
-            get;
-
-            [MethodImplAttribute(MethodImplOptions.InternalCall)]
-            set;
-        }
-
-        // 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.
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public static extern void FailFast(string message);
-
-        // This overload of FailFast will allow you to specify the exception object
-        // whose bucket details *could* be used when undergoing the failfast process.
-        // To be specific:
-        //
-        // 1) When invoked from within a managed EH clause (fault/finally/catch),
-        //    if the exception object is preallocated, the runtime will try to find its buckets
-        //    and use them. If the exception object is not preallocated, it will use the bucket
-        //    details contained in the object (if any).
-        //
-        // 2) When invoked from outside the managed EH clauses (fault/finally/catch),
-        //    if the exception object is preallocated, the runtime will use the callsite's
-        //    IP for bucketing. If the exception object is not preallocated, it will use the bucket
-        //    details contained in the object (if any).
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public static extern void FailFast(string message, Exception exception);
-
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public static extern void FailFast(string message, Exception exception, string errorMessage);
-
-#if FEATURE_WIN32_REGISTRY
-        // This is only used by RegistryKey on Windows.
-        internal static string ExpandEnvironmentVariables(string name)
-        {
-            Debug.Assert(name != null);
-
-            if (name.Length == 0)
-            {
-                return name;
-            }
-
-            Span<char> initialBuffer = stackalloc char[128];
-            var builder = new ValueStringBuilder(initialBuffer);
-
-            uint length;
-            while ((length = Win32Native.ExpandEnvironmentStringsW(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity)
-            {
-                builder.EnsureCapacity((int)length);
-            }
-
-            if (length == 0)
-            {
-                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
-            }
-
-            // length includes the null terminator
-            builder.Length = (int)length - 1;
-            return builder.ToString();
-        }
-#endif // FEATURE_WIN32_REGISTRY
-
-        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        private static extern int GetProcessorCount();
-
-        public static int ProcessorCount
-        {
-            get
-            {
-                return GetProcessorCount();
-            }
-        }
-
-        /*==============================GetCommandLineArgs==============================
-        **Action: Gets the command line and splits it appropriately to deal with whitespace,
-        **        quotes, and escape characters.
-        **Returns: A string array containing your command line arguments.
-        **Arguments: None
-        **Exceptions: None.
-        ==============================================================================*/
-        public static string[] GetCommandLineArgs()
-        {
-            /*
-             * There are multiple entry points to a hosted app.
-             * The host could use ::ExecuteAssembly() or ::CreateDelegate option
-             * ::ExecuteAssembly() -> In this particular case, the runtime invokes the main 
-               method based on the arguments set by the host, and we return those arguments
-             *
-             * ::CreateDelegate() -> In this particular case, the host is asked to create a 
-             * delegate based on the appDomain, assembly and methodDesc passed to it.
-             * which the caller uses to invoke the method. In this particular case we do not have
-             * any information on what arguments would be passed to the delegate.
-             * So our best bet is to simply use the commandLine that was used to invoke the process.
-             * in case it is present.
-             */
-            if (s_CommandLineArgs != null)
-                return (string[])s_CommandLineArgs.Clone();
-
-            return GetCommandLineArgsNative();
-        }
-
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        private static extern string[] GetCommandLineArgsNative();
-
-        private static string[] s_CommandLineArgs;
-        private static void SetCommandLineArgs(string[] cmdLineArgs)
-        {
-            s_CommandLineArgs = cmdLineArgs;
-        }
-
-        private static unsafe char[] GetEnvironmentCharArray()
-        {
-            char[] block = null;
-
-            RuntimeHelpers.PrepareConstrainedRegions();
-
-            char* pStrings = null;
-
-            try
-            {
-                pStrings = Win32Native.GetEnvironmentStrings();
-                if (pStrings == null)
-                {
-                    throw new OutOfMemoryException();
-                }
-
-                // 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++;
-
-                int len = (int)(p - pStrings + 1);
-                block = new char[len];
-
-                fixed (char* pBlock = block)
-                    string.wstrcpy(pBlock, pStrings, len);
-            }
-            finally
-            {
-                if (pStrings != null)
-                    Win32Native.FreeEnvironmentStrings(pStrings);
-            }
-
-            return block;
-        }
-
-        /*===================================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_WINDOWS
-                return "\r\n";
-#else
-                return "\n";
-#endif // PLATFORM_WINDOWS
-            }
-        }
-
-
-        /*===================================Version====================================
-        **Action: Returns the COM+ version struct, describing the build number.
-        **Returns:
-        **Arguments:
-        **Exceptions:
-        ==============================================================================*/
-        public static Version Version
-        {
-            get
-            {
-                // 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.
-
-                return new Version(4, 0, 30319, 42000);
-            }
-        }
-
-#if !FEATURE_PAL
-        internal static bool IsWindows8OrAbove => WindowsVersion.IsWindows8OrAbove;
-
-        // Seperate type so a .cctor is not created for Enviroment which then would be triggered during startup
-        private static class WindowsVersion
-        {
-            // Cache the value in readonly static that can be optimized out by the JIT
-            internal readonly static bool IsWindows8OrAbove = GetIsWindows8OrAbove();
-
-            private static bool GetIsWindows8OrAbove()
-            {
-                bool isWindows8OrAbove;
-                unsafe
-                {
-                    ulong conditionMask = Win32Native.VerSetConditionMask(0, Win32Native.VER_MAJORVERSION, Win32Native.VER_GREATER_EQUAL);
-                    conditionMask = Win32Native.VerSetConditionMask(conditionMask, Win32Native.VER_MINORVERSION, Win32Native.VER_GREATER_EQUAL);
-                    conditionMask = Win32Native.VerSetConditionMask(conditionMask, Win32Native.VER_SERVICEPACKMAJOR, Win32Native.VER_GREATER_EQUAL);
-                    conditionMask = Win32Native.VerSetConditionMask(conditionMask, Win32Native.VER_SERVICEPACKMINOR, Win32Native.VER_GREATER_EQUAL);
-
-                    // Windows 8 version is 6.2
-                    var version = new Win32Native.OSVERSIONINFOEX();
-                    version.dwOSVersionInfoSize = sizeof(Win32Native.OSVERSIONINFOEX);
-                    version.dwMajorVersion = 6;
-                    version.dwMinorVersion = 2;
-                    version.wServicePackMajor = 0;
-                    version.wServicePackMinor = 0;
-
-                    isWindows8OrAbove = Win32Native.VerifyVersionInfoW(ref version,
-                               Win32Native.VER_MAJORVERSION | Win32Native.VER_MINORVERSION | Win32Native.VER_SERVICEPACKMAJOR | Win32Native.VER_SERVICEPACKMINOR,
-                               conditionMask);
-                }
-
-                return isWindows8OrAbove;
-            }
-        }
-#endif
-
-#if FEATURE_COMINTEROP
-        // Seperate type so a .cctor is not created for Enviroment which then would be triggered during startup
-        private static class WinRT
-        {
-            // Cache the value in readonly static that can be optimized out by the JIT
-            public readonly static bool IsSupported = WinRTSupported();
-        }
-
-        // Does the current version of Windows have Windows Runtime suppport?
-        internal static bool IsWinRTSupported => WinRT.IsSupported;
-
-        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        [return: MarshalAs(UnmanagedType.Bool)]
-        private static extern bool WinRTSupported();
-#endif // FEATURE_COMINTEROP
-
-
-        /*==================================StackTrace==================================
-        **Action:
-        **Returns:
-        **Arguments:
-        **Exceptions:
-        ==============================================================================*/
-        public static string StackTrace
-        {
-            [MethodImpl(MethodImplOptions.NoInlining)] // Prevent inlining from affecting where the stacktrace starts
-            get
-            {
-                return Internal.Runtime.Augments.EnvironmentAugments.StackTrace;
-            }
-        }
-
-        internal static string GetStackTrace(Exception e, bool needFileInfo)
-        {
-            // Note: Setting needFileInfo to true will start up COM and set our
-            // apartment state.  Try to not call this when passing "true" 
-            // before the EE's ExecuteMainMethod has had a chance to set up the
-            // apartment state.  -- 
-            StackTrace st;
-            if (e == null)
-                st = new StackTrace(needFileInfo);
-            else
-                st = new StackTrace(e, needFileInfo);
-
-            // Do no include a trailing newline for backwards compatibility
-            return st.ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
-        }
-
-        public static extern bool HasShutdownStarted
-        {
-            [MethodImplAttribute(MethodImplOptions.InternalCall)]
-            get;
-        }
-
-        internal static bool UserInteractive
-        {
-            get
-            {
-                return true;
-            }
-        }
-        public static int CurrentManagedThreadId
-        {
-            get
-            {
-                return Thread.CurrentThread.ManagedThreadId;
-            }
-        }
-
-        public static string GetEnvironmentVariable(string variable)
-        {
-            if (variable == null)
-            {
-                throw new ArgumentNullException(nameof(variable));
-            }
-
-            // separated from the EnvironmentVariableTarget overload to help with tree shaking in common case
-            return GetEnvironmentVariableCore(variable);
-        }
-
-        internal static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target)
-        {
-            if (variable == null)
-            {
-                throw new ArgumentNullException(nameof(variable));
-            }
-
-            ValidateTarget(target);
-
-            return GetEnvironmentVariableCore(variable, target);
-        }
-
-        public static void SetEnvironmentVariable(string variable, string value)
-        {
-            ValidateVariableAndValue(variable, ref value);
-
-            // separated from the EnvironmentVariableTarget overload to help with tree shaking in common case
-            SetEnvironmentVariableCore(variable, value);
-        }
-
-        internal static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target)
-        {
-            ValidateVariableAndValue(variable, ref value);
-            ValidateTarget(target);
-
-            SetEnvironmentVariableCore(variable, value, 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;
-            }
-        }
-
-        private static void ValidateTarget(EnvironmentVariableTarget target)
-        {
-            if (target != EnvironmentVariableTarget.Process &&
-                target != EnvironmentVariableTarget.Machine &&
-                target != EnvironmentVariableTarget.User)
-            {
-                throw new ArgumentOutOfRangeException(nameof(target), target, SR.Format(SR.Arg_EnumIllegalVal, target));
-            }
-        }
-
-        private static string GetEnvironmentVariableCore(string variable)
-        {
-            Span<char> buffer = stackalloc char[128]; // A somewhat reasonable default size
-            return GetEnvironmentVariableCoreHelper(variable, buffer);
-        }
-
-        private static string GetEnvironmentVariableCoreHelper(string variable, Span<char> buffer)
-        {
-            int requiredSize = Win32Native.GetEnvironmentVariable(variable, buffer);
-
-            if (requiredSize == 0 && Marshal.GetLastWin32Error() == Interop.Errors.ERROR_ENVVAR_NOT_FOUND)
-            {
-                return null;
-            }
-
-            if (requiredSize > buffer.Length)
-            {
-                char[] chars = ArrayPool<char>.Shared.Rent(requiredSize);
-                try
-                {
-                    return GetEnvironmentVariableCoreHelper(variable, chars);
-                }
-                finally
-                {
-                    ArrayPool<char>.Shared.Return(chars);
-                }
-            }
-
-            return new string(buffer.Slice(0, requiredSize));
-        }
-
-        private static string GetEnvironmentVariableCore(string variable, EnvironmentVariableTarget target)
-        {
-            if (target == EnvironmentVariableTarget.Process)
-                return GetEnvironmentVariableCore(variable);
-
-#if FEATURE_WIN32_REGISTRY
-            if (ApplicationModel.IsUap)
-#endif
-            {
-                return null;
-            }
-#if FEATURE_WIN32_REGISTRY
-            RegistryKey baseKey;
-            string keyName;
-
-            if (target == EnvironmentVariableTarget.Machine)
-            {
-                baseKey = Registry.LocalMachine;
-                keyName = @"System\CurrentControlSet\Control\Session Manager\Environment";
-            }
-            else if (target == EnvironmentVariableTarget.User)
-            {
-                baseKey = Registry.CurrentUser;
-                keyName = "Environment";
-            }
-            else
-            {
-                throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)target));
-            }
-
-            using (RegistryKey environmentKey = baseKey.OpenSubKey(keyName, writable: false))
-            {
-                return environmentKey?.GetValue(variable) as string;
-            }
-#endif
-        }
-
-        internal static IEnumerable<KeyValuePair<string, string>> EnumerateEnvironmentVariables()
-        {
-            // 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).
-
-            char[] block = GetEnvironmentCharArray();
-            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<string, string>(key, value);
-            }
-        }
-
-        internal static IEnumerable<KeyValuePair<string, string>> EnumerateEnvironmentVariables(EnvironmentVariableTarget target)
-        {
-            if (target == EnvironmentVariableTarget.Process)
-                return EnumerateEnvironmentVariables();
-            return EnumerateEnvironmentVariablesFromRegistry(target);
-        }
-
-        internal static IEnumerable<KeyValuePair<string, string>> EnumerateEnvironmentVariablesFromRegistry(EnvironmentVariableTarget target)
-        {
-#if FEATURE_WIN32_REGISTRY
-            if (ApplicationModel.IsUap)
-#endif
-            {
-                // Without registry support we have nothing to return
-                ValidateTarget(target);
-                yield break;
-            }
-#if FEATURE_WIN32_REGISTRY
-            RegistryKey baseKey;
-            string keyName;
-            if (target == EnvironmentVariableTarget.Machine)
-            {
-                baseKey = Registry.LocalMachine;
-                keyName = @"System\CurrentControlSet\Control\Session Manager\Environment";
-            }
-            else if (target == EnvironmentVariableTarget.User)
-            {
-                baseKey = Registry.CurrentUser;
-                keyName = @"Environment";
-            }
-            else
-            {
-                throw new ArgumentOutOfRangeException(nameof(target), target, SR.Format(SR.Arg_EnumIllegalVal, target));
-            }
-
-            using (RegistryKey environmentKey = baseKey.OpenSubKey(keyName, writable: false))
-            {
-                if (environmentKey != null)
-                {
-                    foreach (string name in environmentKey.GetValueNames())
-                    {
-                        string value = environmentKey.GetValue(name, "").ToString();
-                        yield return new KeyValuePair<string, string>(name, value);
-                    }
-                }
-            }
-#endif // FEATURE_WIN32_REGISTRY
-        }
-
-        private static void SetEnvironmentVariableCore(string variable, string value)
-        {
-            // explicitly null out value if is the empty string.
-            if (string.IsNullOrEmpty(value) || value[0] == '\0')
-                value = null;
-
-            if (!Win32Native.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));
-                }
-            }
-        }
-
-        private static void SetEnvironmentVariableCore(string variable, string value, EnvironmentVariableTarget target)
-        {
-            if (target == EnvironmentVariableTarget.Process)
-            {
-                SetEnvironmentVariableCore(variable, value);
-                return;
-            }
-
-#if FEATURE_WIN32_REGISTRY
-            if (ApplicationModel.IsUap)
-#endif
-            {
-                // other targets ignored
-                return;
-            }
-#if FEATURE_WIN32_REGISTRY
-            // explicitly null out value if is the empty string.
-            if (string.IsNullOrEmpty(value) || value[0] == '\0')
-                value = null;
-
-            RegistryKey baseKey;
-            string keyName;
-
-            if (target == EnvironmentVariableTarget.Machine)
-            {
-                baseKey = Registry.LocalMachine;
-                keyName = @"System\CurrentControlSet\Control\Session Manager\Environment";
-            }
-            else if (target == EnvironmentVariableTarget.User)
-            {
-                // 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));
-                }
-
-                baseKey = Registry.CurrentUser;
-                keyName = "Environment";
-            }
-            else
-            {
-                throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)target));
-            }
-
-            using (RegistryKey environmentKey = baseKey.OpenSubKey(keyName, 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());
-#endif // FEATURE_WIN32_REGISTRY
-        }
-    }
-}
index bfdd601..de5e786 100644 (file)
@@ -307,12 +307,17 @@ namespace System
                 return remoteStackTraceString;
             }
 
-            // Obtain the stack trace string. Note that since Environment.GetStackTrace
-            // will add the path to the source file if the PDB is present and a demand
-            // for FileIOPermission(PathDiscovery) succeeds, we need to make sure we 
-            // don't store the stack trace string in the _stackTraceString member variable.
-            string tempStackTraceString = Environment.GetStackTrace(this, needFileInfo);
-            return remoteStackTraceString + tempStackTraceString;
+            // Obtain the stack trace string. Note that since GetStackTrace
+            // will add the path to the source file if the PDB is present:
+            // we need to make sure we don't store the stack trace string in
+            // the _stackTraceString member variable.
+            return remoteStackTraceString + GetStackTrace(needFileInfo, this);
+        }
+
+        private static string GetStackTrace(bool needFileInfo, Exception e)
+        {
+            // Do not include a trailing newline for backwards compatibility
+            return new StackTrace(e, needFileInfo).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
         }
 
         // Sets the help link for this exception.
@@ -418,7 +423,7 @@ namespace System
             {
                 if (tempStackTraceString == null)
                 {
-                    tempStackTraceString = Environment.GetStackTrace(this, true);
+                    tempStackTraceString = GetStackTrace(true, this);
                 }
                 if (_exceptionMethod == null)
                 {
index ba942ba..ce3db6f 100644 (file)
@@ -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/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs
new file mode 100644 (file)
index 0000000..13adefc
--- /dev/null
@@ -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<char> 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);
+    }
+}
index a10e0c2..e0a7aa5 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\DuplicateWaitObjectException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Empty.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\EntryPointNotFoundException.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.SpecialFolder.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.SpecialFolderOption.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\EnvironmentVariableTarget.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\EventArgs.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\EventHandler.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Object.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ObjectDisposedException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ObsoleteAttribute.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\OperatingSystem.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\OperationCanceledException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\OutOfMemoryException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\OverflowException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ParamArrayAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ParamsArray.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ParseNumbers.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\PasteArguments.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\PlatformID.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\PlatformNotSupportedException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Progress.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Random.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Numerics\Vector_Operations.cs" />
   </ItemGroup>
   <ItemGroup Condition="$(TargetsWindows)">
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Advapi32\Interop.LookupAccountNameW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\BCrypt\Interop.BCryptGenRandom.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\BCrypt\Interop.BCryptGenRandom.GetRandomBytes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\BCrypt\Interop.NTSTATUS.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Crypt32\Interop.CryptProtectMemory.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.BOOL.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.BOOLEAN.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Libraries.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CancelIoEx.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ExpandEnvironmentStrings.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileAttributes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FILE_INFO_BY_HANDLE_CLASS.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileTypes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindClose.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindFirstFileEx.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FlushFileBuffers.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FreeEnvironmentStrings.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetComputerName.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetCPInfo.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetEnvironmentStrings.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetCurrentProcess_IntPtr.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetCurrentDirectory.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileAttributesEx.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileInformationByHandleEx.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLogicalDrives.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemDirectoryW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemInfo.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempFileNameW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempPathW.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetVersionExW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Globalization.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GlobalMemoryStatusEx.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.IsWow64Process_IntPtr.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LockFile.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MAX_PATH.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.MEMORY_BASIC_INFORMATION.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ResolveLocaleName.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SecurityOptions.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetCurrentDirectory.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetEndOfFile.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetEnvironmentVariable.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SYSTEM_INFO.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysFreeString.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysStringLen.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Secur32\Interop.GetUserNameExW.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Shell32\Interop.SHGetKnownFolderPath.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebugProvider.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Normalization.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Guid.Windows.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamCompletionSource.Win32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathHelper.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\DisableMediaInsertionPrompt.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\PasteArguments.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.Windows.cs" Condition="'$(TargetsCoreRT)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Security\SafeBSTRHandle.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.LoadString.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.SendMessageTimeout.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Win32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Win32.cs" />
   </ItemGroup>
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Constants.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Errors.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetEnvironmentVariable.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetEnvironmentStrings.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FreeEnvironmentStrings.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FormatMessage.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Mutex.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Semaphore.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetEnvironmentVariable.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.EventWaitHandle.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Variables.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\Win32Marshal.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Mutex.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Semaphore.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.ResultCode.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.TimeZoneInfo.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Utils.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Access.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.ChDir.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Close.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.FLock.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.FSync.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.FTruncate.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.GetCwd.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.GetEUid.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.GetHostName.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.GetPwUid.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.GetRandomBytes.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.GetUnixName.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.GetUnixRelease.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.LockFileRegion.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.LSeek.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.MksTemps.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.MountPoints.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Open.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.OpenFlags.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.PathConf.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.ReadDir.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.ReadLink.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Stat.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.SysConf.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.SysLog.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Unlink.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Write.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebugProvider.Unix.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Normalization.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Guid.Unix.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.OSX.cs" Condition="'$(TargetsOSX)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Linux.cs" Condition="'$(TargetsOSX)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Unix.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Unix.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Names.Unix.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\PasteArguments.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.Unix.cs" Condition="'$(TargetsCoreRT)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.cs" />
   </ItemGroup>
+  <ItemGroup Condition="$(TargetsUnix) or '$(EnableWinRT)'=='true'">
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.NoRegistry.cs" />
+  </ItemGroup>
   <ItemGroup Condition="'$(FeatureHardwareIntrinsics)' == 'true' AND ('$(Platform)' == 'x64' OR ('$(Platform)' == 'x86' AND '$(TargetsUnix)' != 'true'))">
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\X86\Aes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\X86\Avx.cs" />
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.NoRegistry.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.NoRegistry.cs
new file mode 100644 (file)
index 0000000..427d29d
--- /dev/null
@@ -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();
+    }
+}
index e18b595..a69aa63 100644 (file)
@@ -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<string, object> 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<char> initialBuffer = stackalloc char[128];
@@ -101,7 +99,7 @@ namespace System
                 case SpecialFolder.CommonApplicationData: return "/usr/share";
                 case SpecialFolder.CommonTemplates: return "/usr/share/templates";
             }
-            if (IsMac)
+            if (s_isMac)
             {
                 switch (folder)
                 {
@@ -161,17 +159,17 @@ namespace System
                     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/libraries/System.Private.CoreLib/src/System/Environment.Variables.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Variables.Windows.cs
new file mode 100644 (file)
index 0000000..7e8bcce
--- /dev/null
@@ -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<char> 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<char>.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<char>.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<char> block = new Span<char>(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);
+            }
+        }
+    }
+}
index 3ac7663..f7b87ff 100644 (file)
 // 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 @@ namespace System
         {
             get
             {
+#if FEATURE_APPX
+                if (ApplicationModel.IsUap)
+                    return "Windows Domain";
+#endif
+
                 // See the comment in UserName
                 Span<char> initialBuffer = stackalloc char[40];
                 var builder = new ValueStringBuilder(initialBuffer);
@@ -108,6 +221,11 @@ namespace System
 
         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 @@ namespace System
 
             return path;
         }
+
+#if FEATURE_APPX
+        private static class WinRTFolderPaths
+        {
+            private static Func<SpecialFolder, SpecialFolderOption, string> 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<SpecialFolder, SpecialFolderOption, string>)getFolderPathsMethod?.CreateDelegate(typeof(Func<SpecialFolder, SpecialFolderOption, string>));
+                    s_winRTFolderPathsGetFolderPath = d ?? delegate { return null; };
+                }
+
+                return s_winRTFolderPathsGetFolderPath(folder, option);
+            }
+        }
+#endif
     }
 }
index 991c69c..db56ea1 100644 (file)
@@ -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 @@ namespace System
             }
         }
 
-        public static int ExitCode { get { return EnvironmentAugments.ExitCode; } set { EnvironmentAugments.ExitCode = value; } }
-
         private static string ExpandEnvironmentVariablesCore(string name)
         {
             Span<char> 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 @@ namespace System
             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<OperatingSystem> s_osVersion = new Lazy<OperatingSystem>(() =>
         {
index 6daccb8..870f22d 100644 (file)
@@ -6,8 +6,6 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Reflection;
-using System.Runtime.CompilerServices;
-using Internal.Runtime.Augments;
 
 namespace System
 {
@@ -15,157 +13,113 @@ namespace System
     {
         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<KeyValuePair<string, string>> pairs)
-        {
-            Hashtable hashTable = new Hashtable();
-            foreach (KeyValuePair<string, string> 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 @@ namespace System
                         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;
+            }
+        }
     }
 }