From 49d55dd43674a2113a582cd30e17831fea0f2a59 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 17 Jan 2019 20:07:12 -0500 Subject: [PATCH] Move Environment to shared CoreLib (dotnet/corefxdotnet/coreclr#34654) Rather than having Environment partially live in corefx and call into an EnvironmentAugments type in CoreLib that in turn calls into an Environment type in CoreLib, we're just moving Environment to live in CoreLib. To start that, this PR moves Environment and its dependencies from their current locations into the shared CoreLib. Those changes will mirror over to coreclr. After that, I'll fix it up to work in CoreLib. And once those changes are built and available back to corefx, I'll update System.Runtime.Extensions to just use the functionality from CoreLib and delete remaining unnecessary code from corefx. Signed-off-by: dotnet-bot Commit migrated from https://github.com/dotnet/coreclr/commit/1e21dfc513b51b151d7c9b16f1e2a6a81c339d94 --- .../Interop/Unix/System.Native/Interop.Access.cs | 23 ++ .../Interop/Unix/System.Native/Interop.ChDir.cs | 15 + .../Interop/Unix/System.Native/Interop.GetEUid.cs | 15 + .../Unix/System.Native/Interop.GetHostName.cs | 40 ++ .../Interop/Unix/System.Native/Interop.GetPwUid.cs | 31 ++ .../Unix/System.Native/Interop.GetUnixName.cs | 21 + .../Unix/System.Native/Interop.GetUnixRelease.cs | 14 + .../Unix/System.Native/Interop.MountPoints.cs | 40 ++ .../Interop/Unix/System.Native/Interop.SysConf.cs | 20 + .../Windows/Advapi32/Interop.LookupAccountNameW.cs | 21 + .../src/Interop/Windows/Interop.BOOLEAN.cs | 21 + .../src/Interop/Windows/Interop.Libraries.cs | 2 + .../Kernel32/Interop.ExpandEnvironmentStrings.cs | 14 + .../Windows/Kernel32/Interop.GetComputerName.cs | 28 ++ .../Kernel32/Interop.GetCurrentDirectory.cs | 14 + .../Kernel32/Interop.GetCurrentProcess_IntPtr.cs | 15 + .../Windows/Kernel32/Interop.GetLogicalDrives.cs | 14 + .../Kernel32/Interop.GetSystemDirectoryW.cs | 14 + .../Windows/Kernel32/Interop.GetSystemInfo.cs | 15 + .../Windows/Kernel32/Interop.GetVersionExW.cs | 30 ++ .../Kernel32/Interop.IsWow64Process_IntPtr.cs | 15 + .../Windows/Kernel32/Interop.SYSTEM_INFO.cs | 38 ++ .../Kernel32/Interop.SetCurrentDirectory.cs | 14 + .../Windows/Secur32/Interop.GetUserNameExW.cs | 16 + .../Shell32/Interop.SHGetKnownFolderPath.cs | 320 +++++++++++++++ .../src/System/Environment.SpecialFolder.cs | 115 ++++++ .../src/System/Environment.SpecialFolderOption.cs | 33 ++ .../src/System/Environment.Unix.cs | 449 +++++++++++++++++++++ .../src/System/Environment.Win32.cs | 284 +++++++++++++ .../src/System/Environment.WinRT.cs | 141 +++++++ .../src/System/Environment.Windows.cs | 135 +++++++ .../src/System/Environment.cs | 193 +++++++++ .../src/System/IO/DriveInfoInternal.Unix.cs | 15 + .../src/System/IO/DriveInfoInternal.Windows.cs | 87 ++++ .../src/System/IO/PersistedFiles.Names.Unix.cs | 17 + .../src/System/IO/PersistedFiles.Unix.cs | 163 ++++++++ .../src/System/OperatingSystem.cs | 83 ++++ .../src/System/PasteArguments.Unix.cs | 28 ++ .../src/System/PasteArguments.Windows.cs | 66 +++ .../src/System/PasteArguments.cs | 99 +++++ .../src/System/PlatformID.cs | 19 + 41 files changed, 2737 insertions(+) create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.Access.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.ChDir.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetEUid.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetHostName.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetPwUid.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetUnixName.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetUnixRelease.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.MountPoints.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.SysConf.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Advapi32/Interop.LookupAccountNameW.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Interop.BOOLEAN.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetComputerName.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetCurrentDirectory.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetCurrentProcess_IntPtr.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetLogicalDrives.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetSystemDirectoryW.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetSystemInfo.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetVersionExW.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.IsWow64Process_IntPtr.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.SYSTEM_INFO.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.SetCurrentDirectory.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Secur32/Interop.GetUserNameExW.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/Shell32/Interop.SHGetKnownFolderPath.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.SpecialFolder.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.SpecialFolderOption.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.WinRT.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/DriveInfoInternal.Unix.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/DriveInfoInternal.Windows.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Names.Unix.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Unix.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/PasteArguments.Unix.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/PasteArguments.Windows.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/PasteArguments.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/PlatformID.cs diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.Access.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.Access.cs new file mode 100644 index 0000000..a723f57 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.Access.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 static partial class Interop +{ + internal static partial class Sys + { + internal enum AccessMode : int + { + F_OK = 0, /* Check for existence */ + X_OK = 1, /* Check for execute */ + W_OK = 2, /* Check for write */ + R_OK = 4, /* Check for read */ + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Access", SetLastError = true)] + internal static extern int Access(string path, AccessMode mode); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.ChDir.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.ChDir.cs new file mode 100644 index 0000000..3c66995 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.ChDir.cs @@ -0,0 +1,15 @@ +// 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 static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ChDir", SetLastError = true)] + internal static extern int ChDir(string path); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetEUid.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetEUid.cs new file mode 100644 index 0000000..8b525fa --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetEUid.cs @@ -0,0 +1,15 @@ +// 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 static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetEUid")] + internal static extern uint GetEUid(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetHostName.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetHostName.cs new file mode 100644 index 0000000..3858b49 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetHostName.cs @@ -0,0 +1,40 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostName", SetLastError = true)] + private static extern unsafe int GetHostName(byte* name, int nameLength); + + internal static unsafe string GetHostName() + { + const int HOST_NAME_MAX = 255; + const int ArrLength = HOST_NAME_MAX + 1; + + byte* name = stackalloc byte[ArrLength]; + int err = GetHostName(name, ArrLength); + if (err != 0) + { + // This should never happen. According to the man page, + // the only possible errno for gethostname is ENAMETOOLONG, + // which should only happen if the buffer we supply isn't big + // enough, and we're using a buffer size that the man page + // says is the max for POSIX (and larger than the max for Linux). + Debug.Fail("gethostname failed"); + throw new InvalidOperationException(string.Format("gethostname returned {0}", err)); + } + + // If the hostname is truncated, it is unspecified whether the returned buffer includes a terminating null byte. + name[ArrLength - 1] = 0; + + return Marshal.PtrToStringAnsi((IntPtr)name); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetPwUid.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetPwUid.cs new file mode 100644 index 0000000..56d55c9 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetPwUid.cs @@ -0,0 +1,31 @@ +// 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 static partial class Interop +{ + internal static partial class Sys + { + internal unsafe struct Passwd + { + internal const int InitialBufferSize = 256; + + internal byte* Name; + internal byte* Password; + internal uint UserId; + internal uint GroupId; + internal byte* UserInfo; + internal byte* HomeDirectory; + internal byte* Shell; + }; + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetPwUidR", SetLastError = false)] + internal static extern unsafe int GetPwUidR(uint uid, out Passwd pwd, byte* buf, int bufLen); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetPwNamR", SetLastError = false)] + internal static extern unsafe int GetPwNamR(string name, out Passwd pwd, byte* buf, int bufLen); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetUnixName.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetUnixName.cs new file mode 100644 index 0000000..33664c4 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetUnixName.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; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetUnixName")] + private static extern IntPtr GetUnixNamePrivate(); + + internal static string GetUnixName() + { + IntPtr ptr = GetUnixNamePrivate(); + return Marshal.PtrToStringAnsi(ptr); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetUnixRelease.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetUnixRelease.cs new file mode 100644 index 0000000..5e41ae9 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.GetUnixRelease.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetUnixRelease", CharSet = CharSet.Ansi, SetLastError = true)] + public static extern string GetUnixRelease(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.MountPoints.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.MountPoints.cs new file mode 100644 index 0000000..7e31197 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.MountPoints.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private unsafe delegate void MountPointFound(byte* name); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetAllMountPoints", SetLastError = true)] + private static extern int GetAllMountPoints(MountPointFound mpf); + + internal static string[] GetAllMountPoints() + { + int count = 0; + var found = new string[4]; + + unsafe + { + int result = GetAllMountPoints((byte* name) => + { + if (count == found.Length) + { + Array.Resize(ref found, count * 2); + } + found[count++] = Marshal.PtrToStringAnsi((IntPtr)name); + }); + } + + Array.Resize(ref found, count); + return found; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.SysConf.cs b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.SysConf.cs new file mode 100644 index 0000000..be0c0de --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Unix/System.Native/Interop.SysConf.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal enum SysConfName + { + _SC_CLK_TCK = 1, + _SC_PAGESIZE = 2 + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SysConf", SetLastError = true)] + internal static extern long SysConf(SysConfName name); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Advapi32/Interop.LookupAccountNameW.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Advapi32/Interop.LookupAccountNameW.cs new file mode 100644 index 0000000..3554665 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Advapi32/Interop.LookupAccountNameW.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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Advapi32 + { + [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] + internal static extern bool LookupAccountNameW( + string lpSystemName, + ref char lpAccountName, + ref byte Sid, + ref uint cbSid, + ref char ReferencedDomainName, + ref uint cchReferencedDomainName, + out uint peUse); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Interop.BOOLEAN.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Interop.BOOLEAN.cs new file mode 100644 index 0000000..4874734 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Interop.BOOLEAN.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. + +internal partial class Interop +{ + /// + /// Blittable version of Windows BOOLEAN type. It is convenient in situations where + /// manual marshalling is required, or to avoid overhead of regular bool marshalling. + /// + /// + /// Some Windows APIs return arbitrary integer values although the return type is defined + /// as BOOLEAN. It is best to never compare BOOLEAN to TRUE. Always use bResult != BOOLEAN.FALSE + /// or bResult == BOOLEAN.FALSE . + /// + internal enum BOOLEAN : byte + { + FALSE = 0, + TRUE = 1, + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Interop.Libraries.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Interop.Libraries.cs index 398d18a..8cbc8bf 100644 --- a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Interop.Libraries.cs +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Interop.Libraries.cs @@ -14,5 +14,7 @@ internal static partial class Interop internal const string OleAut32 = "oleaut32.dll"; internal const string User32 = "user32.dll"; internal const string NtDll = "ntdll.dll"; + internal const string Secur32 = "secur32.dll"; + internal const string Shell32 = "shell32.dll"; } } diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs new file mode 100644 index 0000000..ba942ba --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.ExpandEnvironmentStrings.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +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); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetComputerName.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetComputerName.cs new file mode 100644 index 0000000..34a26c1 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetComputerName.cs @@ -0,0 +1,28 @@ +// 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 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "GetComputerNameW")] + private static extern unsafe int GetComputerName(ref char lpBuffer, ref uint nSize); + + // maximum length of the NETBIOS name (not including NULL) + private const int MAX_COMPUTERNAME_LENGTH = 15; + + internal static unsafe string GetComputerName() + { + Span buffer = stackalloc char[MAX_COMPUTERNAME_LENGTH + 1]; + uint length = (uint)buffer.Length; + + return GetComputerName(ref MemoryMarshal.GetReference(buffer), ref length) != 0 ? + buffer.Slice(0, (int)length).ToString() : + null; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetCurrentDirectory.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetCurrentDirectory.cs new file mode 100644 index 0000000..611cc70 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetCurrentDirectory.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "GetCurrentDirectoryW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] + internal static extern uint GetCurrentDirectory(uint nBufferLength, ref char lpBuffer); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetCurrentProcess_IntPtr.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetCurrentProcess_IntPtr.cs new file mode 100644 index 0000000..406e4b0 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetCurrentProcess_IntPtr.cs @@ -0,0 +1,15 @@ +// 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 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern IntPtr GetCurrentProcess(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetLogicalDrives.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetLogicalDrives.cs new file mode 100644 index 0000000..657188f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetLogicalDrives.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern int GetLogicalDrives(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetSystemDirectoryW.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetSystemDirectoryW.cs new file mode 100644 index 0000000..197f6f5 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetSystemDirectoryW.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern uint GetSystemDirectoryW(ref char lpBuffer, uint uSize); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetSystemInfo.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetSystemInfo.cs new file mode 100644 index 0000000..cbf07ea --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetSystemInfo.cs @@ -0,0 +1,15 @@ +// 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 + { + [DllImport(Libraries.Kernel32)] + internal extern static void GetSystemInfo(out SYSTEM_INFO lpSystemInfo); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetVersionExW.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetVersionExW.cs new file mode 100644 index 0000000..11c9143 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.GetVersionExW.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern bool GetVersionExW(ref OSVERSIONINFOEX osvi); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal unsafe struct OSVERSIONINFOEX + { + public int dwOSVersionInfoSize; + public int dwMajorVersion; + public int dwMinorVersion; + public int dwBuildNumber; + public int dwPlatformId; + public fixed char szCSDVersion[128]; + public ushort wServicePackMajor; + public ushort wServicePackMinor; + public ushort wSuiteMask; + public byte wProductType; + public byte wReserved; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.IsWow64Process_IntPtr.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.IsWow64Process_IntPtr.cs new file mode 100644 index 0000000..7953da6 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.IsWow64Process_IntPtr.cs @@ -0,0 +1,15 @@ +// 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 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool IsWow64Process(IntPtr hProcess, out bool Wow64Process); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.SYSTEM_INFO.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.SYSTEM_INFO.cs new file mode 100644 index 0000000..16e5bfd --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.SYSTEM_INFO.cs @@ -0,0 +1,38 @@ +// 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 + { + [StructLayout(LayoutKind.Sequential)] + internal struct SYSTEM_INFO + { + internal ushort wProcessorArchitecture; + internal ushort wReserved; + internal int dwPageSize; + internal IntPtr lpMinimumApplicationAddress; + internal IntPtr lpMaximumApplicationAddress; + internal IntPtr dwActiveProcessorMask; + internal int dwNumberOfProcessors; + internal int dwProcessorType; + internal int dwAllocationGranularity; + internal short wProcessorLevel; + internal short wProcessorRevision; + } + + internal enum ProcessorArchitecture : ushort + { + Processor_Architecture_INTEL = 0, + Processor_Architecture_ARM = 5, + Processor_Architecture_IA64 = 6, + Processor_Architecture_AMD64 = 9, + Processor_Architecture_ARM64 = 12, + Processor_Architecture_UNKNOWN = 0xFFFF + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.SetCurrentDirectory.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.SetCurrentDirectory.cs new file mode 100644 index 0000000..b30e5d7 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.SetCurrentDirectory.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "SetCurrentDirectoryW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] + internal static extern bool SetCurrentDirectory(string path); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Secur32/Interop.GetUserNameExW.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Secur32/Interop.GetUserNameExW.cs new file mode 100644 index 0000000..cf0dd5f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Secur32/Interop.GetUserNameExW.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Secur32 + { + [DllImport(Libraries.Secur32, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern BOOLEAN GetUserNameExW(int NameFormat, ref char lpNameBuffer, ref uint lpnSize); + + internal const int NameSamCompatible = 2; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/Shell32/Interop.SHGetKnownFolderPath.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Shell32/Interop.SHGetKnownFolderPath.cs new file mode 100644 index 0000000..1090805 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/Shell32/Interop.SHGetKnownFolderPath.cs @@ -0,0 +1,320 @@ +// 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 Shell32 + { + internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539); + + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb762188.aspx + [DllImport(Libraries.Shell32, CharSet = CharSet.Unicode, SetLastError = false, BestFitMapping = false, ExactSpelling = true)] + internal static extern int SHGetKnownFolderPath( + [MarshalAs(UnmanagedType.LPStruct)] Guid rfid, + uint dwFlags, + IntPtr hToken, + out string ppszPath); + + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx + internal static class KnownFolders + { + /// + /// (CSIDL_ADMINTOOLS) Per user Administrative Tools + /// "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Administrative Tools" + /// + internal const string AdminTools = "{724EF170-A42D-4FEF-9F26-B60E846FBA4F}"; + + /// + /// (CSIDL_CDBURN_AREA) Temporary Burn folder + /// "%LOCALAPPDATA%\Microsoft\Windows\Burn\Burn" + /// + internal const string CDBurning = "{9E52AB10-F80D-49DF-ACB8-4330F5687855}"; + + /// + /// (CSIDL_COMMON_ADMINTOOLS) Common Administrative Tools + /// "%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Administrative Tools" + /// + internal const string CommonAdminTools = "{D0384E7D-BAC3-4797-8F14-CBA229B392B5}"; + + /// + /// (CSIDL_COMMON_OEM_LINKS) OEM Links folder + /// "%ALLUSERSPROFILE%\OEM Links" + /// + internal const string CommonOEMLinks = "{C1BAE2D0-10DF-4334-BEDD-7AA20B227A9D}"; + + /// + /// (CSIDL_COMMON_PROGRAMS) Common Programs folder + /// "%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs" + /// + internal const string CommonPrograms = "{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}"; + + /// + /// (CSIDL_COMMON_STARTMENU) Common Start Menu folder + /// "%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu" + /// + internal const string CommonStartMenu = "{A4115719-D62E-491D-AA7C-E74B8BE3B067}"; + + /// + /// (CSIDL_COMMON_STARTUP, CSIDL_COMMON_ALTSTARTUP) Common Startup folder + /// "%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\StartUp" + /// + internal const string CommonStartup = "{82A5EA35-D9CD-47C5-9629-E15D2F714E6E}"; + + /// + /// (CSIDL_COMMON_TEMPLATES) Common Templates folder + /// "%ALLUSERSPROFILE%\Microsoft\Windows\Templates" + /// + internal const string CommonTemplates = "{B94237E7-57AC-4347-9151-B08C6C32D1F7}"; + + /// + /// (CSIDL_DRIVES) Computer virtual folder + /// + internal const string ComputerFolder = "{0AC0837C-BBF8-452A-850D-79D08E667CA7}"; + + /// + /// (CSIDL_CONNECTIONS) Network Connections virtual folder + /// + internal const string ConnectionsFolder = "{6F0CD92B-2E97-45D1-88FF-B0D186B8DEDD}"; + + /// + /// (CSIDL_CONTROLS) Control Panel virtual folder + /// + internal const string ControlPanelFolder = "{82A74AEB-AEB4-465C-A014-D097EE346D63}"; + + /// + /// (CSIDL_COOKIES) Cookies folder + /// "%APPDATA%\Microsoft\Windows\Cookies" + /// + internal const string Cookies = "{2B0F765D-C0E9-4171-908E-08A611B84FF6}"; + + /// + /// (CSIDL_DESKTOP, CSIDL_DESKTOPDIRECTORY) Desktop folder + /// "%USERPROFILE%\Desktop" + /// + internal const string Desktop = "{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}"; + + /// + /// (CSIDL_MYDOCUMENTS, CSIDL_PERSONAL) Documents (My Documents) folder + /// "%USERPROFILE%\Documents" + /// + internal const string Documents = "{FDD39AD0-238F-46AF-ADB4-6C85480369C7}"; + + /// + /// (CSIDL_FAVORITES, CSIDL_COMMON_FAVORITES) Favorites folder + /// "%USERPROFILE%\Favorites" + /// + internal const string Favorites = "{1777F761-68AD-4D8A-87BD-30B759FA33DD}"; + + /// + /// (CSIDL_FONTS) Fonts folder + /// "%windir%\Fonts" + /// + internal const string Fonts = "{FD228CB7-AE11-4AE3-864C-16F3910AB8FE}"; + + /// + /// (CSIDL_HISTORY) History folder + /// "%LOCALAPPDATA%\Microsoft\Windows\History" + /// + internal const string History = "{D9DC8A3B-B784-432E-A781-5A1130A75963}"; + + /// + /// (CSIDL_INTERNET_CACHE) Temporary Internet Files folder + /// "%LOCALAPPDATA%\Microsoft\Windows\Temporary Internet Files" + /// + internal const string InternetCache = "{352481E8-33BE-4251-BA85-6007CAEDCF9D}"; + + /// + /// (CSIDL_INTERNET) The Internet virtual folder + /// + internal const string InternetFolder = "{4D9F7874-4E0C-4904-967B-40B0D20C3E4B}"; + + /// + /// (CSIDL_LOCAL_APPDATA) Local folder + /// "%LOCALAPPDATA%" ("%USERPROFILE%\AppData\Local") + /// + internal const string LocalAppData = "{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"; + + /// + /// (CSIDL_RESOURCES_LOCALIZED) Fixed localized resources folder + /// "%windir%\resources\0409" (per active codepage) + /// + internal const string LocalizedResourcesDir = "{2A00375E-224C-49DE-B8D1-440DF7EF3DDC}"; + + /// + /// (CSIDL_MYMUSIC) Music folder + /// "%USERPROFILE%\Music" + /// + internal const string Music = "{4BD8D571-6D19-48D3-BE97-422220080E43}"; + + /// + /// (CSIDL_NETHOOD) Network shortcuts folder "%APPDATA%\Microsoft\Windows\Network Shortcuts" + /// + internal const string NetHood = "{C5ABBF53-E17F-4121-8900-86626FC2C973}"; + + /// + /// (CSIDL_NETWORK, CSIDL_COMPUTERSNEARME) Network virtual folder + /// + internal const string NetworkFolder = "{D20BEEC4-5CA8-4905-AE3B-BF251EA09B53}"; + + /// + /// (CSIDL_MYPICTURES) Pictures folder "%USERPROFILE%\Pictures" + /// + internal const string Pictures = "{33E28130-4E1E-4676-835A-98395C3BC3BB}"; + + /// + /// (CSIDL_PRINTERS) Printers virtual folder + /// + internal const string PrintersFolder = "{76FC4E2D-D6AD-4519-A663-37BD56068185}"; + + /// + /// (CSIDL_PRINTHOOD) Printer Shortcuts folder + /// "%APPDATA%\Microsoft\Windows\Printer Shortcuts" + /// + internal const string PrintHood = "{9274BD8D-CFD1-41C3-B35E-B13F55A758F4}"; + + /// + /// (CSIDL_PROFILE) The root users profile folder "%USERPROFILE%" + /// ("%SystemDrive%\Users\%USERNAME%") + /// + internal const string Profile = "{5E6C858F-0E22-4760-9AFE-EA3317B67173}"; + + /// + /// (CSIDL_COMMON_APPDATA) ProgramData folder + /// "%ALLUSERSPROFILE%" ("%ProgramData%", "%SystemDrive%\ProgramData") + /// + internal const string ProgramData = "{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}"; + + /// + /// (CSIDL_PROGRAM_FILES) Program Files folder for the current process architecture + /// "%ProgramFiles%" ("%SystemDrive%\Program Files") + /// + internal const string ProgramFiles = "{905e63b6-c1bf-494e-b29c-65b732d3d21a}"; + + /// + /// (CSIDL_PROGRAM_FILESX86) 32 bit Program Files folder (available to both 32/64 bit processes) + /// + internal const string ProgramFilesX86 = "{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}"; + + /// + /// (CSIDL_PROGRAM_FILES_COMMON) Common Program Files folder for the current process architecture + /// "%ProgramFiles%\Common Files" + /// + internal const string ProgramFilesCommon = "{F7F1ED05-9F6D-47A2-AAAE-29D317C6F066}"; + + /// + /// (CSIDL_PROGRAM_FILES_COMMONX86) Common 32 bit Program Files folder (available to both 32/64 bit processes) + /// + internal const string ProgramFilesCommonX86 = "{DE974D24-D9C6-4D3E-BF91-F4455120B917}"; + + /// + /// (CSIDL_PROGRAMS) Start menu Programs folder + /// "%APPDATA%\Microsoft\Windows\Start Menu\Programs" + /// + internal const string Programs = "{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}"; + + /// + /// (CSIDL_COMMON_DESKTOPDIRECTORY) Public Desktop folder + /// "%PUBLIC%\Desktop" + /// + internal const string PublicDesktop = "{C4AA340D-F20F-4863-AFEF-F87EF2E6BA25}"; + + /// + /// (CSIDL_COMMON_DOCUMENTS) Public Documents folder + /// "%PUBLIC%\Documents" + /// + internal const string PublicDocuments = "{ED4824AF-DCE4-45A8-81E2-FC7965083634}"; + + /// + /// (CSIDL_COMMON_MUSIC) Public Music folder + /// "%PUBLIC%\Music" + /// + internal const string PublicMusic = "{3214FAB5-9757-4298-BB61-92A9DEAA44FF}"; + + /// + /// (CSIDL_COMMON_PICTURES) Public Pictures folder + /// "%PUBLIC%\Pictures" + /// + internal const string PublicPictures = "{B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5}"; + + /// + /// (CSIDL_COMMON_VIDEO) Public Videos folder + /// "%PUBLIC%\Videos" + /// + internal const string PublicVideos = "{2400183A-6185-49FB-A2D8-4A392A602BA3}"; + + /// + /// (CSIDL_RECENT) Recent Items folder + /// "%APPDATA%\Microsoft\Windows\Recent" + /// + internal const string Recent = "{AE50C081-EBD2-438A-8655-8A092E34987A}"; + + /// + /// (CSIDL_BITBUCKET) Recycle Bin virtual folder + /// + internal const string RecycleBinFolder = "{B7534046-3ECB-4C18-BE4E-64CD4CB7D6AC}"; + + /// + /// (CSIDL_RESOURCES) Resources fixed folder + /// "%windir%\Resources" + /// + internal const string ResourceDir = "{8AD10C31-2ADB-4296-A8F7-E4701232C972}"; + + /// + /// (CSIDL_APPDATA) Roaming user application data folder + /// "%APPDATA%" ("%USERPROFILE%\AppData\Roaming") + /// + internal const string RoamingAppData = "{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}"; + + /// + /// (CSIDL_SENDTO) SendTo folder + /// "%APPDATA%\Microsoft\Windows\SendTo" + /// + internal const string SendTo = "{8983036C-27C0-404B-8F08-102D10DCFD74}"; + + /// + /// (CSIDL_STARTMENU) Start Menu folder + /// "%APPDATA%\Microsoft\Windows\Start Menu" + /// + internal const string StartMenu = "{625B53C3-AB48-4EC1-BA1F-A1EF4146FC19}"; + + /// + /// (CSIDL_STARTUP, CSIDL_ALTSTARTUP) Startup folder + /// "%APPDATA%\Microsoft\Windows\Start Menu\Programs\StartUp" + /// + internal const string Startup = "{B97D20BB-F46A-4C97-BA10-5E3608430854}"; + + /// + /// (CSIDL_SYSTEM) System32 folder + /// "%windir%\system32" + /// + internal const string System = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}"; + + /// + /// (CSIDL_SYSTEMX86) X86 System32 folder + /// "%windir%\system32" or "%windir%\syswow64" + /// + internal const string SystemX86 = "{D65231B0-B2F1-4857-A4CE-A8E7C6EA7D27}"; + + /// + /// (CSIDL_TEMPLATES) Templates folder + /// "%APPDATA%\Microsoft\Windows\Templates" + /// + internal const string Templates = "{A63293E8-664E-48DB-A079-DF759E0509F7}"; + + /// + /// (CSIDL_MYVIDEO) Videos folder + /// "%USERPROFILE%\Videos" + /// + internal const string Videos = "{18989B1D-99B5-455B-841C-AB7C74E4DDFC}"; + + /// + /// (CSIDL_WINDOWS) Windows folder "%windir%" + /// + internal const string Windows = "{F38BF404-1D43-42F2-9305-67DE0B28FC23}"; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.SpecialFolder.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.SpecialFolder.cs new file mode 100644 index 0000000..a5a4d2a --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.SpecialFolder.cs @@ -0,0 +1,115 @@ +// 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. + +namespace System +{ + public static partial class Environment + { + public enum SpecialFolder + { + ApplicationData = SpecialFolderValues.CSIDL_APPDATA, + CommonApplicationData = SpecialFolderValues.CSIDL_COMMON_APPDATA, + LocalApplicationData = SpecialFolderValues.CSIDL_LOCAL_APPDATA, + Cookies = SpecialFolderValues.CSIDL_COOKIES, + Desktop = SpecialFolderValues.CSIDL_DESKTOP, + Favorites = SpecialFolderValues.CSIDL_FAVORITES, + History = SpecialFolderValues.CSIDL_HISTORY, + InternetCache = SpecialFolderValues.CSIDL_INTERNET_CACHE, + Programs = SpecialFolderValues.CSIDL_PROGRAMS, + MyComputer = SpecialFolderValues.CSIDL_DRIVES, + MyMusic = SpecialFolderValues.CSIDL_MYMUSIC, + MyPictures = SpecialFolderValues.CSIDL_MYPICTURES, + MyVideos = SpecialFolderValues.CSIDL_MYVIDEO, + Recent = SpecialFolderValues.CSIDL_RECENT, + SendTo = SpecialFolderValues.CSIDL_SENDTO, + StartMenu = SpecialFolderValues.CSIDL_STARTMENU, + Startup = SpecialFolderValues.CSIDL_STARTUP, + System = SpecialFolderValues.CSIDL_SYSTEM, + Templates = SpecialFolderValues.CSIDL_TEMPLATES, + DesktopDirectory = SpecialFolderValues.CSIDL_DESKTOPDIRECTORY, + Personal = SpecialFolderValues.CSIDL_PERSONAL, + MyDocuments = SpecialFolderValues.CSIDL_PERSONAL, + ProgramFiles = SpecialFolderValues.CSIDL_PROGRAM_FILES, + CommonProgramFiles = SpecialFolderValues.CSIDL_PROGRAM_FILES_COMMON, + AdminTools = SpecialFolderValues.CSIDL_ADMINTOOLS, + CDBurning = SpecialFolderValues.CSIDL_CDBURN_AREA, + CommonAdminTools = SpecialFolderValues.CSIDL_COMMON_ADMINTOOLS, + CommonDocuments = SpecialFolderValues.CSIDL_COMMON_DOCUMENTS, + CommonMusic = SpecialFolderValues.CSIDL_COMMON_MUSIC, + CommonOemLinks = SpecialFolderValues.CSIDL_COMMON_OEM_LINKS, + CommonPictures = SpecialFolderValues.CSIDL_COMMON_PICTURES, + CommonStartMenu = SpecialFolderValues.CSIDL_COMMON_STARTMENU, + CommonPrograms = SpecialFolderValues.CSIDL_COMMON_PROGRAMS, + CommonStartup = SpecialFolderValues.CSIDL_COMMON_STARTUP, + CommonDesktopDirectory = SpecialFolderValues.CSIDL_COMMON_DESKTOPDIRECTORY, + CommonTemplates = SpecialFolderValues.CSIDL_COMMON_TEMPLATES, + CommonVideos = SpecialFolderValues.CSIDL_COMMON_VIDEO, + Fonts = SpecialFolderValues.CSIDL_FONTS, + NetworkShortcuts = SpecialFolderValues.CSIDL_NETHOOD, + PrinterShortcuts = SpecialFolderValues.CSIDL_PRINTHOOD, + UserProfile = SpecialFolderValues.CSIDL_PROFILE, + CommonProgramFilesX86 = SpecialFolderValues.CSIDL_PROGRAM_FILES_COMMONX86, + ProgramFilesX86 = SpecialFolderValues.CSIDL_PROGRAM_FILESX86, + Resources = SpecialFolderValues.CSIDL_RESOURCES, + LocalizedResources = SpecialFolderValues.CSIDL_RESOURCES_LOCALIZED, + SystemX86 = SpecialFolderValues.CSIDL_SYSTEMX86, + Windows = SpecialFolderValues.CSIDL_WINDOWS, + } + + // These values are specific to Windows and are known to SHGetFolderPath, however they are + // also the values used in the SpecialFolder enum. As such, we keep them as constants + // with their Win32 names, but keep them here rather than in Interop.Kernel32 as they're + // used on all platforms. + private static class SpecialFolderValues + { + internal const int CSIDL_APPDATA = 0x001a; + internal const int CSIDL_COMMON_APPDATA = 0x0023; + internal const int CSIDL_LOCAL_APPDATA = 0x001c; + internal const int CSIDL_COOKIES = 0x0021; + internal const int CSIDL_FAVORITES = 0x0006; + internal const int CSIDL_HISTORY = 0x0022; + internal const int CSIDL_INTERNET_CACHE = 0x0020; + internal const int CSIDL_PROGRAMS = 0x0002; + internal const int CSIDL_RECENT = 0x0008; + internal const int CSIDL_SENDTO = 0x0009; + internal const int CSIDL_STARTMENU = 0x000b; + internal const int CSIDL_STARTUP = 0x0007; + internal const int CSIDL_SYSTEM = 0x0025; + internal const int CSIDL_TEMPLATES = 0x0015; + internal const int CSIDL_DESKTOPDIRECTORY = 0x0010; + internal const int CSIDL_PERSONAL = 0x0005; + internal const int CSIDL_PROGRAM_FILES = 0x0026; + internal const int CSIDL_PROGRAM_FILES_COMMON = 0x002b; + internal const int CSIDL_DESKTOP = 0x0000; + internal const int CSIDL_DRIVES = 0x0011; + internal const int CSIDL_MYMUSIC = 0x000d; + internal const int CSIDL_MYPICTURES = 0x0027; + + internal const int CSIDL_ADMINTOOLS = 0x0030; // \Start Menu\Programs\Administrative Tools + internal const int CSIDL_CDBURN_AREA = 0x003b; // USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning + internal const int CSIDL_COMMON_ADMINTOOLS = 0x002f; // All Users\Start Menu\Programs\Administrative Tools + internal const int CSIDL_COMMON_DOCUMENTS = 0x002e; // All Users\Documents + internal const int CSIDL_COMMON_MUSIC = 0x0035; // All Users\My Music + internal const int CSIDL_COMMON_OEM_LINKS = 0x003a; // Links to All Users OEM specific apps + internal const int CSIDL_COMMON_PICTURES = 0x0036; // All Users\My Pictures + internal const int CSIDL_COMMON_STARTMENU = 0x0016; // All Users\Start Menu + internal const int CSIDL_COMMON_PROGRAMS = 0X0017; // All Users\Start Menu\Programs + internal const int CSIDL_COMMON_STARTUP = 0x0018; // All Users\Startup + internal const int CSIDL_COMMON_DESKTOPDIRECTORY = 0x0019; // All Users\Desktop + internal const int CSIDL_COMMON_TEMPLATES = 0x002d; // All Users\Templates + internal const int CSIDL_COMMON_VIDEO = 0x0037; // All Users\My Video + internal const int CSIDL_FONTS = 0x0014; // windows\fonts + internal const int CSIDL_MYVIDEO = 0x000e; // "My Videos" folder + internal const int CSIDL_NETHOOD = 0x0013; // %APPDATA%\Microsoft\Windows\Network Shortcuts + internal const int CSIDL_PRINTHOOD = 0x001b; // %APPDATA%\Microsoft\Windows\Printer Shortcuts + internal const int CSIDL_PROFILE = 0x0028; // %USERPROFILE% (%SystemDrive%\Users\%USERNAME%) + internal const int CSIDL_PROGRAM_FILES_COMMONX86 = 0x002c; // x86 Program Files\Common on RISC + internal const int CSIDL_PROGRAM_FILESX86 = 0x002a; // x86 C:\Program Files on RISC + internal const int CSIDL_RESOURCES = 0x0038; // %windir%\Resources + internal const int CSIDL_RESOURCES_LOCALIZED = 0x0039; // %windir%\resources\0409 (code page) + internal const int CSIDL_SYSTEMX86 = 0x0029; // %windir%\system32 + internal const int CSIDL_WINDOWS = 0x0024; // GetWindowsDirectory() + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.SpecialFolderOption.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.SpecialFolderOption.cs new file mode 100644 index 0000000..cd0e571 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.SpecialFolderOption.cs @@ -0,0 +1,33 @@ +// 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. + +namespace System +{ + public static partial class Environment + { + public enum SpecialFolderOption + { + None = 0, + Create = SpecialFolderOptionValues.CSIDL_FLAG_CREATE, + DoNotVerify = SpecialFolderOptionValues.CSIDL_FLAG_DONT_VERIFY, + } + + // These values are specific to Windows and are known to SHGetFolderPath, however they are + // also the values used in the SpecialFolderOption enum. As such, we keep them as constants + // with their Win32 names, but keep them here rather than in Interop.Kernel32 as they're + // used on all platforms. + private static class SpecialFolderOptionValues + { + /// + /// Force folder creation in SHGetFolderPath. Equivalent of KF_FLAG_CREATE (0x00008000). + /// + internal const int CSIDL_FLAG_CREATE = 0x8000; + + /// + /// Return an unverified folder path. Equivalent of KF_FLAG_DONT_VERIFY (0x00004000). + /// + internal const int CSIDL_FLAG_DONT_VERIFY = 0x4000; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs new file mode 100644 index 0000000..e18b595 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -0,0 +1,449 @@ +// 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 Internal.Runtime.Augments; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace System +{ + public static partial class Environment + { + internal static readonly bool IsMac = Interop.Sys.GetUnixName() == "OSX"; + private static Func s_directoryCreateDirectory; + + private static string CurrentDirectoryCore + { + get { return Interop.Sys.GetCwd(); } + set { Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirectory: true); } + } + + public static int ExitCode { get { return EnvironmentAugments.ExitCode; } set { EnvironmentAugments.ExitCode = value; } } + + private static string ExpandEnvironmentVariablesCore(string name) + { + Span initialBuffer = stackalloc char[128]; + var result = new ValueStringBuilder(initialBuffer); + + int lastPos = 0, pos; + while (lastPos < name.Length && (pos = name.IndexOf('%', lastPos + 1)) >= 0) + { + if (name[lastPos] == '%') + { + string key = name.Substring(lastPos + 1, pos - lastPos - 1); + string value = GetEnvironmentVariable(key); + if (value != null) + { + result.Append(value); + lastPos = pos + 1; + continue; + } + } + result.Append(name.AsSpan(lastPos, pos - lastPos)); + lastPos = pos; + } + result.Append(name.AsSpan(lastPos)); + + return result.ToString(); + } + + private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option) + { + // Get the path for the SpecialFolder + string path = GetFolderPathCoreWithoutValidation(folder); + Debug.Assert(path != null); + + // If we didn't get one, or if we got one but we're not supposed to verify it, + // or if we're supposed to verify it and it passes verification, return the path. + if (path.Length == 0 || + option == SpecialFolderOption.DoNotVerify || + Interop.Sys.Access(path, Interop.Sys.AccessMode.R_OK) == 0) + { + return path; + } + + // Failed verification. If None, then we're supposed to return an empty string. + // If Create, we're supposed to create it and then return the path. + if (option == SpecialFolderOption.None) + { + return string.Empty; + } + else + { + Debug.Assert(option == SpecialFolderOption.Create); + + // TODO #11151: Replace with Directory.CreateDirectory once we have access to System.IO.FileSystem here. + Func createDirectory = LazyInitializer.EnsureInitialized(ref s_directoryCreateDirectory, () => + { + Type dirType = Type.GetType("System.IO.Directory, System.IO.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: true); + MethodInfo mi = dirType.GetTypeInfo().GetDeclaredMethod("CreateDirectory"); + return (Func)mi.CreateDelegate(typeof(Func)); + }); + createDirectory(path); + + return path; + } + } + + private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder) + { + // First handle any paths that involve only static paths, avoiding the overheads of getting user-local paths. + // https://www.freedesktop.org/software/systemd/man/file-hierarchy.html + switch (folder) + { + case SpecialFolder.CommonApplicationData: return "/usr/share"; + case SpecialFolder.CommonTemplates: return "/usr/share/templates"; + } + if (IsMac) + { + switch (folder) + { + case SpecialFolder.ProgramFiles: return "/Applications"; + case SpecialFolder.System: return "/System"; + } + } + + // All other paths are based on the XDG Base Directory Specification: + // https://specifications.freedesktop.org/basedir-spec/latest/ + string home = null; + try + { + home = PersistedFiles.GetHomeDirectory(); + } + catch (Exception exc) + { + Debug.Fail($"Unable to get home directory: {exc}"); + } + + // Fall back to '/' when we can't determine the home directory. + // This location isn't writable by non-root users which provides some safeguard + // that the application doesn't write data which is meant to be private. + if (string.IsNullOrEmpty(home)) + { + home = "/"; + } + + // TODO: Consider caching (or precomputing and caching) all subsequent results. + // This would significantly improve performance for repeated access, at the expense + // of not being responsive to changes in the underlying environment variables, + // configuration files, etc. + + switch (folder) + { + case SpecialFolder.UserProfile: + case SpecialFolder.MyDocuments: // same value as Personal + return home; + case SpecialFolder.ApplicationData: + return GetXdgConfig(home); + case SpecialFolder.LocalApplicationData: + // "$XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored." + // "If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used." + string data = GetEnvironmentVariable("XDG_DATA_HOME"); + if (string.IsNullOrEmpty(data) || data[0] != '/') + { + data = Path.Combine(home, ".local", "share"); + } + return data; + + case SpecialFolder.Desktop: + case SpecialFolder.DesktopDirectory: + return ReadXdgDirectory(home, "XDG_DESKTOP_DIR", "Desktop"); + case SpecialFolder.Templates: + return ReadXdgDirectory(home, "XDG_TEMPLATES_DIR", "Templates"); + case SpecialFolder.MyVideos: + return ReadXdgDirectory(home, "XDG_VIDEOS_DIR", "Videos"); + + case SpecialFolder.MyMusic: + return 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"); + case SpecialFolder.Fonts: + return IsMac ? Path.Combine(home, "Library", "Fonts") : Path.Combine(home, ".fonts"); + + case SpecialFolder.Favorites: + if (IsMac) return Path.Combine(home, "Library", "Favorites"); + break; + case SpecialFolder.InternetCache: + if (IsMac) return Path.Combine(home, "Library", "Caches"); + break; + } + + // No known path for the SpecialFolder + return string.Empty; + } + + private static string GetXdgConfig(string home) + { + // "$XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored." + // "If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used." + string config = GetEnvironmentVariable("XDG_CONFIG_HOME"); + if (string.IsNullOrEmpty(config) || config[0] != '/') + { + config = Path.Combine(home, ".config"); + } + return config; + } + + private static string ReadXdgDirectory(string homeDir, string key, string fallback) + { + Debug.Assert(!string.IsNullOrEmpty(homeDir), $"Expected non-empty homeDir"); + Debug.Assert(!string.IsNullOrEmpty(key), $"Expected non-empty key"); + Debug.Assert(!string.IsNullOrEmpty(fallback), $"Expected non-empty fallback"); + + string envPath = GetEnvironmentVariable(key); + if (!string.IsNullOrEmpty(envPath) && envPath[0] == '/') + { + return envPath; + } + + // Use the user-dirs.dirs file to look up the right config. + // Note that the docs also highlight a list of directories in which to look for this file: + // "$XDG_CONFIG_DIRS defines the preference-ordered set of base directories to search for configuration files in addition + // to the $XDG_CONFIG_HOME base directory. The directories in $XDG_CONFIG_DIRS should be separated with a colon ':'. If + // $XDG_CONFIG_DIRS is either not set or empty, a value equal to / etc / xdg should be used." + // For simplicity, we don't currently do that. We can add it if/when necessary. + + string userDirsPath = Path.Combine(GetXdgConfig(homeDir), "user-dirs.dirs"); + if (Interop.Sys.Access(userDirsPath, Interop.Sys.AccessMode.R_OK) == 0) + { + try + { + using (var reader = new StreamReader(userDirsPath)) + { + string line; + while ((line = reader.ReadLine()) != null) + { + // Example lines: + // XDG_DESKTOP_DIR="$HOME/Desktop" + // XDG_PICTURES_DIR = "/absolute/path" + + // Skip past whitespace at beginning of line + int pos = 0; + SkipWhitespace(line, ref pos); + if (pos >= line.Length) continue; + + // Skip past requested key name + if (string.CompareOrdinal(line, pos, key, 0, key.Length) != 0) continue; + pos += key.Length; + + // Skip past whitespace and past '=' + SkipWhitespace(line, ref pos); + if (pos >= line.Length - 4 || line[pos] != '=') continue; // 4 for ="" and at least one char between quotes + pos++; // skip past '=' + + // Skip past whitespace and past first quote + SkipWhitespace(line, ref pos); + if (pos >= line.Length - 3 || line[pos] != '"') continue; // 3 for "" and at least one char between quotes + pos++; // skip past opening '"' + + // Skip past relative prefix if one exists + bool relativeToHome = false; + const string RelativeToHomePrefix = "$HOME/"; + if (string.CompareOrdinal(line, pos, RelativeToHomePrefix, 0, RelativeToHomePrefix.Length) == 0) + { + relativeToHome = true; + pos += RelativeToHomePrefix.Length; + } + else if (line[pos] != '/') // if not relative to home, must be absolute path + { + continue; + } + + // Find end of path + int endPos = line.IndexOf('"', pos); + if (endPos <= pos) continue; + + // Got we need. Now extract it. + string path = line.Substring(pos, endPos - pos); + return relativeToHome ? + Path.Combine(homeDir, path) : + path; + } + } + } + catch (Exception exc) + { + // assembly not found, file not found, errors reading file, etc. Just eat everything. + Debug.Fail($"Failed reading {userDirsPath}: {exc}"); + } + } + + return Path.Combine(homeDir, fallback); + } + + private static void SkipWhitespace(string line, ref int pos) + { + while (pos < line.Length && char.IsWhiteSpace(line[pos])) pos++; + } + + public static string[] GetLogicalDrives() => Interop.Sys.GetAllMountPoints(); + + private static bool Is64BitOperatingSystemWhen32BitProcess => false; + + public static string MachineName + { + get + { + string hostName = Interop.Sys.GetHostName(); + int dotPos = hostName.IndexOf('.'); + return dotPos == -1 ? hostName : hostName.Substring(0, dotPos); + } + } + + public static string NewLine => "\n"; + + private static readonly Lazy s_osVersion = new Lazy(() => GetOperatingSystem(Interop.Sys.GetUnixRelease())); + + private static OperatingSystem GetOperatingSystem(string release) + { + int major = 0, minor = 0, build = 0, revision = 0; + + // Parse the uname's utsname.release for the first four numbers found. + // This isn't perfect, but Version already doesn't map exactly to all possible release + // formats, e.g. 2.6.19-1.2895.fc6 + if (release != null) + { + int i = 0; + major = FindAndParseNextNumber(release, ref i); + minor = FindAndParseNextNumber(release, ref i); + build = FindAndParseNextNumber(release, ref i); + revision = FindAndParseNextNumber(release, ref i); + } + + // For compatibility reasons with Mono, PlatformID.Unix is returned on MacOSX. PlatformID.MacOSX + // is hidden from the editor and shouldn't be used. + return new OperatingSystem(PlatformID.Unix, new Version(major, minor, build, revision)); + } + + private static int FindAndParseNextNumber(string text, ref int pos) + { + // Move to the beginning of the number + for (; pos < text.Length; pos++) + { + char c = text[pos]; + if ('0' <= c && c <= '9') + { + break; + } + } + + // Parse the number; + int num = 0; + for (; pos < text.Length; pos++) + { + char c = text[pos]; + if ('0' > c || c > '9') + break; + + try + { + num = checked((num * 10) + (c - '0')); + } + // Integer overflow can occur for example with: + // Linux nelknet 4.15.0-24201807041620-generic + // To form a valid Version, num must be positive. + catch (OverflowException) + { + return int.MaxValue; + } + } + + return num; + } + + public static string SystemDirectory => GetFolderPathCore(SpecialFolder.System, SpecialFolderOption.None); + + public static int SystemPageSize => CheckedSysConf(Interop.Sys.SysConfName._SC_PAGESIZE); + + public static unsafe string UserName + { + get + { + // First try with a buffer that should suffice for 99% of cases. + string username; + const int BufLen = Interop.Sys.Passwd.InitialBufferSize; + byte* stackBuf = stackalloc byte[BufLen]; + if (TryGetUserNameFromPasswd(stackBuf, BufLen, out username)) + { + return username; + } + + // Fallback to heap allocations if necessary, growing the buffer until + // we succeed. TryGetUserNameFromPasswd will throw if there's an unexpected error. + int lastBufLen = BufLen; + while (true) + { + lastBufLen *= 2; + byte[] heapBuf = new byte[lastBufLen]; + fixed (byte* buf = &heapBuf[0]) + { + if (TryGetUserNameFromPasswd(buf, heapBuf.Length, out username)) + { + return username; + } + } + } + + } + } + + private static unsafe bool TryGetUserNameFromPasswd(byte* buf, int bufLen, out string path) + { + // Call getpwuid_r to get the passwd struct + Interop.Sys.Passwd passwd; + int error = Interop.Sys.GetPwUidR(Interop.Sys.GetEUid(), out passwd, buf, bufLen); + + // If the call succeeds, give back the user name retrieved + if (error == 0) + { + Debug.Assert(passwd.Name != null); + path = Marshal.PtrToStringAnsi((IntPtr)passwd.Name); + return true; + } + + // If the current user's entry could not be found, give back null, + // but still return true as false indicates the buffer was too small. + if (error == -1) + { + path = null; + return true; + } + + var errorInfo = new Interop.ErrorInfo(error); + + // If the call failed because the buffer was too small, return false to + // indicate the caller should try again with a larger buffer. + if (errorInfo.Error == Interop.Error.ERANGE) + { + path = null; + return false; + } + + // Otherwise, fail. + throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); + } + + public static string UserDomainName => MachineName; + + /// Invoke , throwing if it fails. + private static int CheckedSysConf(Interop.Sys.SysConfName name) + { + long result = Interop.Sys.SysConf(name); + if (result == -1) + { + Interop.ErrorInfo errno = Interop.Sys.GetLastErrorInfo(); + throw errno.Error == Interop.Error.EINVAL ? + new ArgumentOutOfRangeException(nameof(name), name, errno.GetErrorMessage()) : + Interop.GetIOException(errno); + } + return (int)result; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs new file mode 100644 index 0000000..3ac7663 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Win32.cs @@ -0,0 +1,284 @@ +// 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; +using System.Text; +using System.Runtime.InteropServices; + +namespace System +{ + public static partial class Environment + { + public static string UserName + { + get + { + // 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. + + // https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and + // https://msdn.microsoft.com/en-us/library/ms679635.aspx + + Span initialBuffer = stackalloc char[40]; + var builder = new ValueStringBuilder(initialBuffer); + GetUserName(ref builder); + + ReadOnlySpan name = builder.AsSpan(); + int index = name.IndexOf('\\'); + if (index != -1) + { + // In the form of DOMAIN\User, cut off DOMAIN\ + name = name.Slice(index + 1); + } + + return name.ToString(); + } + } + + private static void GetUserName(ref ValueStringBuilder builder) + { + uint size = 0; + while (Interop.Secur32.GetUserNameExW(Interop.Secur32.NameSamCompatible, ref builder.GetPinnableReference(), ref size) == Interop.BOOLEAN.FALSE) + { + if (Marshal.GetLastWin32Error() == Interop.Errors.ERROR_MORE_DATA) + { + builder.EnsureCapacity(checked((int)size)); + } + else + { + builder.Length = 0; + return; + } + } + + builder.Length = (int)size; + } + + public static string UserDomainName + { + get + { + // See the comment in UserName + Span initialBuffer = stackalloc char[40]; + var builder = new ValueStringBuilder(initialBuffer); + GetUserName(ref builder); + + ReadOnlySpan name = builder.AsSpan(); + int index = name.IndexOf('\\'); + if (index != -1) + { + // In the form of DOMAIN\User, cut off \User and return + return name.Slice(0, index).ToString(); + } + + // In theory we should never get use out of LookupAccountNameW as the above API should + // always return what we need. Can't find any clues in the historical sources, however. + + // Domain names aren't typically long. + // https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and + Span initialDomainNameBuffer = stackalloc char[64]; + var domainBuilder = new ValueStringBuilder(initialBuffer); + uint length = (uint)domainBuilder.Capacity; + + // This API will fail to return the domain name without a buffer for the SID. + // SIDs are never over 68 bytes long. + Span sid = stackalloc byte[68]; + uint sidLength = 68; + + while (!Interop.Advapi32.LookupAccountNameW(null, ref builder.GetPinnableReference(), ref MemoryMarshal.GetReference(sid), + ref sidLength, ref domainBuilder.GetPinnableReference(), ref length, out _)) + { + int error = Marshal.GetLastWin32Error(); + + // The docs don't call this out clearly, but experimenting shows that the error returned is the following. + if (error != Interop.Errors.ERROR_INSUFFICIENT_BUFFER) + { + throw new InvalidOperationException(Win32Marshal.GetMessage(error)); + } + + domainBuilder.EnsureCapacity((int)length); + } + + domainBuilder.Length = (int)length; + return domainBuilder.ToString(); + } + } + + private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option) + { + // We're using SHGetKnownFolderPath instead of SHGetFolderPath as SHGetFolderPath is + // capped at MAX_PATH. + // + // Because we validate both of the input enums we shouldn't have to care about CSIDL and flag + // definitions we haven't mapped. If we remove or loosen the checks we'd have to account + // for mapping here (this includes tweaking as SHGetFolderPath would do). + // + // The only SpecialFolderOption defines we have are equivalent to KnownFolderFlags. + + string folderGuid; + + switch (folder) + { + case SpecialFolder.ApplicationData: + folderGuid = Interop.Shell32.KnownFolders.RoamingAppData; + break; + case SpecialFolder.CommonApplicationData: + folderGuid = Interop.Shell32.KnownFolders.ProgramData; + break; + case SpecialFolder.LocalApplicationData: + folderGuid = Interop.Shell32.KnownFolders.LocalAppData; + break; + case SpecialFolder.Cookies: + folderGuid = Interop.Shell32.KnownFolders.Cookies; + break; + case SpecialFolder.Desktop: + folderGuid = Interop.Shell32.KnownFolders.Desktop; + break; + case SpecialFolder.Favorites: + folderGuid = Interop.Shell32.KnownFolders.Favorites; + break; + case SpecialFolder.History: + folderGuid = Interop.Shell32.KnownFolders.History; + break; + case SpecialFolder.InternetCache: + folderGuid = Interop.Shell32.KnownFolders.InternetCache; + break; + case SpecialFolder.Programs: + folderGuid = Interop.Shell32.KnownFolders.Programs; + break; + case SpecialFolder.MyComputer: + folderGuid = Interop.Shell32.KnownFolders.ComputerFolder; + break; + case SpecialFolder.MyMusic: + folderGuid = Interop.Shell32.KnownFolders.Music; + break; + case SpecialFolder.MyPictures: + folderGuid = Interop.Shell32.KnownFolders.Pictures; + break; + case SpecialFolder.MyVideos: + folderGuid = Interop.Shell32.KnownFolders.Videos; + break; + case SpecialFolder.Recent: + folderGuid = Interop.Shell32.KnownFolders.Recent; + break; + case SpecialFolder.SendTo: + folderGuid = Interop.Shell32.KnownFolders.SendTo; + break; + case SpecialFolder.StartMenu: + folderGuid = Interop.Shell32.KnownFolders.StartMenu; + break; + case SpecialFolder.Startup: + folderGuid = Interop.Shell32.KnownFolders.Startup; + break; + case SpecialFolder.System: + folderGuid = Interop.Shell32.KnownFolders.System; + break; + case SpecialFolder.Templates: + folderGuid = Interop.Shell32.KnownFolders.Templates; + break; + case SpecialFolder.DesktopDirectory: + folderGuid = Interop.Shell32.KnownFolders.Desktop; + break; + case SpecialFolder.Personal: + // Same as Personal + // case SpecialFolder.MyDocuments: + folderGuid = Interop.Shell32.KnownFolders.Documents; + break; + case SpecialFolder.ProgramFiles: + folderGuid = Interop.Shell32.KnownFolders.ProgramFiles; + break; + case SpecialFolder.CommonProgramFiles: + folderGuid = Interop.Shell32.KnownFolders.ProgramFilesCommon; + break; + case SpecialFolder.AdminTools: + folderGuid = Interop.Shell32.KnownFolders.AdminTools; + break; + case SpecialFolder.CDBurning: + folderGuid = Interop.Shell32.KnownFolders.CDBurning; + break; + case SpecialFolder.CommonAdminTools: + folderGuid = Interop.Shell32.KnownFolders.CommonAdminTools; + break; + case SpecialFolder.CommonDocuments: + folderGuid = Interop.Shell32.KnownFolders.PublicDocuments; + break; + case SpecialFolder.CommonMusic: + folderGuid = Interop.Shell32.KnownFolders.PublicMusic; + break; + case SpecialFolder.CommonOemLinks: + folderGuid = Interop.Shell32.KnownFolders.CommonOEMLinks; + break; + case SpecialFolder.CommonPictures: + folderGuid = Interop.Shell32.KnownFolders.PublicPictures; + break; + case SpecialFolder.CommonStartMenu: + folderGuid = Interop.Shell32.KnownFolders.CommonStartMenu; + break; + case SpecialFolder.CommonPrograms: + folderGuid = Interop.Shell32.KnownFolders.CommonPrograms; + break; + case SpecialFolder.CommonStartup: + folderGuid = Interop.Shell32.KnownFolders.CommonStartup; + break; + case SpecialFolder.CommonDesktopDirectory: + folderGuid = Interop.Shell32.KnownFolders.PublicDesktop; + break; + case SpecialFolder.CommonTemplates: + folderGuid = Interop.Shell32.KnownFolders.CommonTemplates; + break; + case SpecialFolder.CommonVideos: + folderGuid = Interop.Shell32.KnownFolders.PublicVideos; + break; + case SpecialFolder.Fonts: + folderGuid = Interop.Shell32.KnownFolders.Fonts; + break; + case SpecialFolder.NetworkShortcuts: + folderGuid = Interop.Shell32.KnownFolders.NetHood; + break; + case SpecialFolder.PrinterShortcuts: + folderGuid = Interop.Shell32.KnownFolders.PrintersFolder; + break; + case SpecialFolder.UserProfile: + folderGuid = Interop.Shell32.KnownFolders.Profile; + break; + case SpecialFolder.CommonProgramFilesX86: + folderGuid = Interop.Shell32.KnownFolders.ProgramFilesCommonX86; + break; + case SpecialFolder.ProgramFilesX86: + folderGuid = Interop.Shell32.KnownFolders.ProgramFilesX86; + break; + case SpecialFolder.Resources: + folderGuid = Interop.Shell32.KnownFolders.ResourceDir; + break; + case SpecialFolder.LocalizedResources: + folderGuid = Interop.Shell32.KnownFolders.LocalizedResourcesDir; + break; + case SpecialFolder.SystemX86: + folderGuid = Interop.Shell32.KnownFolders.SystemX86; + break; + case SpecialFolder.Windows: + folderGuid = Interop.Shell32.KnownFolders.Windows; + break; + default: + return string.Empty; + } + + return GetKnownFolderPath(folderGuid, option); + } + + private static string GetKnownFolderPath(string folderGuid, SpecialFolderOption option) + { + Guid folderId = new Guid(folderGuid); + + int hr = Interop.Shell32.SHGetKnownFolderPath(folderId, (uint)option, IntPtr.Zero, out string path); + if (hr != 0) // Not S_OK + { + return string.Empty; + } + + return path; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.WinRT.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.WinRT.cs new file mode 100644 index 0000000..c757bea --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.WinRT.cs @@ -0,0 +1,141 @@ +// 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 Windows.Foundation.Metadata; +using Windows.Storage; +using System.IO; + +namespace System +{ + public static partial class Environment + { + public static string UserName => "Windows User"; + public static string UserDomainName => "Windows Domain"; + + private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option) + { + // For testing we'll fall back if the needed APIs aren't present. + // + // We're not honoring the special folder options (noverify/create) for a few reasons. One, most of the + // folders always exist (e.g. it is moot). Two, most locations are inaccessible from an appcontainer + // currently - making it impossible to answer the question of existence or create if necessary. Thirdly, + // the Win32 API would create these folders with very specific ACLs, which even in the cases we can create + // are a significant compat risk (trying to replicate internal Windows behavior- it is documented that they + // set specific ACLs, but not which ones). + if (ApiInformation.IsTypePresent("Windows.Storage.UserDataPaths")) + { + return GetFolderPathCoreCurrent(folder); + } + else + { + return GetFolderPathCoreFallBack(folder); + } + } + + private static string GetFolderPathCoreCurrent(SpecialFolder folder) + { + // While all of these give back real paths, most of them are not accessible + // from an appcontainer currently (they will give access denied) + switch (folder) + { + case SpecialFolder.ApplicationData: + return UserDataPaths.GetDefault().RoamingAppData; + case SpecialFolder.CommonApplicationData: + return AppDataPaths.GetDefault().ProgramData; + case SpecialFolder.LocalApplicationData: + return AppDataPaths.GetDefault().LocalAppData; + case SpecialFolder.Cookies: + return AppDataPaths.GetDefault().Cookies; + case SpecialFolder.Desktop: + return AppDataPaths.GetDefault().Desktop; + case SpecialFolder.Favorites: + return AppDataPaths.GetDefault().Favorites; + case SpecialFolder.History: + return AppDataPaths.GetDefault().History; + case SpecialFolder.InternetCache: + return AppDataPaths.GetDefault().InternetCache; + case SpecialFolder.MyMusic: + return UserDataPaths.GetDefault().Music; + case SpecialFolder.MyPictures: + return UserDataPaths.GetDefault().Pictures; + case SpecialFolder.MyVideos: + return UserDataPaths.GetDefault().Videos; + case SpecialFolder.Recent: + return UserDataPaths.GetDefault().Recent; + case SpecialFolder.System: + return SystemDataPaths.GetDefault().System; + case SpecialFolder.Templates: + return UserDataPaths.GetDefault().Templates; + case SpecialFolder.DesktopDirectory: + return UserDataPaths.GetDefault().Desktop; + case SpecialFolder.Personal: + return UserDataPaths.GetDefault().Documents; + case SpecialFolder.CommonDocuments: + return SystemDataPaths.GetDefault().PublicDocuments; + case SpecialFolder.CommonMusic: + return SystemDataPaths.GetDefault().PublicMusic; + case SpecialFolder.CommonPictures: + return SystemDataPaths.GetDefault().PublicPictures; + case SpecialFolder.CommonDesktopDirectory: + return SystemDataPaths.GetDefault().PublicDesktop; + case SpecialFolder.CommonVideos: + return SystemDataPaths.GetDefault().PublicVideos; + case SpecialFolder.UserProfile: + return UserDataPaths.GetDefault().Profile; + case SpecialFolder.SystemX86: + return SystemDataPaths.GetDefault().SystemX86; + case SpecialFolder.Windows: + return SystemDataPaths.GetDefault().Windows; + + // The following aren't available on WinRT. Our default behavior + // is string.Empty for paths that aren't available. + // + // case SpecialFolder.Programs: + // case SpecialFolder.MyComputer: + // case SpecialFolder.SendTo: + // case SpecialFolder.StartMenu: + // case SpecialFolder.Startup: + // case SpecialFolder.ProgramFiles: + // case SpecialFolder.CommonProgramFiles: + // case SpecialFolder.AdminTools: + // case SpecialFolder.CDBurning: + // case SpecialFolder.CommonAdminTools: + // case SpecialFolder.CommonOemLinks: + // case SpecialFolder.CommonStartMenu: + // case SpecialFolder.CommonPrograms: + // case SpecialFolder.CommonStartup: + // case SpecialFolder.CommonTemplates: + // case SpecialFolder.Fonts: + // case SpecialFolder.NetworkShortcuts: + // case SpecialFolder.PrinterShortcuts: + // case SpecialFolder.CommonProgramFilesX86: + // case SpecialFolder.ProgramFilesX86: + // case SpecialFolder.Resources: + // case SpecialFolder.LocalizedResources: + + default: + return string.Empty; + } + } + + private static string GetFolderPathCoreFallBack(SpecialFolder folder) + { + // For testing without the new WinRT APIs. We cannot use Win32 APIs for + // special folders as they are not in the WACK. + switch (folder) + { + case SpecialFolder.ApplicationData: + return ApplicationData.Current.RoamingFolder?.Path; + case SpecialFolder.LocalApplicationData: + return ApplicationData.Current.LocalFolder?.Path; + case SpecialFolder.System: + return SystemDirectory; + case SpecialFolder.Windows: + return Path.GetDirectoryName(SystemDirectory); + default: + return string.Empty; + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs new file mode 100644 index 0000000..991c69c --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -0,0 +1,135 @@ +// 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; +using System.Runtime.InteropServices; +using System.Text; +using Internal.Runtime.Augments; + +namespace System +{ + public static partial class Environment + { + private static string CurrentDirectoryCore + { + get + { + Span initialBuffer = stackalloc char[Interop.Kernel32.MAX_PATH]; + var builder = new ValueStringBuilder(initialBuffer); + + uint length; + while ((length = Interop.Kernel32.GetCurrentDirectory((uint)builder.Capacity, ref builder.GetPinnableReference())) > builder.Capacity) + { + builder.EnsureCapacity((int)length); + } + + if (length == 0) + throw Win32Marshal.GetExceptionForLastWin32Error(); + + builder.Length = (int)length; + + // If we have a tilde in the path, make an attempt to expand 8.3 filenames + return builder.AsSpan().Contains('~') + ? PathHelper.TryExpandShortFileName(ref builder, null) + : builder.ToString(); + } + set + { + if (!Interop.Kernel32.SetCurrentDirectory(value)) + { + int errorCode = Marshal.GetLastWin32Error(); + throw Win32Marshal.GetExceptionForWin32Error( + errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND ? Interop.Errors.ERROR_PATH_NOT_FOUND : errorCode, + value); + } + } + } + + public static string[] GetLogicalDrives() => DriveInfoInternal.GetLogicalDrives(); + + public static string NewLine => "\r\n"; + + public static int SystemPageSize + { + get + { + Interop.Kernel32.GetSystemInfo(out Interop.Kernel32.SYSTEM_INFO info); + return info.dwPageSize; + } + } + + public static int ExitCode { get { return EnvironmentAugments.ExitCode; } set { EnvironmentAugments.ExitCode = value; } } + + private static string ExpandEnvironmentVariablesCore(string name) + { + Span initialBuffer = stackalloc char[128]; + var builder = new ValueStringBuilder(initialBuffer); + + uint length; + while ((length = Interop.Kernel32.ExpandEnvironmentStringsW(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) + { + builder.EnsureCapacity((int)length); + } + + if (length == 0) + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + + // length includes the null terminator + builder.Length = (int)length - 1; + return builder.ToString(); + } + + 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; + } + } + + private static readonly unsafe Lazy s_osVersion = new Lazy(() => + { + var version = new Interop.Kernel32.OSVERSIONINFOEX { dwOSVersionInfoSize = sizeof(Interop.Kernel32.OSVERSIONINFOEX) }; + if (!Interop.Kernel32.GetVersionExW(ref version)) + { + throw new InvalidOperationException(SR.InvalidOperation_GetVersion); + } + + return new OperatingSystem( + PlatformID.Win32NT, + new Version(version.dwMajorVersion, version.dwMinorVersion, version.dwBuildNumber, (version.wServicePackMajor << 16) | version.wServicePackMinor), + Marshal.PtrToStringUni((IntPtr)version.szCSDVersion)); + }); + + public static string SystemDirectory + { + get + { + // Normally this will be C:\Windows\System32 + Span initialBuffer = stackalloc char[32]; + var builder = new ValueStringBuilder(initialBuffer); + + uint length; + while ((length = Interop.Kernel32.GetSystemDirectoryW(ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) + { + builder.EnsureCapacity((int)length); + } + + if (length == 0) + throw Win32Marshal.GetExceptionForLastWin32Error(); + + builder.Length = (int)length; + return builder.ToString(); + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.cs new file mode 100644 index 0000000..6daccb8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.cs @@ -0,0 +1,193 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using Internal.Runtime.Augments; + +namespace System +{ + public static partial class Environment + { + public static string GetEnvironmentVariable(string variable) + { + return EnvironmentAugments.GetEnvironmentVariable(variable); + } + + public static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) + { + return EnvironmentAugments.GetEnvironmentVariable(variable, target); + } + + 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(); + } + + public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target) + { + // See comments in GetEnvironmentVariables() + return EnvironmentAugments.EnumerateEnvironmentVariables(target).ToHashtable(); + } + + private static Hashtable ToHashtable(this IEnumerable> pairs) + { + Hashtable hashTable = new Hashtable(); + foreach (KeyValuePair pair in pairs) + { + try + { + hashTable.Add(pair.Key, pair.Value); + } + catch (ArgumentException) + { + // Throw and catch intentionally to provide non-fatal notification about corrupted environment block + } + } + return hashTable; + } + + public static void SetEnvironmentVariable(string variable, string value) + { + EnvironmentAugments.SetEnvironmentVariable(variable, value); + } + + public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target) + { + EnvironmentAugments.SetEnvironmentVariable(variable, value, target); + } + + public static string CommandLine + { + get + { + return PasteArguments.Paste(GetCommandLineArgs(), pasteFirstArgumentUsingArgV0Rules: true); + } + } + + public static string CurrentDirectory + { + get { return 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(); + + 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); } + } + + public static long WorkingSet + { + get + { + // Use reflection to access the implementation in System.Diagnostics.Process.dll. While far from ideal, + // we do this to avoid duplicating the Windows, Linux, macOS, and potentially other platform-specific implementations + // present in Process. If it proves important, we could look at separating that functionality out of Process into + // Common files which could also be included here. + Type processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false); + IDisposable currentProcess = processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) as IDisposable; + if (currentProcess != null) + { + using (currentProcess) + { + object result = processType.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null); + if (result is long) return (long)result; + } + } + // Could not get the current working set. + return 0; + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DriveInfoInternal.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DriveInfoInternal.Unix.cs new file mode 100644 index 0000000..78ef957 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DriveInfoInternal.Unix.cs @@ -0,0 +1,15 @@ +// 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.Text; + +namespace System.IO +{ + /// Contains internal volume helpers that are shared between many projects. + internal static partial class DriveInfoInternal + { + internal static string[] GetLogicalDrives() => Interop.Sys.GetAllMountPoints(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DriveInfoInternal.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DriveInfoInternal.Windows.cs new file mode 100644 index 0000000..47dbcdd --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DriveInfoInternal.Windows.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Text; + +namespace System.IO +{ + /// Contains internal volume helpers that are shared between many projects. + internal static partial class DriveInfoInternal + { + public static string[] GetLogicalDrives() + { + int drives = Interop.Kernel32.GetLogicalDrives(); + if (drives == 0) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + + // GetLogicalDrives returns a bitmask starting from + // position 0 "A" indicating whether a drive is present. + // Loop over each bit, creating a string for each one + // that is set. + + uint d = (uint)drives; + int count = 0; + while (d != 0) + { + if (((int)d & 1) != 0) count++; + d >>= 1; + } + + string[] result = new string[count]; + Span root = stackalloc char[] { 'A', ':', '\\' }; + d = (uint)drives; + count = 0; + while (d != 0) + { + if (((int)d & 1) != 0) + { + result[count++] = root.ToString(); + } + d >>= 1; + root[0]++; + } + return result; + } + + public static string NormalizeDriveName(string driveName) + { + Debug.Assert(driveName != null); + + string name; + + if (driveName.Length == 1) + { + name = driveName + ":\\"; + } + else + { + name = Path.GetPathRoot(driveName); + // Disallow null or empty drive letters and UNC paths + if (string.IsNullOrEmpty(name) || name.StartsWith("\\\\", StringComparison.Ordinal)) + { + throw new ArgumentException(SR.Arg_MustBeDriveLetterOrRootDir, nameof(driveName)); + } + } + // We want to normalize to have a trailing backslash so we don't have two equivalent forms and + // because some Win32 API don't work without it. + if (name.Length == 2 && name[1] == ':') + { + name = name + "\\"; + } + + // Now verify that the drive letter could be a real drive name. + // On Windows this means it's between A and Z, ignoring case. + char letter = driveName[0]; + if (!((letter >= 'A' && letter <= 'Z') || (letter >= 'a' && letter <= 'z'))) + { + throw new ArgumentException(SR.Arg_MustBeDriveLetterOrRootDir, nameof(driveName)); + } + + return name; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Names.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Names.Unix.cs new file mode 100644 index 0000000..8984f1a --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Names.Unix.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.IO +{ + internal static partial class PersistedFiles + { + // Temporary data, /tmp/.dotnet/corefx + // User-persisted data, ~/.dotnet/corefx/ + // System-persisted data, /etc/dotnet/corefx/ + + internal const string TopLevelDirectory = "dotnet"; + internal const string TopLevelHiddenDirectory = "." + TopLevelDirectory; + internal const string SecondLevelDirectory = "corefx"; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Unix.cs new file mode 100644 index 0000000..d8064af --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Unix.cs @@ -0,0 +1,163 @@ +// 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.Runtime.InteropServices; + +namespace System.IO +{ + internal static partial class PersistedFiles + { + private static string s_userProductDirectory; + + /// + /// Get the location of where to persist information for a particular aspect of the framework, + /// such as "cryptography". + /// + /// The directory name for the feature + /// A path within the user's home directory for persisting data for the feature + internal static string GetUserFeatureDirectory(string featureName) + { + if (s_userProductDirectory == null) + { + EnsureUserDirectories(); + } + + return Path.Combine(s_userProductDirectory, featureName); + } + + /// + /// Get the location of where to persist information for a particular aspect of a feature of + /// the framework, such as "x509stores" within "cryptography". + /// + /// The directory name for the feature + /// The directory name for the sub-feature + /// A path within the user's home directory for persisting data for the sub-feature + internal static string GetUserFeatureDirectory(string featureName, string subFeatureName) + { + if (s_userProductDirectory == null) + { + EnsureUserDirectories(); + } + + return Path.Combine(s_userProductDirectory, featureName, subFeatureName); + } + + /// + /// Get the location of where to persist information for a particular aspect of the framework, + /// with a lot of hierarchy, such as ["cryptography", "x509stores", "my"] + /// + /// A non-empty set of directories to use for the storage hierarchy + /// A path within the user's home directory for persisting data for the feature + internal static string GetUserFeatureDirectory(params string[] featurePathParts) + { + Debug.Assert(featurePathParts != null); + Debug.Assert(featurePathParts.Length > 0); + + if (s_userProductDirectory == null) + { + EnsureUserDirectories(); + } + + return Path.Combine(s_userProductDirectory, Path.Combine(featurePathParts)); + } + + private static void EnsureUserDirectories() + { + string userHomeDirectory = GetHomeDirectory(); + + if (string.IsNullOrEmpty(userHomeDirectory)) + { + throw new InvalidOperationException(SR.PersistedFiles_NoHomeDirectory); + } + + s_userProductDirectory = Path.Combine( + userHomeDirectory, + TopLevelHiddenDirectory, + SecondLevelDirectory); + } + + /// Gets the current user's home directory. + /// The path to the home directory, or null if it could not be determined. + internal static string GetHomeDirectory() + { + // First try to get the user's home directory from the HOME environment variable. + // This should work in most cases. + string userHomeDirectory = Environment.GetEnvironmentVariable("HOME"); + if (!string.IsNullOrEmpty(userHomeDirectory)) + return userHomeDirectory; + + // In initialization conditions, however, the "HOME" environment variable may + // not yet be set. For such cases, consult with the password entry. + unsafe + { + // First try with a buffer that should suffice for 99% of cases. + // Note that, theoretically, userHomeDirectory may be null in the success case + // if we simply couldn't find a home directory for the current user. + // In that case, we pass back the null value and let the caller decide + // what to do. + const int BufLen = Interop.Sys.Passwd.InitialBufferSize; + byte* stackBuf = stackalloc byte[BufLen]; + if (TryGetHomeDirectoryFromPasswd(stackBuf, BufLen, out userHomeDirectory)) + return userHomeDirectory; + + // Fallback to heap allocations if necessary, growing the buffer until + // we succeed. TryGetHomeDirectory will throw if there's an unexpected error. + int lastBufLen = BufLen; + while (true) + { + lastBufLen *= 2; + byte[] heapBuf = new byte[lastBufLen]; + fixed (byte* buf = &heapBuf[0]) + { + if (TryGetHomeDirectoryFromPasswd(buf, heapBuf.Length, out userHomeDirectory)) + return userHomeDirectory; + } + } + } + } + + /// Wrapper for getpwuid_r. + /// The scratch buffer to pass into getpwuid_r. + /// The length of . + /// The resulting path; null if the user didn't have an entry. + /// true if the call was successful (path may still be null); false is a larger buffer is needed. + private static unsafe bool TryGetHomeDirectoryFromPasswd(byte* buf, int bufLen, out string path) + { + // Call getpwuid_r to get the passwd struct + Interop.Sys.Passwd passwd; + int error = Interop.Sys.GetPwUidR(Interop.Sys.GetEUid(), out passwd, buf, bufLen); + + // If the call succeeds, give back the home directory path retrieved + if (error == 0) + { + Debug.Assert(passwd.HomeDirectory != null); + path = Marshal.PtrToStringAnsi((IntPtr)passwd.HomeDirectory); + return true; + } + + // If the current user's entry could not be found, give back null + // path, but still return true as false indicates the buffer was + // too small. + if (error == -1) + { + path = null; + return true; + } + + var errorInfo = new Interop.ErrorInfo(error); + + // If the call failed because the buffer was too small, return false to + // indicate the caller should try again with a larger buffer. + if (errorInfo.Error == Interop.Error.ERANGE) + { + path = null; + return false; + } + + // Otherwise, fail. + throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs new file mode 100644 index 0000000..0fc9b0f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs @@ -0,0 +1,83 @@ +// 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.Runtime.Serialization; + +namespace System +{ + public sealed class OperatingSystem : ISerializable, ICloneable + { + private readonly Version _version; + private readonly PlatformID _platform; + private readonly string _servicePack; + private string _versionString; + + public OperatingSystem(PlatformID platform, Version version) : this(platform, version, null) + { + } + + internal OperatingSystem(PlatformID platform, Version version, string servicePack) + { + if (platform < PlatformID.Win32S || platform > PlatformID.MacOSX) + { + throw new ArgumentOutOfRangeException(nameof(platform), platform, SR.Format(SR.Arg_EnumIllegalVal, platform)); + } + + if (version == null) + { + throw new ArgumentNullException(nameof(version)); + } + + _platform = platform; + _version = version; + _servicePack = servicePack; + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + public PlatformID Platform => _platform; + + public string ServicePack => _servicePack ?? string.Empty; + + public Version Version => _version; + + public object Clone() => new OperatingSystem(_platform, _version, _servicePack); + + public override string ToString() => VersionString; + + public string VersionString + { + get + { + if (_versionString == null) + { + string os; + switch (_platform) + { + case PlatformID.Win32S: os = "Microsoft Win32S "; break; + case PlatformID.Win32Windows: os = (_version.Major > 4 || (_version.Major == 4 && _version.Minor > 0)) ? "Microsoft Windows 98 " : "Microsoft Windows 95 "; break; + case PlatformID.Win32NT: os = "Microsoft Windows NT "; break; + case PlatformID.WinCE: os = "Microsoft Windows CE "; break; + case PlatformID.Unix: os = "Unix "; break; + case PlatformID.Xbox: os = "Xbox "; break; + case PlatformID.MacOSX: os = "Mac OS X "; break; + default: + Debug.Fail($"Unknown platform {_platform}"); + os = " "; break; + } + + _versionString = string.IsNullOrEmpty(_servicePack) ? + os + _version.ToString() : + os + _version.ToString(3) + " " + _servicePack; + } + + return _versionString; + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/PasteArguments.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/PasteArguments.Unix.cs new file mode 100644 index 0000000..1a4d928 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/PasteArguments.Unix.cs @@ -0,0 +1,28 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; + +namespace System +{ + internal static partial class PasteArguments + { + /// + /// Repastes a set of arguments into a linear string that parses back into the originals under pre- or post-2008 VC parsing rules. + /// On Unix: the rules for parsing the executable name (argv[0]) are ignored. + /// + internal static string Paste(IEnumerable arguments, bool pasteFirstArgumentUsingArgV0Rules) + { + var stringBuilder = new StringBuilder(); + foreach (string argument in arguments) + { + AppendArgument(stringBuilder, argument); + } + return stringBuilder.ToString(); + } + + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/PasteArguments.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/PasteArguments.Windows.cs new file mode 100644 index 0000000..7cdcbc4 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/PasteArguments.Windows.cs @@ -0,0 +1,66 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; + +namespace System +{ + internal static partial class PasteArguments + { + /// + /// Repastes a set of arguments into a linear string that parses back into the originals under pre- or post-2008 VC parsing rules. + /// The rules for parsing the executable name (argv[0]) are special, so you must indicate whether the first argument actually is argv[0]. + /// + internal static string Paste(IEnumerable arguments, bool pasteFirstArgumentUsingArgV0Rules) + { + var stringBuilder = new StringBuilder(); + + foreach (string argument in arguments) + { + if (pasteFirstArgumentUsingArgV0Rules) + { + pasteFirstArgumentUsingArgV0Rules = false; + + // Special rules for argv[0] + // - Backslash is a normal character. + // - Quotes used to include whitespace characters. + // - Parsing ends at first whitespace outside quoted region. + // - No way to get a literal quote past the parser. + + bool hasWhitespace = false; + foreach (char c in argument) + { + if (c == Quote) + { + throw new ApplicationException("The argv[0] argument cannot include a double quote."); + } + if (char.IsWhiteSpace(c)) + { + hasWhitespace = true; + } + } + if (argument.Length == 0 || hasWhitespace) + { + stringBuilder.Append(Quote); + stringBuilder.Append(argument); + stringBuilder.Append(Quote); + } + else + { + stringBuilder.Append(argument); + } + } + else + { + AppendArgument(stringBuilder, argument); + } + } + + return stringBuilder.ToString(); + } + + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/PasteArguments.cs b/src/libraries/System.Private.CoreLib/src/System/PasteArguments.cs new file mode 100644 index 0000000..c088fd4 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/PasteArguments.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; + +namespace System +{ + internal static partial class PasteArguments + { + internal static void AppendArgument(StringBuilder stringBuilder, string argument) + { + if (stringBuilder.Length != 0) + { + stringBuilder.Append(' '); + } + + // Parsing rules for non-argv[0] arguments: + // - Backslash is a normal character except followed by a quote. + // - 2N backslashes followed by a quote ==> N literal backslashes followed by unescaped quote + // - 2N+1 backslashes followed by a quote ==> N literal backslashes followed by a literal quote + // - Parsing stops at first whitespace outside of quoted region. + // - (post 2008 rule): A closing quote followed by another quote ==> literal quote, and parsing remains in quoting mode. + if (argument.Length != 0 && ContainsNoWhitespaceOrQuotes(argument)) + { + // Simple case - no quoting or changes needed. + stringBuilder.Append(argument); + } + else + { + stringBuilder.Append(Quote); + int idx = 0; + while (idx < argument.Length) + { + char c = argument[idx++]; + if (c == Backslash) + { + int numBackSlash = 1; + while (idx < argument.Length && argument[idx] == Backslash) + { + idx++; + numBackSlash++; + } + + if (idx == argument.Length) + { + // We'll emit an end quote after this so must double the number of backslashes. + stringBuilder.Append(Backslash, numBackSlash * 2); + } + else if (argument[idx] == Quote) + { + // Backslashes will be followed by a quote. Must double the number of backslashes. + stringBuilder.Append(Backslash, numBackSlash * 2 + 1); + stringBuilder.Append(Quote); + idx++; + } + else + { + // Backslash will not be followed by a quote, so emit as normal characters. + stringBuilder.Append(Backslash, numBackSlash); + } + + continue; + } + + if (c == Quote) + { + // Escape the quote so it appears as a literal. This also guarantees that we won't end up generating a closing quote followed + // by another quote (which parses differently pre-2008 vs. post-2008.) + stringBuilder.Append(Backslash); + stringBuilder.Append(Quote); + continue; + } + + stringBuilder.Append(c); + } + + stringBuilder.Append(Quote); + } + } + + private static bool ContainsNoWhitespaceOrQuotes(string s) + { + for (int i = 0; i < s.Length; i++) + { + char c = s[i]; + if (char.IsWhiteSpace(c) || c == Quote) + { + return false; + } + } + + return true; + } + + private const char Quote = '\"'; + private const char Backslash = '\\'; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/PlatformID.cs b/src/libraries/System.Private.CoreLib/src/System/PlatformID.cs new file mode 100644 index 0000000..1e0a688 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/PlatformID.cs @@ -0,0 +1,19 @@ +// 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.ComponentModel; + +namespace System +{ + public enum PlatformID + { + [EditorBrowsable(EditorBrowsableState.Never)] Win32S = 0, + [EditorBrowsable(EditorBrowsableState.Never)] Win32Windows = 1, + Win32NT = 2, + [EditorBrowsable(EditorBrowsableState.Never)] WinCE = 3, + Unix = 4, + [EditorBrowsable(EditorBrowsableState.Never)] Xbox = 5, + [EditorBrowsable(EditorBrowsableState.Never)] MacOSX = 6 + } +} -- 2.7.4