diff --git a/src/Common/src/Interop/Unix/libc/Interop.Environment.cs b/src/Common/src/Interop/Unix/libc/Interop.Environment.cs new file mode 100644 index 00000000000..f425906dcb4 --- /dev/null +++ b/src/Common/src/Interop/Unix/libc/Interop.Environment.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class libc + { + + } +} diff --git a/src/Common/src/Interop/Windows/mincore/Interop.Environment.cs b/src/Common/src/Interop/Windows/mincore/Interop.Environment.cs new file mode 100644 index 00000000000..c1e6276e8ee --- /dev/null +++ b/src/Common/src/Interop/Windows/mincore/Interop.Environment.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + private static class Libraries + { + internal const string Process = "api-ms-win-core-processenvironment-l1-1-0.dll"; + } + + internal static unsafe partial class mincore + { + // TODO: Once we have marshalling setup we probably want to revisit these PInvokes + [DllImport(Libraries.Process, EntryPoint = "GetEnvironmentVariableW")] + internal static unsafe extern int GetEnvironmentVariable(char* lpName, char* lpValue, int size); + + [DllImport(Libraries.Process, EntryPoint = "ExpandEnvironmentStringsW")] + internal static unsafe extern int ExpandEnvironmentStrings(char* lpSrc, char* lpDst, int nSize); + } +} diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 325ed5f79fa..cd36ab785aa 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -219,7 +219,7 @@ - + @@ -477,6 +477,11 @@ + + + + + @@ -485,6 +490,9 @@ + + + diff --git a/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.UWP.cs b/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.UWP.cs new file mode 100644 index 00000000000..54c47ce8681 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.UWP.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*============================================================ +** +** +** Purpose: Provides some basic access to some environment +** functionality. +** +** +============================================================*/ + +using System.Text; +using System.Collections; + +namespace System +{ + public static partial class Environment + { + public unsafe static String ExpandEnvironmentVariables(String name) + { + if (name == null) + throw new ArgumentNullException("name"); + + // Environment variable accessors are not approved modern API. + // Behave as if no variables are defined in this case. + return name; + } + + public unsafe static String GetEnvironmentVariable(String variable) + { + if (variable == null) + throw new ArgumentNullException("variable"); + + // Environment variable accessors are not approved modern API. + // Behave as if the variable was not found in this case. + return null; + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.Unix.cs b/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.Unix.cs new file mode 100644 index 00000000000..19bb7032026 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.Unix.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*============================================================ +** +** +** Purpose: Provides some basic access to some environment +** functionality. +** +** +============================================================*/ + +using System.Text; +using System.Collections; + +namespace System +{ + public static partial class Environment + { + public unsafe static String ExpandEnvironmentVariables(String name) + { + if (name == null) + throw new ArgumentNullException("name"); + + //TODO: Unix implementations + return name; + } + + public unsafe static String GetEnvironmentVariable(String variable) + { + if (variable == null) + throw new ArgumentNullException("variable"); + + //TODO: Unix implementations + return null; + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.Win32.cs b/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.Win32.cs new file mode 100644 index 00000000000..c713f912a5b --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.Win32.cs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*============================================================ +** +** +** Purpose: Provides some basic access to some environment +** functionality. +** +** +============================================================*/ + +using System.Text; +using System.Collections; + +namespace System +{ + public static partial class Environment + { + public unsafe static String ExpandEnvironmentVariables(String name) + { + if (name == null) + throw new ArgumentNullException("name"); + + if (name.Length == 0) + { + return name; + } + + int currentSize = 128; + char* blob = stackalloc char[currentSize]; // A somewhat reasonable default size + int requiredSize; + fixed (char* pName = name) + { + requiredSize = Interop.mincore.ExpandEnvironmentStrings(pName, blob, currentSize); + } + + if (requiredSize == 0) + { + // TODO: This used to throw an exception: + // Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + throw new ArgumentException(); + } + + if (requiredSize <= currentSize) + { + return new string(blob); + } + + // Fallback to using heap allocated buffers. + char[] newBlob = null; + while (requiredSize > currentSize) + { + currentSize = requiredSize; + newBlob = new char[currentSize]; + + fixed (char* pName = name, pBlob = newBlob) + { + requiredSize = Interop.mincore.ExpandEnvironmentStrings(pName, pBlob, currentSize); + } + + if (requiredSize == 0) + { + // TODO: This used to throw an exception: + // Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + throw new ArgumentException(); + } + } + + return new string(newBlob); + } + + public unsafe static String GetEnvironmentVariable(String variable) + { + if (variable == null) + throw new ArgumentNullException("variable"); + + // The convention of the API is as follows: + // You call the API with a buffer of a given size. + // If the size of the buffer is insufficient to hold the value, + // the API will return the required size for the buffer. + // In that case we resize the buffer and try again. + + int currentSize = 128; + char* blob = stackalloc char[currentSize]; // A somewhat reasonable default size + + int requiredSize; + fixed (char* pText = variable) + { + requiredSize = Interop.mincore.GetEnvironmentVariable(pText, blob, currentSize); + } + + if (requiredSize == 0) + { + return null; + } + + if (requiredSize <= currentSize) + { + return new string(blob); + } + + // Fallback to using heap allocated buffers. + char[] newblob = null; + while (requiredSize > currentSize) + { + currentSize = requiredSize; + // need to retry since the environment variable might be changed + newblob = new char[currentSize]; + fixed (char* pText = variable, pBlob = newblob) + { + requiredSize = Interop.mincore.GetEnvironmentVariable(pText, pBlob, currentSize); + } + + if (requiredSize == 0) + { + return null; + } + } + // We should never end up with a null blob + Diagnostics.Debug.Assert(newblob != null); + + return new string(newblob); + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.cs b/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.cs index 99e66c97208..7e0d9efad3e 100644 --- a/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.cs +++ b/src/System.Private.CoreLib/src/System/Environment.EnvironmentVariables.cs @@ -15,30 +15,11 @@ namespace System { + public static partial class Environment { private const int MaxEnvVariableValueLength = 32767; // maximum length for environment variable name and value - public static String ExpandEnvironmentVariables(String name) - { - if (name == null) - throw new ArgumentNullException("name"); - - // Environment variable accessors are not approved modern API. - // Behave as if no variables are defined in this case. - return name; - } - - public static String GetEnvironmentVariable(String variable) - { - if (variable == null) - throw new ArgumentNullException("variable"); - - // Environment variable accessors are not approved modern API. - // Behave as if the variable was not found in this case. - return null; - } - public static IDictionary GetEnvironmentVariables() { // Environment variable accessors are not approved modern API.