Search code examples
c++.netcommand-line-interfacemanualreseteventdownloadfileasync

Waiting DownloadFileAsync with ManualResetEvent c++/cli


i'm having a little but frustrating problem in my C++/CLI with windows forms application.

So the problem is that i have to download a file from a webserver by using a WebClient istance. Normally i use DownloadFile and not DownoadFileAsyn, but if i want to show a progress bar showing the progress of download file, i MUST use DownloadFileAsyn. So how can i wait the process of downloading until it's finished ?

The code is:

    ref class Example{

    private:

        static System::Threading::ManualResetEvent^ mre = gcnew System::Threading::ManualResetEvent(false);

    public:

        void Download();
        void DownloadFileCompleted(Object^ sender, System::ComponentModel::AsyncCompletedEventArgs^ e);
    };






void Example::Download(){

    WebClient^ request = gcnew WebClient;
    request->Credentials = gcnew NetworkCredential("anonymous", "anonymous");

    request->DownloadFileCompleted += gcnew System::ComponentModel::AsyncCompletedEventHandler(this,&FileCrypt::DownloadFileCompleted);

    request->DownloadFileAsync(gcnew Uri("ftp://ftp...."+remote_path),remote_file,mre);
    mre->WaitOne();


/*
BLOCK OF INSTRUCTIONS THAT I WANT TO RUN AFTER THE FILE DOWNLOAD IS COMPLETED

*/
}

void Example::DownloadFileCompleted(Object^ sender, System::ComponentModel::AsyncCompletedEventArgs^ e){
    MessageBox::Show("COMPLETED");
    mre->Set();
}

So when the download is completed, the program stop running and it doesn't run the block of instructions above written, after the mre->WaitOne() instruction. The DownloadFileCompleted() is not executed, in fact even the messagebox is shown.

Any ideas? I've serarched for this problem, and many people had it, but in c# only. And i've just "translated" the solution from c# to c++. But it doesn't work...


Solution

  • You cannot wait, that causes deadlock. The DownloadFileCompleted() method cannot run until your main thread goes idle and re-enters the dispatcher loop. But it isn't idle, it is stuck in the WaitOne() call. So the method cannot run and the MRE can't be set. Which in turn causes WaitOne() to never complete. A deadly embrace from which your program can never recover. One of the standard threading bugs.

    It isn't clear why you wait, there just is no point to the WaitOne() call at all as posted. You can simply delete it and everything works fine. Maybe there's actually some code after it in your real program, you must move that code into the DownloadFileCompleted() method.

    The general programming rule for threads that display UI applies here. It can never sleep and it can never block. Doing so makes the UI unresponsive and significantly raises the odds for deadlock. UI is event-driven, events triggered by for example the user moving the mouse or pressing a key. The code that runs when the event is fired can only run when the thread isn't doing anything else. The download completion is also signaled as an event.