1

This works fine:

private WebClient _webClient;

private void ButtonStart_Click(object sender, RoutedEventArgs e) {
    using (_webClient = new WebClient()) {
        _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
}

private void ButtonStop_Click(object sender, RoutedEventArgs e) {
    _webClient.CancelAsync();
}

While this code (notice the async/await pattern)...:

private WebClient _webClient;

private async void ButtonStart_Click(object sender, RoutedEventArgs e) {
    using (_webClient = new WebClient()) {
        await _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
}

private void ButtonStop_Click(object sender, RoutedEventArgs e) {
    _webClient.CancelAsync();
}

... throws the following exception:

System.Net.WebException

The request was aborted: The request was canceled.

   at System.Net.ConnectStream.EndRead(IAsyncResult asyncResult)
   at System.Net.WebClient.DownloadBitsReadCallbackState(DownloadBitsState state, IAsyncResult result)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at WpfApp1.MainWindow.<ButtonStart_Click>d__2.MoveNext() in WpfApp1\MainWindow.xaml.cs:line 19

How can I cancel a task started with await WebClient.DownloadFileTaskAsync() without throwing an exception?

5
  • How would you know the download was aborted? Commented Apr 26, 2019 at 19:23
  • 1
    I doubt that first block of code really works fine: you'll be disposing the _webClient almost instantly, which means it's probably disposed by the time CancelAsync() gets called. I'd guess that CancelAsync is returning an error in a Task which you're ignoring because you're not awaiting it. So it appears to work fine, but the request isn't actually getting canceled. Commented Apr 26, 2019 at 20:55
  • @PauloMorgado By checking the AsyncCompletedEventArgs.Cancelled status in the WebClient.DownloadFileCompleted event handler. Commented Apr 29, 2019 at 7:12
  • 1
    @StriplingWarrior Well the download is stopped, that surely means the request is cancelled, isn't it? Commented Apr 29, 2019 at 7:16
  • 1
    @Otiel: Touché. I was just thinking your download shouldn't complete when the request isn't canceled. Normally if you have a using statement and neglect to await async tasks you'll get burned for it. But based on this comment and the source code, WebClient basically ignores getting disposed. Commented Apr 29, 2019 at 15:04

2 Answers 2

7

The exception is exactly how it's supposed to work.

If you don't want that exception propagating out of your event handler, then catch the exception.

Sign up to request clarification or add additional context in comments.

Comments

3

You can catch the exception like this:

using (_webClient = new WebClient())
{
    try
    {
        await _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
    catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled)
    {
        Console.WriteLine("Cancelled");
    }
}

Update: How to change the default behavior of CancelAsync, to avoid having to catch an exception:

public static Task<bool> OnCancelReturnTrue(this Task task)
{
    return task.ContinueWith(t =>
    {
        if (t.IsFaulted)
        {
            if (t.Exception.InnerException is WebException webEx
                && webEx.Status == WebExceptionStatus.RequestCanceled) return true;
            throw t.Exception;
        }
        return t.IsCanceled;
    }, TaskContinuationOptions.ExecuteSynchronously);
}

Usage example:

bool cancelled = await _webClient.DownloadFileTaskAsync(
    "https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin").OnCancelReturnTrue();
if (cancelled) Console.WriteLine("Cancelled");

2 Comments

Thanks. That's already what I'm doing, just wanted to know if there was a cleaner way to cancel the task without throwing an exception.
@Otiel it throws exception by design. Apparently cancellation is perceived as something exceptional. If you want to get fancy you could concoct some custom extension method to change this behavior to your likings. I have updated my answer with an example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.