Ask a questionAsk a question
 

QuestionCanceling async download on app termination

  • Wednesday, October 28, 2009 4:43 PMKalobok Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I need an advice on how to gracefully cancel an async download when the application is closing.

    Let's say I have a WebClient downloading some file via DownloadFileAsync(). I want to be sure the file is downloaded completely, so, I set DownloadFileCompleted to a handler which removes the file if e.Error or e.Cancelled is set. If I need to cancel the download I call CancelAsync() and the handler is called (although not immediately) and does its job. But not when the application is terminating. It looks like in this case the application closes before the handler is called. Even if I wait for !webclient.IsBusy after CancelAsync().

    I solved this problem by moving the cancel/error handling code to a separate function and now my code looks like:

    private void MyOnDownloadCompleted(...)
    {
        if (e.Error != null  ||  e.Cancelled)
        {
            MyCancelAndErrorHandler();
        }
        ...
    }

    public void CancelDownload()
    {
        // If canceling the download NOT on the application termination,
        // this prevents the MyCancelAndErrorHandler from being called twice.
        webclient.DownloadFileCompleted -= MyOnDownloadCompleted;

        webclient.CancelAsync();
        while (webclient.IsBusy)
        {
            Thread.Sleep(100);
        }
        MyCancelAndErrorHandler();
    }

    This works, but looks ugly. I don't like the Sleep() loop and I don't like the mess with the MyCancelAndErrorHandler.

    Is there any better way to do this? Many centuries ago in Delphi there was a very nice function Application.ProcessMessages, which could hopefully help in this case if put after Sleep(), but AFAIK, there's no such function in .NET. Any other ideas?

All Replies

  • Wednesday, October 28, 2009 5:10 PMStephen Cleary Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    There's a few options.

    A quick hack is Application.DoEvents, an equivalent to the old Delphi Application.ProcessMessages. However, I can't recommend it. It's a hack.

    If you're writing a Windows Forms application, then you could have your main form hide itself instead of close until the download completes.

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ

    Microsoft Certified Professional Developer
  • Wednesday, October 28, 2009 5:57 PMKalobok Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    The problem is I'm trying to create just a class, not an application. So, it does not know if the application is a WF, WPF or just a console one. I suspect that Application.DoEvents will not help in this case too.

    And I suspect that any background thread will have the same problem, not just WebClient.

  • Wednesday, October 28, 2009 6:57 PMStephen Cleary Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    In that case, you need to define an explicit foreground thread to wait for the operation to complete. The ActionThread from the Nito.Async library (http://www.codeplex.com/NitoAsync) may be useful here: it's a foreground thread that supports completion notifications (e.g., WebClient.DownloadFileCompleted).

           -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ

    Microsoft Certified Professional Developer
  • Wednesday, October 28, 2009 7:19 PMKalobok Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks, this looks like what I was looking for or at least a good code to study. :)
  • Wednesday, October 28, 2009 9:16 PMKalobok Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    In case someone else is interested, I've found a rather simple alternative to DoEvents(). Not sure how reliable it is, but looks working. Just make the dispatcher invoke something. This causes the message queue to be processed so that the webclient's DownloadFileCompleted delegate can be called.

    System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(
        System.Windows.Threading.DispatcherPriority.Background,
        new Action(delegate{}));
  • Thursday, October 29, 2009 6:02 PMKalobok Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Found yet another surprise. It looks like Thread.Sleep() called in a background thread processes the posted messages. At least I can see some delegates called in the same thread after Sleep() is entered but before the thread really sleeps.