diff options
author | Stephen Toub <stoub@microsoft.com> | 2019-01-30 16:22:27 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-30 16:22:27 -0500 |
commit | 91e1ffccc38fc87f6f496eb056396a1b775f08af (patch) | |
tree | 827d79048645d76128154f67d2e571766ae39893 | |
parent | 6a43dd765ea6341310fde3f83bb41ff88d94b435 (diff) | |
download | coreclr-91e1ffccc38fc87f6f496eb056396a1b775f08af.tar.gz coreclr-91e1ffccc38fc87f6f496eb056396a1b775f08af.tar.bz2 coreclr-91e1ffccc38fc87f6f496eb056396a1b775f08af.zip |
Switch CoreLib over to using shared Environment from corefx (#22106)
This requires merging/adapting the implementation with EnvironmentAugments (which goes away completely), the shared files, what corert has, etc.
18 files changed, 692 insertions, 910 deletions
diff --git a/src/System.Private.CoreLib/Resources/Strings.resx b/src/System.Private.CoreLib/Resources/Strings.resx index 98a3d9ebdb..6800d8ed33 100644 --- a/src/System.Private.CoreLib/Resources/Strings.resx +++ b/src/System.Private.CoreLib/Resources/Strings.resx @@ -496,6 +496,9 @@ <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> @@ -2464,6 +2467,9 @@ <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> @@ -3190,6 +3196,9 @@ <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> diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj index db83064834..d8bde72ee4 100644 --- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -160,7 +160,7 @@ <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" /> @@ -371,7 +371,6 @@ <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> <!-- @@ -444,4 +443,4 @@ </ItemGroup> <Import Project="ILLink.targets" /> <Import Project="GenerateCompilerResponseFile.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs index ba942ba6ff..ce3db6f2b4 100644 --- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs @@ -8,7 +8,7 @@ internal partial class Interop { internal partial class Kernel32 { - [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint ExpandEnvironmentStringsW(string lpSrc, ref char lpDst, uint nSize); + [DllImport(Libraries.Kernel32, EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern uint ExpandEnvironmentStrings(string lpSrc, ref char lpDst, uint nSize); } } diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs new file mode 100644 index 0000000000..13adefc216 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetEnvironmentVariable.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal static unsafe int GetEnvironmentVariable(string lpName, Span<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); + } +} diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index a10e0c247b..e0a7aa594c 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -211,6 +211,9 @@ <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" /> @@ -370,12 +373,15 @@ <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" /> @@ -915,32 +921,40 @@ <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" /> @@ -952,8 +966,8 @@ <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" /> @@ -971,9 +985,12 @@ <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" /> @@ -987,12 +1004,14 @@ <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" /> @@ -1030,6 +1049,7 @@ <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> @@ -1043,10 +1063,15 @@ <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" /> @@ -1066,15 +1091,23 @@ <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" /> @@ -1084,12 +1117,14 @@ <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" /> @@ -1101,16 +1136,23 @@ <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/System.Private.CoreLib/shared/System/Environment.NoRegistry.cs b/src/System.Private.CoreLib/shared/System/Environment.NoRegistry.cs new file mode 100644 index 0000000000..427d29d818 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Environment.NoRegistry.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Collections; +using Microsoft.Win32; + +namespace System +{ + public static partial class Environment + { + // Systems without the Windows registry pretend that it's always empty. + + private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) => null; + + private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) { } + + private static IDictionary GetEnvironmentVariablesFromRegistry(bool fromMachine) => new Hashtable(); + } +} diff --git a/src/System.Private.CoreLib/shared/System/Environment.Unix.cs b/src/System.Private.CoreLib/shared/System/Environment.Unix.cs index e18b5951ea..a69aa63a72 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.Unix.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.Unix.cs @@ -16,17 +16,15 @@ namespace System { public static partial class Environment { - internal static readonly bool IsMac = Interop.Sys.GetUnixName() == "OSX"; + private static readonly bool s_isMac = Interop.Sys.GetUnixName() == "OSX"; private static Func<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/System.Private.CoreLib/shared/System/Environment.Variables.Windows.cs b/src/System.Private.CoreLib/shared/System/Environment.Variables.Windows.cs new file mode 100644 index 0000000000..7e8bcce6ad --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Environment.Variables.Windows.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace System +{ + public static partial class Environment + { + private static string GetEnvironmentVariableCore(string variable) + { + Span<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); + } + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Environment.Win32.cs b/src/System.Private.CoreLib/shared/System/Environment.Win32.cs index 3ac7663b50..f7b87ff786 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.Win32.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.Win32.cs @@ -2,18 +2,126 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections; +using System.Diagnostics; using System.IO; -using System.Text; +using System.Reflection; using System.Runtime.InteropServices; +using System.Text; +using Internal.Win32; namespace System { public static partial class Environment { + private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) + { + Debug.Assert(variable != null); + +#if FEATURE_APPX + if (ApplicationModel.IsUap) + return null; // Systems without the Windows registry pretend that it's always empty. +#endif + + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: false)) + { + return environmentKey?.GetValue(variable) as string; + } + } + + private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) + { + Debug.Assert(variable != null); + +#if FEATURE_APPX + if (ApplicationModel.IsUap) + return; // Systems without the Windows registry pretend that it's always empty. +#endif + + const int MaxUserEnvVariableLength = 255; // User-wide env vars stored in the registry have names limited to 255 chars + if (!fromMachine && variable.Length >= MaxUserEnvVariableLength) + { + throw new ArgumentException(SR.Argument_LongEnvVarValue, nameof(variable)); + } + + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: true)) + { + if (environmentKey != null) + { + if (value == null) + { + environmentKey.DeleteValue(variable, throwOnMissingValue: false); + } + else + { + environmentKey.SetValue(variable, value); + } + } + } + + // send a WM_SETTINGCHANGE message to all windows + IntPtr r = Interop.User32.SendMessageTimeout(new IntPtr(Interop.User32.HWND_BROADCAST), Interop.User32.WM_SETTINGCHANGE, IntPtr.Zero, "Environment", 0, 1000, IntPtr.Zero); + Debug.Assert(r != IntPtr.Zero, "SetEnvironmentVariable failed: " + Marshal.GetLastWin32Error()); + } + + private static IDictionary GetEnvironmentVariablesFromRegistry(bool fromMachine) + { + var results = new Hashtable(); +#if FEATURE_APPX + if (ApplicationModel.IsUap) // Systems without the Windows registry pretend that it's always empty. + return results; +#endif + + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: false)) + { + if (environmentKey != null) + { + foreach (string name in environmentKey.GetValueNames()) + { + string value = environmentKey.GetValue(name, "").ToString(); + try + { + results.Add(name, value); + } + catch (ArgumentException) + { + // Throw and catch intentionally to provide non-fatal notification about corrupted environment block + } + } + } + } + + return results; + } + + private static RegistryKey OpenEnvironmentKeyIfExists(bool fromMachine, bool writable) + { + RegistryKey baseKey; + string keyName; + + if (fromMachine) + { + baseKey = Registry.LocalMachine; + keyName = @"System\CurrentControlSet\Control\Session Manager\Environment"; + } + else + { + baseKey = Registry.CurrentUser; + keyName = "Environment"; + } + + return baseKey.OpenSubKey(keyName, writable: writable); + } + public static string UserName { get { +#if FEATURE_APPX + if (ApplicationModel.IsUap) + return "Windows User"; +#endif + // 40 should be enough as we're asking for the SAM compatible name (DOMAIN\User). // The max length should be 15 (domain) + 1 (separator) + 20 (name) + null. If for // some reason it isn't, we'll grow the buffer. @@ -60,6 +168,11 @@ 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 } } diff --git a/src/System.Private.CoreLib/shared/System/Environment.Windows.cs b/src/System.Private.CoreLib/shared/System/Environment.Windows.cs index 991c69c023..db56ea1a7a 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.Windows.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.Windows.cs @@ -3,9 +3,10 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; -using Internal.Runtime.Augments; +using Microsoft.Win32; namespace System { @@ -59,15 +60,13 @@ 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>(() => { diff --git a/src/System.Private.CoreLib/shared/System/Environment.cs b/src/System.Private.CoreLib/shared/System/Environment.cs index 6daccb8da9..870f22d49a 100644 --- a/src/System.Private.CoreLib/shared/System/Environment.cs +++ b/src/System.Private.CoreLib/shared/System/Environment.cs @@ -6,8 +6,6 @@ using System.Collections; 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; + } + } } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs index 8bed354ac1..a501f87a56 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs @@ -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 } } diff --git a/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs b/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs index 4374c735cd..9d2cc9a4e6 100644 --- a/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs +++ b/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs @@ -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); diff --git a/src/System.Private.CoreLib/src/System/CLRConfig.cs b/src/System.Private.CoreLib/src/System/CLRConfig.cs index 1885c57e9f..5b14f282ed 100644 --- a/src/System.Private.CoreLib/src/System/CLRConfig.cs +++ b/src/System.Private.CoreLib/src/System/CLRConfig.cs @@ -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/System.Private.CoreLib/src/System/Environment.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs new file mode 100644 index 0000000000..8d92a7e4a8 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Environment.CoreCLR.cs @@ -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/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/System.Private.CoreLib/src/System/Environment.Windows.cs deleted file mode 100644 index 5bf7bea158..0000000000 --- a/src/System.Private.CoreLib/src/System/Environment.Windows.cs +++ /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/System.Private.CoreLib/src/System/Environment.cs b/src/System.Private.CoreLib/src/System/Environment.cs deleted file mode 100644 index 04bd7d0ce7..0000000000 --- a/src/System.Private.CoreLib/src/System/Environment.cs +++ /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 - } - } -} diff --git a/src/System.Private.CoreLib/src/System/Exception.cs b/src/System.Private.CoreLib/src/System/Exception.cs index bfdd601802..de5e786b1c 100644 --- a/src/System.Private.CoreLib/src/System/Exception.cs +++ b/src/System.Private.CoreLib/src/System/Exception.cs @@ -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) { |