using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.BITS;
namespace Vanara.IO
{
/// Manages the set of files for a background copy job.
public class BackgroundCopyFileCollection : ICollection, IDisposable
{
private IBackgroundCopyJob m_ijob;
internal BackgroundCopyFileCollection(IBackgroundCopyJob ijob)
{
m_ijob = ijob;
}
internal BackgroundCopyFileCollection()
{
}
/// Gets the number of files in the current job.
public int Count
{
get
{
try
{
return (int)m_ijob.EnumFiles().GetCount();
}
catch (COMException cex)
{
HandleCOMException(cex);
}
return 0;
}
}
/// Gets a value indicating whether the is read-only.
bool ICollection.IsReadOnly => false;
/// Add a file to a download or an upload job. Only one file can be added to upload jobs.
///
/// Contains the name of the file on the server (for example, http://[server]/[path]/file.ext). The format of the name must conform to the transfer
/// protocol you use. You cannot use wildcards in the path or file name. The URL must contain only legal URL characters; no escape processing is
/// performed. The URL is limited to 2,200 characters, not including the null terminator. You can use SMB to express the remote name of the file to
/// download or upload; there is no SMB support for upload-reply jobs. You can specify the remote name as a UNC path, full path with a network drive, or
/// use the "file://" prefix.
///
///
/// Contains the name of the file on the client. The file name must include the full path (for example, d:\myapp\updates\file.ext). You cannot use
/// wildcards in the path or file name, and directories in the path must exist. The user must have permission to write to the local directory for
/// downloads and the reply portion of an upload-reply job. BITS does not support NTFS streams. Instead of using network drives, which are session
/// specific, use UNC paths.
///
public void Add(string remoteFilePath, string localFilePath)
{
try
{
m_ijob.AddFile(remoteFilePath, localFilePath);
}
catch (COMException cex)
{
HandleCOMException(cex);
}
}
/// Add a file to a download job and specify the ranges of the file you want to download.
///
/// Contains the name of the file on the server (for example, http://[server]/[path]/file.ext). The format of the name must conform to the transfer
/// protocol you use. You cannot use wildcards in the path or file name. The URL must contain only legal URL characters; no escape processing is
/// performed. The URL is limited to 2,200 characters, not including the null terminator. You can use SMB to express the remote name of the file to
/// download or upload; there is no SMB support for upload-reply jobs. You can specify the remote name as a UNC path, full path with a network drive, or
/// use the "file://" prefix.
///
///
/// Contains the name of the file on the client. The file name must include the full path (for example, d:\myapp\updates\file.ext). You cannot use
/// wildcards in the path or file name, and directories in the path must exist. The user must have permission to write to the local directory for
/// downloads and the reply portion of an upload-reply job. BITS does not support NTFS streams. Instead of using network drives, which are session
/// specific, use UNC paths.
///
/// Zero-based offset to the beginning of the range of bytes to download from a file.
/// Number of bytes in the range.
public void Add(string remoteFilePath, string localFilePath, long initialOffset, long length = -1)
{
IBackgroundCopyJob3 ijob3 = null;
try { ijob3 = (IBackgroundCopyJob3)m_ijob; }
catch { throw new NotSupportedException(); }
var rng = new[] { new BG_FILE_RANGE { InitialOffset = (ulong)initialOffset, Length = length == -1 ? ulong.MaxValue : (ulong)length } };
try
{
ijob3.AddFileWithRanges(remoteFilePath, localFilePath, 1, rng);
}
catch (COMException cex)
{
HandleCOMException(cex);
}
}
/// Add a list of files to download from a URL.
///
/// Contains the name of the directory on the server (for example, http://[server]/[path]/). The format of the name must conform to the transfer protocol
/// you use. You cannot use wildcards in the path or file name. The URL must contain only legal URL characters; no escape processing is performed. The
/// URL is limited to 2,200 characters, not including the null terminator. You can use SMB to express the remote name of the file to download or upload;
/// there is no SMB support for upload-reply jobs. You can specify the remote name as a UNC path, full path with a network drive, or use the "file://" prefix.
///
///
/// Contains the name of the directory on the client. The directory must exist. The user must have permission to write to the directory for downloads.
/// BITS does not support NTFS streams. Instead of using network drives, which are session specific, use UNC paths.
///
/// List of file names to retrieve. Filename will be appended to both the remoteUrlRoot and the localDirectory.
public void AddRange(Uri remoteUrlRoot, DirectoryInfo localDirectory, IEnumerable files)
{
var array = files.Select(s => new BG_FILE_INFO(new Uri(remoteUrlRoot, s).AbsoluteUri, Path.Combine(localDirectory.FullName, s))).ToArray();
try
{
m_ijob.AddFileSet((uint)array.Length, array);
}
catch (COMException cex)
{
HandleCOMException(cex);
}
}
/// Disposes of the BackgroundCopyFileSet object.
void IDisposable.Dispose()
{
m_ijob = null;
}
///
/// Returns an object that implements the interface and that can iterate through the
/// objects within the collection.
///
///
/// Returns an object that implements the interface and that can iterate through the
/// objects within the collection.
///
public IEnumerator GetEnumerator()
{
var ienum = m_ijob.EnumFiles();
return new Enumerator(ienum);
}
/// Adds an item to the .
/// The object to add to the .
///
void ICollection.Add(BackgroundCopyFileInfo item)
{
}
/// Removes all items from the .
///
void ICollection.Clear()
{
throw new NotSupportedException();
}
/// Determines whether the contains a specific value.
/// The object to locate in the .
/// true if is found in the ; otherwise, false.
///
bool ICollection.Contains(BackgroundCopyFileInfo item)
{
throw new NotSupportedException();
}
///
/// Copies the elements of the to an , starting at a particular index.
///
///
/// The one-dimensional that is the destination of the elements copied from . The
/// must have zero-based indexing.
///
/// The zero-based index in at which copying begins.
///
void ICollection.CopyTo(BackgroundCopyFileInfo[] array, int arrayIndex)
{
throw new NotSupportedException();
}
/// Returns an enumerator that iterates through a collection.
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// Removes the first occurrence of a specific object from the .
/// The object to remove from the .
///
/// true if was successfully removed from the ; otherwise, false. This method also returns false if
/// is not found in the original .
///
///
bool ICollection.Remove(BackgroundCopyFileInfo item)
{
throw new NotSupportedException();
}
internal BackgroundCopyFileInfo[] GetBCFIArray() => this.ToArray();
private void HandleCOMException(COMException cex)
{
var state = m_ijob.GetState();
if (state == BG_JOB_STATE.BG_JOB_STATE_ERROR || state == BG_JOB_STATE.BG_JOB_STATE_TRANSIENT_ERROR)
{
var pErr = m_ijob.GetError();
throw new BackgroundCopyException(pErr);
}
else
throw new BackgroundCopyException(cex);
}
///
/// An implementation the interface that can iterate through the objects within the
/// collection.
///
private sealed class Enumerator : IEnumerator
{
private IBackgroundCopyFile icurrentfile;
private IEnumBackgroundCopyFiles ienum;
internal Enumerator(IEnumBackgroundCopyFiles enumfiles)
{
ienum = enumfiles;
ienum.Reset();
}
///
/// Gets the object in the collection to which the enumerator is pointing.
///
public BackgroundCopyFileInfo Current => icurrentfile != null ? new BackgroundCopyFileInfo(icurrentfile) : throw new InvalidOperationException();
///
/// Gets the object in the collection to which the enumerator is pointing.
///
object IEnumerator.Current => Current;
/// Disposes of the Enumerator object.
public void Dispose()
{
ienum = null;
icurrentfile = null;
}
/// Moves the enumerator index to the next object in the collection.
///
public bool MoveNext()
{
try
{
icurrentfile = ienum.Next(1)?.FirstOrDefault();
return icurrentfile != null;
}
catch { return false; }
}
/// Resets the enumerator index to the beginning of the collection.
public void Reset()
{
icurrentfile = null;
ienum.Reset();
}
}
}
}