diff --git a/BITS/BackgroundCopyJob.cs b/BITS/BackgroundCopyJob.cs index a1a35ad1..d2d74667 100644 --- a/BITS/BackgroundCopyJob.cs +++ b/BITS/BackgroundCopyJob.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using Vanara.Extensions; @@ -122,42 +123,64 @@ namespace Vanara.IO internal static readonly Version CopyCallback2 = new(3, 0); internal static readonly Version CopyCallback3 = new(10, 1); + private EventHandler complEvent, errEvent, modEvent; + private EventHandler fRangTranEvent; + private EventHandler fTranEvent; + private readonly bool isReadOnly = false; private IBackgroundCopyJob m_ijob; - private Notifier m_notifier; + private Notifier m_notifier = null; internal BackgroundCopyJob(IBackgroundCopyJob ijob) { m_ijob = ijob ?? throw new ArgumentNullException(nameof(ijob)); - m_notifier = new Notifier(this); - m_ijob.SetNotifyInterface(m_notifier); - var bitsVer = BackgroundCopyManager.Version; - NotifyFlags = bitsVer switch - { - var v when v >= CopyCallback3 => BG_NOTIFY.BG_NOTIFY_FILE_RANGES_TRANSFERRED | BG_NOTIFY.BG_NOTIFY_FILE_TRANSFERRED | BG_NOTIFY.BG_NOTIFY_JOB_ERROR | BG_NOTIFY.BG_NOTIFY_JOB_MODIFICATION | BG_NOTIFY.BG_NOTIFY_JOB_TRANSFERRED, - var v when v >= CopyCallback2 => BG_NOTIFY.BG_NOTIFY_FILE_TRANSFERRED | BG_NOTIFY.BG_NOTIFY_JOB_ERROR | BG_NOTIFY.BG_NOTIFY_JOB_MODIFICATION | BG_NOTIFY.BG_NOTIFY_JOB_TRANSFERRED, - _ => BG_NOTIFY.BG_NOTIFY_JOB_ERROR | BG_NOTIFY.BG_NOTIFY_JOB_MODIFICATION | BG_NOTIFY.BG_NOTIFY_JOB_TRANSFERRED, - }; Files = new BackgroundCopyFileCollection(m_ijob); Credentials = new BackgroundCopyJobCredentials(IJob2); + try { isReadOnly = OwnerIsElevated; } + catch + { + // Try to set a value. If it fails, we know we can't write. + try { DisplayName = DisplayName; } + catch { isReadOnly = true; } + } } /// Occurs when all of the files in the job have been transferred. - public event EventHandler Completed; + public event EventHandler Completed + { + add => AddEvent(BG_NOTIFY.BG_NOTIFY_JOB_TRANSFERRED, ref complEvent, value); + remove => RemoveEvent(BG_NOTIFY.BG_NOTIFY_JOB_TRANSFERRED, ref complEvent, value); + } /// Fires when an error occurs. - public event EventHandler Error; + public event EventHandler Error + { + add => AddEvent(BG_NOTIFY.BG_NOTIFY_JOB_ERROR, ref errEvent, value); + remove => RemoveEvent(BG_NOTIFY.BG_NOTIFY_JOB_ERROR, ref errEvent, value); + } /// Occurs when file ranges have been transferred. - public event EventHandler FileRangesTransferred; + public event EventHandler FileRangesTransferred + { + add => AddEvent(BG_NOTIFY.BG_NOTIFY_FILE_RANGES_TRANSFERRED, ref fRangTranEvent, value); + remove => RemoveEvent(BG_NOTIFY.BG_NOTIFY_FILE_RANGES_TRANSFERRED, ref fRangTranEvent, value); + } /// Occurs when a file has been transferred. - public event EventHandler FileTransferred; + public event EventHandler FileTransferred + { + add => AddEvent(BG_NOTIFY.BG_NOTIFY_FILE_TRANSFERRED, ref fTranEvent, value); + remove => RemoveEvent(BG_NOTIFY.BG_NOTIFY_FILE_TRANSFERRED, ref fTranEvent, value); + } /// /// Occurs when the job has been modified. For example, a property value changed, the state of the job changed, or progress is made /// transferring the files. /// - public event EventHandler Modified; + public event EventHandler Modified + { + add => AddEvent(BG_NOTIFY.BG_NOTIFY_JOB_MODIFICATION, ref modEvent, value); + remove => RemoveEvent(BG_NOTIFY.BG_NOTIFY_JOB_MODIFICATION, ref modEvent, value); + } /// Gets or sets the flags that identify the owner and ACL information to maintain when transferring a file using SMB. public BackgroundCopyACLFlags ACLFlags @@ -551,11 +574,11 @@ namespace Vanara.IO set => RunAction(() => { if (value is null) - m_ijob.SetProxySettings(BG_JOB_PROXY_USAGE.BG_JOB_PROXY_USAGE_PRECONFIG, null, null); + RunAction(() => m_ijob.SetProxySettings(BG_JOB_PROXY_USAGE.BG_JOB_PROXY_USAGE_PRECONFIG, null, null)); else if (string.IsNullOrEmpty(value.Address.AbsoluteUri)) - m_ijob.SetProxySettings(BG_JOB_PROXY_USAGE.BG_JOB_PROXY_USAGE_NO_PROXY, null, null); + RunAction(() => m_ijob.SetProxySettings(BG_JOB_PROXY_USAGE.BG_JOB_PROXY_USAGE_NO_PROXY, null, null)); else - m_ijob.SetProxySettings(BG_JOB_PROXY_USAGE.BG_JOB_PROXY_USAGE_OVERRIDE, value.Address.AbsoluteUri, string.Join(" ", value.BypassList)); + RunAction(() => m_ijob.SetProxySettings(BG_JOB_PROXY_USAGE.BG_JOB_PROXY_USAGE_OVERRIDE, value.Address.AbsoluteUri, string.Join(" ", value.BypassList))); if (value.Credentials is not null) throw new ArgumentException("The set Proxy property does not support proxy credentials. Please use the SetCredentials method."); }); @@ -649,7 +672,7 @@ namespace Vanara.IO { var st = State; if (st is not BackgroundCopyJobState.Acknowledged and not BackgroundCopyJobState.Cancelled) - m_ijob.SetNotifyFlags(value); + RunAction(() => m_ijob.SetNotifyFlags(value)); }); } @@ -812,7 +835,8 @@ namespace Vanara.IO try { NotifyFlags = 0; - m_ijob.SetNotifyInterface(null); + if (State is not BackgroundCopyJobState.Cancelled and not BackgroundCopyJobState.Acknowledged) + m_ijob.SetNotifyInterface(null); if (State == BackgroundCopyJobState.Transferred) Complete(); if (State != BackgroundCopyJobState.Acknowledged) @@ -829,31 +853,41 @@ namespace Vanara.IO { if (AutoCompleteOnSuccess) Complete(); - Completed?.Invoke(this, new BackgroundCopyJobEventArgs(this)); + complEvent?.Invoke(this, new BackgroundCopyJobEventArgs(this)); } /// Called when an error occurs. /// The error. - protected virtual void OnError(IBackgroundCopyError err) => Error?.Invoke(this, new BackgroundCopyJobEventArgs(this)); + protected virtual void OnError(IBackgroundCopyError err) => errEvent?.Invoke(this, new BackgroundCopyJobEventArgs(this)); /// Called when a file range has been transferred. /// The file being transferred. /// The ranges transferred. - protected virtual void OnFileRangesTransferred(IBackgroundCopyFile file, BG_FILE_RANGE[] ranges) => FileRangesTransferred?.Invoke(this, new BackgroundCopyFileRangesTransferredEventArgs(this, file, ranges)); + protected virtual void OnFileRangesTransferred(IBackgroundCopyFile file, BG_FILE_RANGE[] ranges) => fRangTranEvent?.Invoke(this, new BackgroundCopyFileRangesTransferredEventArgs(this, file, ranges)); /// Called when a file transfer is completed. /// The transferred file. - protected virtual void OnFileTransferred(IBackgroundCopyFile pFile) => FileTransferred?.Invoke(this, new BackgroundCopyFileTransferredEventArgs(this, pFile)); + protected virtual void OnFileTransferred(IBackgroundCopyFile pFile) => fTranEvent?.Invoke(this, new BackgroundCopyFileTransferredEventArgs(this, pFile)); /// Called when the job has been modified. - protected virtual void OnModified() => Modified?.Invoke(this, new BackgroundCopyJobEventArgs(this)); + protected virtual void OnModified() => modEvent?.Invoke(this, new BackgroundCopyJobEventArgs(this)); - private T GetDerived() where T : class + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddEvent(BG_NOTIFY flag, ref EventHandler eventHandler, EventHandler value) { - var ret = m_ijob as T; - return ret ?? throw new PlatformNotSupportedException(); + if (isReadOnly) + throw new UnauthorizedAccessException("This job was created while in an elevated session, but this session is not elevated."); + if (m_notifier is null) + RunAction(() => m_ijob.SetNotifyInterface(m_notifier ??= new Notifier(this))); + var bitsVer = BackgroundCopyManager.Version; + if (flag == BG_NOTIFY.BG_NOTIFY_FILE_TRANSFERRED && bitsVer < CopyCallback2 || flag == BG_NOTIFY.BG_NOTIFY_FILE_RANGES_TRANSFERRED && bitsVer < CopyCallback3) + throw new NotSupportedException("This event is not supported under this version of Windows."); + eventHandler += value; + NotifyFlags |= flag; } + private T GetDerived() where T : class => m_ijob as T ?? throw new PlatformNotSupportedException(); + private object GetProperty(BITS_JOB_PROPERTY_ID id) { var value = RunAction(() => IJob5.GetProperty(id)); @@ -878,6 +912,14 @@ namespace Vanara.IO throw new BackgroundCopyException(cex); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RemoveEvent(BG_NOTIFY flag, ref EventHandler eventHandler, EventHandler value) + { + eventHandler -= value; + if (eventHandler is null) + NotifyFlags = NotifyFlags.ClearFlags(flag); + } + private void RunAction(Action action) { try { action(); }