Brad
Brad

Reputation: 2185

C# download file with progress indicator and then install file

I'm trying to write a C# Windows application which downloads a file (waits for that download to complete - providing some indicator of progress) and then executes the downloaded file. I think I either need:

The former (synchronous download) seems to do what I want but I can't find a way to indicate progress any way and it appears to make my program non-responsive (like it's hung) - which may cause users to close the application instead of waiting for it to finish downloading.

The later (asynchronous download) I've been able to accomplish with a progress indicator etc but what happens is that the download kicks off and my application immediately tries to install it before its finished downloading which of course fails.

So whats the best way to accomplish this? Below is the code I presently have for the async download with progress bar.

Edit 4/10/2012

The old code was getting to cumbersome to work around so here is what I presently have. It works except that it doesn't update the progress bar.

    private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
    {

            List<App> FilesToDownload = e.Argument as List<App>;

            foreach (App Program in FilesToDownload) // Loop through List with foreach
            {
                //string Text = ;
                UpdateRichTextBox("Downloading " + Program.Filename + "... Please wait");

                string Source = Program.DownloadLocation + "/" +  Program.Filename;
                string Destination = System.AppDomain.CurrentDomain.BaseDirectory + Program.Filename;

                WebClient webClient = new WebClient();
                webClient.DownloadFile(Source, @Destination);
            }

            UpdateRichTextBox("File download complete");

            foreach (App Program in FilesToDownload) // Loop through List with foreach
            {
                string Filename = Program.Filename;
                string Arguments = Program.InstallParameters;

                if (Filename != "advisorinstaller.exe")
                {
                    Process p = new Process();
                    p.StartInfo.FileName = System.AppDomain.CurrentDomain.BaseDirectory + Filename;
                    p.StartInfo.Arguments = Arguments;
                    p.Start();
                    p.WaitForExit();
                }
                else
                {
                    MessageBox.Show("About to install Belarc Advisor. This may take 10-15 minutes to run - do not shutdown this program. Click OK when ready to proceed.");
                }
            }
    }

    private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        UpdateRichTextBox("Install Complete");

    }

Edit 4/11/2012

So I have edited my backgroundworker do work as follows:

        private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {

            List<App> FilesToDownload = e.Argument as List<App>;

            int counter = 0;
            int percent = 0;

            foreach (App Program in FilesToDownload) // Loop through List with foreach
            {
                percent = ((counter / FilesToDownload.Count) * 100);
                backgroundWorker1.ReportProgress(percent, "");

                UpdateRichTextBox("Downloading " + Program.Filename + "... Please wait");

                string Source = Program.DownloadLocation + "/" +  Program.Filename;
                string Destination = System.AppDomain.CurrentDomain.BaseDirectory + Program.Filename;

                WebClient webClient = new WebClient();
                webClient.DownloadFile(Source, @Destination);


                counter++;

            }

            //backgroundWorker1.ReportProgress(100, "Complete!");
    }

If I uncomment the last line the progress bar proceeds to 100%. But its not progressing using:

percent = ((counter / FilesToDownload.Count) * 100);
backgroundWorker1.ReportProgress(percent, "");

Any ideas why?

Upvotes: 0

Views: 5756

Answers (4)

Dennis
Dennis

Reputation: 20561

No need to wrap the WebClient in a BackgroundWorker as it is already asynchronous (using a thread from the thread-pool).

Note: The sample below was written in the editor, whilst I checked the MSDN documentation for the method signatures I could have easily have made a mistake.

public void InstallSecuniaPSI()
{
    var source = new Uri("http://www.webserver.com:8014/psisetup.exe");
    var dest = Path.Combine(Path.GetTemp(), "Download_SecuniaPSI.exe");

    var client = new WebClient();
    client.DownloadProgressChanged += OnDownloadProgressChanged;
    client.DownloadFileCompleted += OnDownloadComplete;
    client.DownloadFileAsync(source, dest, dest);   
}

private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    // report progress
    Console.WriteLine("'{0}' downloaded {1} of {2} bytes. {3}% complete"(string)e.UserState, e.BytesReceived, e.TotalBytesToReceive, e.ProgressPercentage);
}

private void OnDownloadComplete(object sender, AsyncCompletedEventArgs e)
{
    // clean up
    var client = (WebClient)sender;
    client.DownloadProgressChanged -= OnDownloadProgressChanged;
    client.DownloadFileCompleted -= OnDownloadComplete;

    if (e.Error != null)
        throw e.Error;

    if (e.Cancelled)
        Environment.Exit(1);

    // install program
    var downloadedFile = (string)e.UserState;
    var processInfo = new ProcessStartInfo(downloadedFile, "/S");
    processInfo.CreateNoWindow = true;

    var installProcess = Process.Start(processInfo);
    installProcess.WaitForExit();

    Environment.Exit(installProcess.ExitCode);
}

The only thing that needs to be sorted now is how to make the program wait; if it is a console program then call Console.ReadKey() after the InstallSecuniaPSI().

HTH,

Upvotes: 0

Philipp Schmid
Philipp Schmid

Reputation: 5828

Have you considered using ClickOnce to do this? It will solve your problem along with auto updates.

Check out this SO questions for places to get started: https://stackoverflow.com/questions/1635103/how-to-basic-tutorial-to-write-a-clickonce-app.

Upvotes: 0

Philipp Schmid
Philipp Schmid

Reputation: 5828

You can use the ManualResetEvent class to wait on your main thread (using WaitOne()) until your client_DownloadFileCompleted() event handler is called (which calls Set() on your object).

Upvotes: 0

ChrisWue
ChrisWue

Reputation: 19020

You can use a BackgroundWorker to perform a synchroneous download in the background. It supports progress reporting as well. This will avoid blocking your UI (because the download is running on a different thread).

Upvotes: 1

Related Questions