// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.InteropServices; namespace Microsoft.Build.Shared.FileSystem { /// /// Native implementation of file system operations /// internal static class WindowsNative { /// /// Maximum path length. /// public const int MaxPath = 260; /// /// ERROR_SUCCESS /// public const int ErrorSuccess = 0x0; /// /// ERROR_FILE_NOT_FOUND /// public const int ErrorFileNotFound = 0x2; /// /// ERROR_PATH_NOT_FOUND /// public const int ErrorPathNotFound = 0x3; /// /// ERROR_DIRECTORY /// public const int ErrorDirectory = 0x10b; /// /// ERROR_ACCESS_DENIED /// public const int ErrorAccessDenied = 0x5; /// /// ERROR_NO_MORE_FILES /// public const uint ErrorNoMoreFiles = 0x12; /// /// Modifies the search condition of PathMatchSpecEx /// /// /// /// public static class DwFlags { /// /// The pszSpec parameter points to a single file name pattern to be matched. /// public const int PmsfNormal = 0x0; /// /// The pszSpec parameter points to a semicolon-delimited list of file name patterns to be matched. /// public const int PmsfMultiple = 0x1; /// /// If PMSF_NORMAL is used, ignore leading spaces in the string pointed to by pszSpec. If PMSF_MULTIPLE is used, /// ignore leading spaces in each file type contained in the string pointed to by pszSpec. This flag can be combined with PMSF_NORMAL and PMSF_MULTIPLE. /// public const int PmsfDontStripSpaces = 0x00010000; } /// /// Status of attempting to enumerate a directory. /// public enum EnumerateDirectoryStatus { /// /// Enumeration of an existent directory succeeded. /// Success, /// /// One or more path components did not exist, so the search directory could not be opened. /// SearchDirectoryNotFound, /// /// A path component in the search path refers to a file. Only directories can be enumerated. /// CannotEnumerateFile, /// /// Directory enumeration could not complete due to denied access to the search directory or a file inside. /// AccessDenied, /// /// Directory enumeration failed without a well-known status (see ). /// UnknownError, } /// /// Represents the result of attempting to enumerate a directory. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] public struct EnumerateDirectoryResult { /// /// Enumerated directory. /// public readonly string Directory; /// /// Overall status indication. /// public readonly EnumerateDirectoryStatus Status; /// /// Native error code. Note that an error code other than ERROR_SUCCESS may be present even on success. /// public readonly int NativeErrorCode; /// public EnumerateDirectoryResult(string directory, EnumerateDirectoryStatus status, int nativeErrorCode) { Directory = directory; Status = status; NativeErrorCode = nativeErrorCode; } /// /// Indicates if enumeration succeeded. /// public bool Succeeded { get { return Status == EnumerateDirectoryStatus.Success; } } /// /// Throws an exception if the native error code could not be canonicalized (a fairly exceptional circumstance). /// This is allowed when is . /// /// /// This is a good default: case when switching on every possible /// public NativeWin32Exception ThrowForUnknownError() { Debug.Assert(Status == EnumerateDirectoryStatus.UnknownError); throw CreateExceptionForError(); } /// /// Throws an exception if the native error code was corresponds to a known /// (and enumeration was not successful). /// public NativeWin32Exception ThrowForKnownError() { Debug.Assert(Status != EnumerateDirectoryStatus.UnknownError && Status != EnumerateDirectoryStatus.Success); throw CreateExceptionForError(); } /// /// Creates (but does not throw) an exception for this result. The result must not be successful. /// public NativeWin32Exception CreateExceptionForError() { Debug.Assert(Status != EnumerateDirectoryStatus.Success); if (Status == EnumerateDirectoryStatus.UnknownError) { return new NativeWin32Exception( NativeErrorCode, "Enumerating a directory failed"); } else { return new NativeWin32Exception( NativeErrorCode, string.Format( CultureInfo.InvariantCulture, "Enumerating a directory failed: {0:G}", Status)); } } } /// /// Win32FindData /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] public struct Win32FindData { /// /// The file attributes of a file /// public FileAttributes DwFileAttributes; /// /// Specified when a file or directory was created /// public System.Runtime.InteropServices.ComTypes.FILETIME FtCreationTime; /// /// Specifies when the file was last read from, written to, or for executable files, run. /// public System.Runtime.InteropServices.ComTypes.FILETIME FtLastAccessTime; /// /// For a file, the structure specifies when the file was last written to, truncated, or overwritten. /// For a directory, the structure specifies when the directory is created. /// public System.Runtime.InteropServices.ComTypes.FILETIME FtLastWriteTime; /// /// The high-order DWORD value of the file size, in bytes. /// public uint NFileSizeHigh; /// /// The low-order DWORD value of the file size, in bytes. /// public uint NFileSizeLow; /// /// If the dwFileAttributes member includes the FILE_ATTRIBUTE_REPARSE_POINT attribute, this member specifies the reparse point tag. /// public uint DwReserved0; /// /// Reserved for future use. /// public uint DwReserved1; /// /// The name of the file. /// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxPath)] public string CFileName; /// /// An alternative name for the file. /// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string CAlternate; } /// [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible", Justification = "Needed for custom enumeration.")] public static extern SafeFindFileHandle FindFirstFileW( string lpFileName, out Win32FindData lpFindFileData); /// [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] [SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible", Justification = "Needed for custom enumeration.")] public static extern bool FindNextFileW(SafeHandle hFindFile, out Win32FindData lpFindFileData); /// [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] [SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible", Justification = "Needed for creating symlinks.")] public static extern int PathMatchSpecExW([In] string pszFileParam, [In] string pszSpec, [In] int flags); /// [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool FindClose(IntPtr findFileHandle); } }