using System; using System.Collections.Generic; using System.Runtime.InteropServices; using static Vanara.PInvoke.BITS; namespace Vanara.IO { /// Information about a file in a background copy job. public class BackgroundCopyFileInfo { internal BG_FILE_INFO fi; private IBackgroundCopyFile iFile; internal BackgroundCopyFileInfo(IBackgroundCopyFile ibgfile) { iFile = ibgfile; } internal BackgroundCopyFileInfo(BG_FILE_INFO bgfi) { fi = bgfi; } /// Retrieves the ranges that you want to download from the remote file. /// The file copy ranges. public IEnumerable FileRanges { get { IFile2.GetFileRanges(out var cnt, out var rng); return rng.ToEnumerable((int)cnt); } } /// Returns the set of file ranges that have been downloaded. /// The file copy ranges that have been downloaded. Ranges will be merged together as much as possible. The ranges are ordered by offset. public IEnumerable FilledFileRanges { get { IFile6.GetFilledFileRanges(out var cnt, out var rng); return rng.ToEnumerable((int)cnt); } } /// Gets or sets a value indicating whether the contents of the file are valid. /// true if the file content is valid; otherwise, false. public bool IsFileContentValid { get => IFile3.GetValidationState(); set => IFile3.SetValidationState(value); } /// /// Size of the file in bytes. If the value is -1, the total size of the file has not been determined. BITS does not set this value if it cannot /// determine the size of the file. For example, if the specified file or server does not exist, BITS cannot determine the size of the file. If you are /// downloading ranges from a file, BytesTotal reflects the total number of bytes you want to download from the file. /// public long Length => (long)CopyProgress.BytesTotal; /// Retrieves the local name of the file. public string LocalFilePath { get => iFile == null ? fi.LocalName : iFile.GetLocalName(); set { if (iFile != null) throw new InvalidOperationException("You cannot change the LocalFilePath property on CurrentFileSet results."); fi.LocalName = value; } } /// Gets the amount of file data downloaded from the originating server. public ulong OriginServerDownloadBytes { get { IFile4.GetPeerDownloadStats(out var o, out var _); return o; } } /// Gets the amount of file data downloaded from a peer-to-peer source. public ulong PeerDownloadBytes { get { IFile4.GetPeerDownloadStats(out var _, out var p); return p; } } /// Gets the percentage of the transfer that has completed. public float PercentComplete { get { var p = CopyProgress; return p.Completed || p.BytesTotal == 0 ? 100.0F : p.BytesTransferred * 100.0F / p.BytesTotal; } } /// Retrieves the remote name of the file. public string RemoteFilePath { get => iFile == null ? fi.RemoteName : iFile.GetRemoteName(); set { fi.RemoteName = value; (iFile as IBackgroundCopyFile2)?.SetRemoteName(value); } } /// Gets or sets the full set of HTTP response headers from the server's last HTTP response packet. /// The response headers. public string ResponseHeaders { get { var hdr = IFile5.GetProperty(BITS_FILE_PROPERTY_ID.BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS); return hdr.String; } set { var hdr = new BITS_FILE_PROPERTY_VALUE { String = value }; IFile5.SetProperty(BITS_FILE_PROPERTY_ID.BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS, hdr); } } /// Gets the full path of the temporary file that contains the content of the download. public string TemporaryName => IFile3.GetTemporaryName(); /// /// For downloads, the value is TRUE if the file is available to the user; otherwise, the value is FALSE. Files are available to the user after calling /// the method. If the method generates a transient error, those files /// processed before the error occurred are available to the user; the others are not. Use the Completed property to determine if the file is available /// to the user when Complete fails. For uploads, the value is TRUE when the file upload is complete; otherwise, the value is FALSE. /// public bool TransferCompleted => CopyProgress.Completed; /// Gets the number of bytes transferred. public long BytesTransferred => (long)CopyProgress.BytesTransferred; /// Gets a value that determines if any part of the file was downloaded from a peer. /// Is TRUE if any part of the file was downloaded from a peer; otherwise, FALSE. public bool IsDownloadedFromPeer => IFile3.IsDownloadedFromPeer(); /// Retrieves the progress of the file transfer. internal BG_FILE_PROGRESS CopyProgress => iFile?.GetProgress() ?? throw new InvalidOperationException("You can only get the CopyProgress on CurrentFileSet results."); private IBackgroundCopyFile2 IFile2 => GetDerived(); private IBackgroundCopyFile3 IFile3 => GetDerived(); private IBackgroundCopyFile4 IFile4 => GetDerived(); private IBackgroundCopyFile5 IFile5 => GetDerived(); private IBackgroundCopyFile6 IFile6 => GetDerived(); /// Adds a new set of file ranges to be prioritized for download. /// /// An array of file ranges to be downloaded. Requested ranges are allowed to overlap previously downloaded (or pending) ranges. Ranges are automatically /// split into non-overlapping ranges. /// public void RequestFileRanges(BackgroundCopyFileRange[] ranges) { IFile6.RequestFileRanges((uint)ranges.Length, Array.ConvertAll(ranges, r => r.fr)); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() => $"{LocalFilePath} => {RemoteFilePath}"; /// Specifies a position to prioritize downloading missing data from. /// Specifies the new position to prioritize downloading missing data from. public void UpdateDownloadPosition(ulong offset) => IFile6.UpdateDownloadPosition(offset); private T GetDerived() where T : class { T ret = iFile as T; return ret ?? throw new PlatformNotSupportedException(); } } /// Identifies a range of bytes to download from a file. [StructLayout(LayoutKind.Sequential)] public class BackgroundCopyFileRange { internal BG_FILE_RANGE fr; /// Zero-based offset to the beginning of the range of bytes to download from a file. public ulong InitialOffset { get => fr.InitialOffset; set => fr.InitialOffset = value; } /// /// The length of the range, in bytes. Do not specify a zero byte length. To indicate that the range extends to the end of the file, specify BG_LENGTH_TO_EOF. /// public ulong Length { get => fr.Length; set => fr.Length = value; } /// Performs an implicit conversion from to . /// The BG_FILE_RANGE instance. /// The result of the conversion. public static implicit operator BackgroundCopyFileRange(BG_FILE_RANGE p) => new BackgroundCopyFileRange { fr = p }; } }