// 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);
}
}