using System; using System.ComponentModel; using System.IO; #if !(NET20 || NET35) using System.Threading.Tasks; #endif using Vanara.PInvoke; using static Vanara.PInvoke.Kernel32; namespace Vanara.Extensions { /// Extension methods for and derived classes to facilitate retrieval of extended properties. public static class FileInfoExtension { /// Gets the file handle of the reference. /// The file information. /// if set to retrieve the handle as read-only. /// if set to retrieve the handle as overlapped. /// A safe handle to the file. public static SafeHFILE GetFileHandle(this FileSystemInfo fi, bool readOnly = true, bool overlapped = false) { var fa = readOnly ? System.IO.FileAccess.Read : System.IO.FileAccess.ReadWrite; var fs = readOnly ? FileShare.Read : FileShare.None; var ff = FileFlagsAndAttributes.FILE_ATTRIBUTE_NORMAL; if (overlapped) ff |= FileFlagsAndAttributes.FILE_FLAG_OVERLAPPED; if (fi is DirectoryInfo) ff |= FileFlagsAndAttributes.FILE_FLAG_BACKUP_SEMANTICS; return CreateFile(fi.FullName, (Kernel32.FileAccess)fa, fs, null, FileMode.Open, ff, HFILE.NULL); } /// Gets the file system flags for a drive. /// The reference. /// The associated system file flags. public static FileSystemFlags GetFileSystemFlags(this DriveInfo di) => GetVolumeInformation(di.Name, out _, out _, out _, out var fsFlags, out _) ? fsFlags : throw new Win32Exception(); /// Gets the maximum length of a file name on the specified drive. /// The reference. /// The maximum length of a file name on the specified drive. public static uint GetMaxFileNameLength(this DriveInfo di) => GetVolumeInformation(di.Name, out _, out _, out var compLen, out _, out _) ? compLen : throw new Win32Exception(); /// Gets whether NTFS compression is enabled for this file. /// The file information. /// if the specified files is compressed under NTFS; otherwise . public static bool GetNtfsCompression(this FileSystemInfo fi) { using var fs = GetFileHandle(fi); return DeviceIoControl(fs, IOControlCode.FSCTL_GET_COMPRESSION, out ushort outVal) ? outVal != 0 : throw new Win32Exception(); //return (fi.Attributes & FileAttributes.Compressed) == FileAttributes.Compressed; } /// Gets the length of the file on the disk. If the file is compressed, this will return the compressed size. /// The instance. /// The actual size of the file on the disk in bytes, compressed or uncompressed. public static ulong GetPhysicalLength(this FileInfo fi) { GetCompressedFileSize(fi.FullName, out ulong sz).ThrowIfFailed(); return sz; } /// Sets whether NTFS compression is enabled for this file. /// The file information. /// if set to compress the file under NTFS; to decompress. public static void SetNtfsCompression(this FileSystemInfo fi, bool compressed) { using var fs = GetFileHandle(fi, false); if (!DeviceIoControl(fs, IOControlCode.FSCTL_SET_COMPRESSION, (ushort)(compressed ? 1 : 0))) throw new Win32Exception(); } #if !(NET20 || NET35) /// Asynchronously gets whether NTFS compression is enabled for this file. /// The file information. /// if the specified files is compressed under NTFS; otherwise . public static Task GetNtfsCompressionAsync(this FileSystemInfo fi) { using var fs = GetFileHandle(fi, true, true); return ConvertTask(DeviceIoControlAsync(fs, IOControlCode.FSCTL_GET_COMPRESSION), u => u.GetValueOrDefault() > 0); } /// Asynchronously sets whether NTFS compression is enabled for this file. /// The file information. /// if set to compress the file under NTFS; to decompress. public static Task SetNtfsCompressionAsync(this FileSystemInfo fi, bool compressed) { using var fs = GetFileHandle(fi, false, true); return DeviceIoControlAsync(fs, IOControlCode.FSCTL_SET_COMPRESSION, (ushort)(compressed ? 1 : 0)); } private static Task ConvertTask(Task task, Converter converter = null) { var tret = new TaskCompletionSource(); if (task.IsCanceled) tret.TrySetCanceled(); else if (task.IsFaulted && task.Exception != null) tret.TrySetException(task.Exception); else { if (converter == null) tret.TrySetResult((TNew)Convert.ChangeType(task.Result, typeof(TNew))); else tret.TrySetResult(converter(task.Result)); } return tret.Task; } #endif } }