|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
#nullable disable
namespace Microsoft.Build.Shared.FileSystem
{
/// <summary>
/// Native implementation of file system operations
/// </summary>
internal static class WindowsNative
{
/// <summary>
/// Maximum path length.
/// </summary>
public const int MaxPath = 260;
/// <summary>
/// ERROR_SUCCESS
/// </summary>
public const int ErrorSuccess = 0x0;
/// <summary>
/// ERROR_FILE_NOT_FOUND
/// </summary>
public const int ErrorFileNotFound = 0x2;
/// <summary>
/// ERROR_PATH_NOT_FOUND
/// </summary>
public const int ErrorPathNotFound = 0x3;
/// <summary>
/// ERROR_DIRECTORY
/// </summary>
public const int ErrorDirectory = 0x10b;
/// <summary>
/// ERROR_ACCESS_DENIED
/// </summary>
public const int ErrorAccessDenied = 0x5;
/// <summary>
/// ERROR_NO_MORE_FILES
/// </summary>
public const uint ErrorNoMoreFiles = 0x12;
/// <summary>
/// Modifies the search condition of PathMatchSpecEx
/// </summary>
/// <remarks>
/// <see ref="https://msdn.microsoft.com/en-us/library/windows/desktop/bb773728(v=vs.85).aspx"/>
/// </remarks>
public static class DwFlags
{
/// <summary>
/// The pszSpec parameter points to a single file name pattern to be matched.
/// </summary>
public const int PmsfNormal = 0x0;
/// <summary>
/// The pszSpec parameter points to a semicolon-delimited list of file name patterns to be matched.
/// </summary>
public const int PmsfMultiple = 0x1;
/// <summary>
/// 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.
/// </summary>
public const int PmsfDontStripSpaces = 0x00010000;
}
/// <summary>
/// Status of attempting to enumerate a directory.
/// </summary>
public enum EnumerateDirectoryStatus
{
/// <summary>
/// Enumeration of an existent directory succeeded.
/// </summary>
Success,
/// <summary>
/// One or more path components did not exist, so the search directory could not be opened.
/// </summary>
SearchDirectoryNotFound,
/// <summary>
/// A path component in the search path refers to a file. Only directories can be enumerated.
/// </summary>
CannotEnumerateFile,
/// <summary>
/// Directory enumeration could not complete due to denied access to the search directory or a file inside.
/// </summary>
AccessDenied,
/// <summary>
/// Directory enumeration failed without a well-known status (see <see cref="EnumerateDirectoryResult.NativeErrorCode"/>).
/// </summary>
UnknownError,
}
/// <summary>
/// Represents the result of attempting to enumerate a directory.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")]
public struct EnumerateDirectoryResult
{
/// <summary>
/// Enumerated directory.
/// </summary>
public readonly string Directory;
/// <summary>
/// Overall status indication.
/// </summary>
public readonly EnumerateDirectoryStatus Status;
/// <summary>
/// Native error code. Note that an error code other than <c>ERROR_SUCCESS</c> may be present even on success.
/// </summary>
public readonly int NativeErrorCode;
/// <nodoc />
public EnumerateDirectoryResult(string directory, EnumerateDirectoryStatus status, int nativeErrorCode)
{
Directory = directory;
Status = status;
NativeErrorCode = nativeErrorCode;
}
/// <summary>
/// Indicates if enumeration succeeded.
/// </summary>
public readonly bool Succeeded
{
get { return Status == EnumerateDirectoryStatus.Success; }
}
/// <summary>
/// Throws an exception if the native error code could not be canonicalized (a fairly exceptional circumstance).
/// This is allowed when <see cref="Status"/> is <see cref="EnumerateDirectoryStatus.UnknownError"/>.
/// </summary>
/// <remarks>
/// This is a good <c>default:</c> case when switching on every possible <see cref="EnumerateDirectoryStatus"/>
/// </remarks>
public readonly NativeWin32Exception ThrowForUnknownError()
{
Debug.Assert(Status == EnumerateDirectoryStatus.UnknownError);
throw CreateExceptionForError();
}
/// <summary>
/// Throws an exception if the native error code was corresponds to a known <see cref="EnumerateDirectoryStatus"/>
/// (and enumeration was not successful).
/// </summary>
public NativeWin32Exception ThrowForKnownError()
{
Debug.Assert(Status != EnumerateDirectoryStatus.UnknownError &&
Status != EnumerateDirectoryStatus.Success);
throw CreateExceptionForError();
}
/// <summary>
/// Creates (but does not throw) an exception for this result. The result must not be successful.
/// </summary>
public readonly 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));
}
}
}
/// <summary>
/// <c>Win32FindData</c>
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")]
public struct Win32FindData
{
/// <summary>
/// The file attributes of a file
/// </summary>
public FileAttributes DwFileAttributes;
/// <summary>
/// Specified when a file or directory was created
/// </summary>
public System.Runtime.InteropServices.ComTypes.FILETIME FtCreationTime;
/// <summary>
/// Specifies when the file was last read from, written to, or for executable files, run.
/// </summary>
public System.Runtime.InteropServices.ComTypes.FILETIME FtLastAccessTime;
/// <summary>
/// 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.
/// </summary>
public System.Runtime.InteropServices.ComTypes.FILETIME FtLastWriteTime;
/// <summary>
/// The high-order DWORD value of the file size, in bytes.
/// </summary>
public uint NFileSizeHigh;
/// <summary>
/// The low-order DWORD value of the file size, in bytes.
/// </summary>
public uint NFileSizeLow;
/// <summary>
/// If the dwFileAttributes member includes the FILE_ATTRIBUTE_REPARSE_POINT attribute, this member specifies the reparse point tag.
/// </summary>
public uint DwReserved0;
/// <summary>
/// Reserved for future use.
/// </summary>
public uint DwReserved1;
/// <summary>
/// The name of the file.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxPath)]
public string CFileName;
/// <summary>
/// An alternative name for the file.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string CAlternate;
}
/// <nodoc/>
[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);
/// <nodoc/>
[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);
/// <nodoc/>
[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);
/// <nodoc/>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool FindClose(IntPtr findFileHandle);
}
}
|