/// <summary>
 /// cancel a background task - or prevent a task from running
 /// </summary>
 public void Cancel()
 {
     lock (_lock)
     {
         if (_client != null)
         {
             _client.CancelAsync();
         }
         else
         {
             // probably an unstarted task or a task that has completed
             _complete = true;
             TaskComplete.Set();
         }
     }
 }
        public static void DownloadFileSync(this IWebClient client, Uri address, string filename, CancellationToken cancellationToken)
        {
            // Events are only fired when downloading asynchronously with DownloadFileAsync.
            // This method allows for synchronous downloads while still firing events.
            // Based on the solution given here: https://stackoverflow.com/a/25834736 (user195275)

            object mutex             = new object();
            bool   downloadCancelled = false;

            void handleDownloadComplete(object sender, AsyncCompletedEventArgs e)
            {
                downloadCancelled = e.Cancelled;

                lock (e.UserState)
                    Monitor.Pulse(e.UserState);
            }

            client.DownloadFileCompleted += handleDownloadComplete;

            try {
                lock (mutex) {
                    using (cancellationToken.Register(() => client.CancelAsync())) {
                        client.DownloadFileAsync(address, filename, mutex);

                        Monitor.Wait(mutex);
                    }
                }
            }
            finally {
                client.DownloadFileCompleted -= handleDownloadComplete;
            }

            if (downloadCancelled)
            {
                throw new WebException(Properties.ExceptionMessages.TheRequestWasCancelled, WebExceptionStatus.RequestCanceled);
            }
        }
        static Timer SetupTransferProgress(IWebClient webClient, ITransferProgress transferProgress) {
            var lastTime = Tools.Generic.GetCurrentUtcDateTime;
            long lastBytes = 0;

            transferProgress.Progress = 0;
            transferProgress.FileSizeTransfered = 0;

            webClient.DownloadProgressChanged +=
                (sender, args) => {
                    var bytes = args.BytesReceived;
                    var now = Tools.Generic.GetCurrentUtcDateTime;

                    transferProgress.Progress = args.ProgressPercentage;
                    transferProgress.FileSizeTransfered = bytes;

                    if (lastBytes != 0) {
                        var timeSpan = now - lastTime;
                        var bytesChange = bytes - lastBytes;

                        if (timeSpan.TotalMilliseconds > 0)
                            transferProgress.Speed = (long) (bytesChange/(timeSpan.TotalMilliseconds/1000.0));
                    }

                    lastBytes = bytes;
                    lastTime = now;
                };

            webClient.DownloadFileCompleted += (sender, args) => { transferProgress.Completed = true; };

            var timer = new TimerWithElapsedCancellation(500, () => {
                if (!transferProgress.Completed
                    && Tools.Generic.LongerAgoThan(lastTime, timeout)) {
                    webClient.CancelAsync();
                    return false;
                }
                return !transferProgress.Completed;
            });
            return timer;
        }