5

I'm new to using event handlers and backgroundworkers, so I may be missing something completely obvious here. Still, I've been trying to fix this for two days, so I thought I might as well see what anyone had to say.

I have a backgroundworker called SqlExpressDownloader. It starts running at the beginning of my program, the rest of the work runs, and then it should wait for the operations in the SqlExpressDownloader_DoWork() method to complete before continuing. The only problem is that for some reason whenever I do while(SqlExpressDownloader.IsBusy), it always responds as busy and therefore will wait forever.

The code for the event handler is here:

    private void SqlExpressDownloader_DoWork(object sender, DoWorkEventArgs e)
    {
        string sSource = string.Format("{0}\\{1}", Paths.Settings_Common, "sqlexpr_x64_enu.exe");
        Debug.WriteLine(sSource);
        Debug.WriteLine("http://www.elexioamp.com/Install/redistributables/sql2008r2express/sqlexpr_x64_enu.exe");
        if (!System.IO.File.Exists(sSource))
        {
            WebClient oWebClient = new WebClient();
            oWebClient.DownloadProgressChanged += DownloadProgressChanged;
            oWebClient.DownloadDataCompleted += DownloadComplete;

            oWebClient.DownloadFileAsync(new System.Uri("http://www.elexioamp.com/Install/redistributables/sql2008r2express/sqlexpr_x64_enu.exe"), sSource);

            while (oWebClient.IsBusy)
            {
                Thread.Sleep(100);
            }

            e.Result = "";
            DownloadFinished = true;
        }
    }

I have watched the code and have watched it complete this method. I even added a return after the DownloadFinished = true, but it still responds as busy. What I want to know is how to make the backgroundworker respond as not busy.

EDIT The events are all added in the constructor as shown here:

        SqlExpressDownloader = new BackgroundWorker();
        SqlExpressDownloader.DoWork += new DoWorkEventHandler(this.SqlExpressDownloader_DoWork);
        SqlExpressDownloader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.SqlExpressDownloader_RunWorkerCompleted);

The RunWorkerCompleteEventHandler looks like this:

    private void SqlExpressDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            Debug.WriteLine("The actions are complete.");
        }
        else
        {
            Debug.WriteLine("Error in completed work.");
        }
    }

But, when I debugged it last, it didn't actually trigger.

ijb109
  • 942
  • 1
  • 19
  • 30
  • How are you starting the worker? – Patrick Jul 11 '13 at 14:46
  • In the main method, I'm starting it with `SqlExpressDownloader.RunWorkerAsync()`. – ijb109 Jul 11 '13 at 14:48
  • Are you using a while loop similar to the one in the body of the method when checking the IsBusy of the worker? Does the RunWorkerCompleted event trigger when the method is complete? – Patrick Jul 11 '13 at 14:51
  • The while loop looks almost exactly like the one listed here. I updated information about the RunWorkerCompleted in the question. – ijb109 Jul 11 '13 at 15:12
  • Is there some other code that adds a handler to the DoWork event, perhaps in the .designer.cs file? Your code should work the way you have it. – Patrick Jul 11 '13 at 15:28
  • I can't find any, I've been looking. I know that this DoWork event is working because I've been debugging it. I don't know why it won't trigger the Runworker_completed method, something must be wrong there. – ijb109 Jul 11 '13 at 17:33
  • This question is answered here: http://stackoverflow.com/questions/14430979/why-backgroundworker-always-is-busy?rq=1 The problem is that the completed event is run in the main thread - you can't just sit there and wait for isBusy to go false. – Arunas Mar 04 '15 at 01:43

3 Answers3

3

Instead of querying SqlExpressDownloader.IsBusy in a loop, try subscribing to the RunWorkerCompleted event of the BackgroundWorker and place your code in there that should only occur after the DoWork event has completed.

You'll also have access to the RunWorkerCompletedEventArgs, which you can check to make sure no error was thrown from the DoWork portion of your BackgroundWorker.

    ...
    ...
    SqlExpressDownloader.RunWorkerCompleted += SqlExpressDownloader_RunWorkerCompleted;
    SqlExpressDownloader.RunWorkerAsync();
}

private void SqlExpressDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        // do something in response to the error
    }

    // stuff to do after DoWork has completed
}

I found Joe Albahari's tutorial helpful when I was learning how to use these.

Grant Winney
  • 65,241
  • 13
  • 115
  • 165
1

You can replace your code with more elegant async/await solution like this

private async Task SqlExpressDownloadAsync()
{
    string sSource = string.Format("{0}\\{1}", Paths.Settings_Common, "sqlexpr_x64_enu.exe");
    Debug.WriteLine(sSource);
    Debug.WriteLine("http://www.elexioamp.com/Install/redistributables/sql2008r2express/sqlexpr_x64_enu.exe");
    if (!System.IO.File.Exists(sSource))
    {
        WebClient oWebClient = new WebClient();
        oWebClient.DownloadProgressChanged += DownloadProgressChanged;
        oWebClient.DownloadDataCompleted += DownloadComplete;
        await oWebClient.DownloadFileTaskAsync(new System.Uri("http://www.elexioamp.com/Install/redistributables/sql2008r2express/sqlexpr_x64_enu.exe"), sSource);  
    }   
}
Alex S
  • 1,171
  • 1
  • 9
  • 25
  • I'm not familiar with tasks. Am I able to track the progress of the download like I am using event handlers on the backgroundworker? There's a GUI component involved in my project. I'll try this out. – ijb109 Jul 11 '13 at 14:52
  • This is not running async at the moment though. You should wrap it in a task.run – Patrick Jul 11 '13 at 14:58
  • Absolutely! I added missing event handles, they will will fire as with your code. – Alex S Jul 11 '13 at 14:59
  • @Patrick could you elaborate? The code awaits for DownloadFileTaskAsync – Alex S Jul 11 '13 at 15:00
  • @Patrick good question, how long do you think it will run? Please provide your answer as well. – Alex S Jul 11 '13 at 15:03
  • What's that suppose to mean? – Patrick Jul 11 '13 at 15:07
  • If File.Exist is checking a file at a UNC path for instance it could take, I dunno, a few seconds. What is your limit for running async? Is it a general number of lines under 8 that you don't find should take "too long"? – Patrick Jul 11 '13 at 15:08
  • If Paths.Settings_Common is a property that loads something from a database that can take some time as well. The fact is you don't know how long it will take since you can't be certain how things are implemented. – Patrick Jul 11 '13 at 15:10
  • @Patrick you are correct, I would either move the whole code to Task.Run or remove the check for file existence if really not needed here. I don't think that checking for file existence is a good way to tell that exact same version of file has been previously downloaded. – Alex S Jul 11 '13 at 15:13
1

I had a similar issue. DownloadASync would fire but .IsBusy would always stay on true.

This probably won't be a common problem, just thought I share my resolution.

I used

MessageBox.Show(new Form() { TopMost = true }, "", "")

This was the cause. I also tried:

var t = new Form() { TopMost = true };
MessageBox.Show(t, "", "");
t.Dispose();

This caused the same issue.

My code had multiple threads, I assume one of them must have gotten stuck, or perhaps the MessageBox(the new Form() { TopMost = true; } ) call created a stuck thread.

As soon as I removed that part, eg.

MessageBox.Show("", "");

Everything worked as expected again.

So maybe you are creating another thread somewhere that is causing your issue.

Kickass
  • 1,114
  • 9
  • 16