views:

532

answers:

3

Hi guys,

A while ago I asked for some help about setting up a progress bar to show to the user how much progress has been made on the given set of files and folders.

See here:

First Attempt At Background worker.

As a result I managed to get it to work but on some occasions it partially fills up and sometimes not at all. Exiting with the following exception?

TargetInvocationException was unhandled .. Exception has been thrown by the target of an invocation.

Bombing out to this line of code in my program.

    [MTAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm()); <-- Exception raised at this line !!!
    }

It was also highlighed in green, something I have never seen before. I also tried changing the attribute also from STA to MTA. If that does something or of it doesn't ... then please do tell. As i am lead to believe it supposed to be headed up by [MTAThread].

My application does not raise the error if I do not report progress but when i do above is always guranteed to happen. I cleaned up my DoWork event handler so that it makes no cross thread calls to the thread where any control is defined i.e a Label. I removed these even tho' they work even tho' Albahari.com told me they shouldn't work and shouldn't be there. So i removed then and stuck with this ... It's much simpler, cleaner, tidier and thread-safe with the important part shown.

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        .
        .

        for (int i = 0; i < allFiles.Length; i++)
        {
            localSize += allFiles[i].Length;
            // a label used to say   what file is being processed.
            //toolStripStatusLabel.Text = allFiles[i].Name; 
            localFiles++;                
            worker.ReportProgress((100 * i) / allFiles.Length);
        }

        .
        .
    }

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // this event handler is also implemented in my application.
    }

    private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        fileRetrievalProgressBar.Value = Math.Min(e.ProgressPercentage, 100);
    }

Above is what my event handlers concerned look like. Hopefully that should be enough information for someone to help me out and as alot of people I have asked are all drawing a blank here.

Has my problem got something got something to do with the way am calling my progress bar or something to do woth invoke or begininvoke .. something along them alines. i'm just guessing but doesn't the background worker automatically do this for you. Or is this automatically implied and something that I should reply upon as I do not know when this might happen. So will I have to do something with like ...

this.Invoke or this.BeginInvoke

Hovering over both of the above peices of code reveals an interesting tooltip that makes alot of sense but am I struggling on how to implement. As I believe an anonymous method is used ??? I am using the below my exmaple for the prior statement.

    // Takes callbacks from the GetTotalSize() method
private void ProgressCallback(float p)
{
    // Invokes update progress bar on GUI thread:
    this.BeginInvoke(updateProgMethod, new object[] { p });
}

// Actually updates the progress bar:
private void UpdateProgress(float p)
{
    progressBar.Value = (int)(p * (progressBar.Maximum - progressBar.Minimum)) + progressBar.Minimum;
}

------> Thanks to a chap called 'Cecil Has A Name'

That should be it. Looking forward to getting some reponses on this small problem of mine.

+1  A: 

The code you posted above looks fine (although you shouldn't need to use BeginInvoke() in the ProgressChanged event hander). The reason that it looks like Application.Run() is throwing the exception is because you don't have any other try/catches in the call stack.

The exception you list isn't the actual problem that is occurring, but just a wrapper. If you look at the InnerException of the thrown exception, it should give you more information about what is going on.

If you need more help, you should probably post the details of the inner exception, and we can help more.

Based on the inner exception, it looks like the value that you're setting of the progress bar is not between the minimum and maximum values of the progress bar. Have you set the minimum and maximum, and verified that all values set into the progress bar are between them?

Andy
I have posted the excpetion in a link above.The problem has something got to do with the way the values are being generated, atm they are being approximated out of a 100% and then they are applied as values. Maybe a simple and literal approach might be needed. A simpler way to generate the value that is needed to the fill the bar upto and apply this value onto the bar. I think I know how to fix this .....
IbrarMumtaz
A: 

In Visual Studio under the debug menu click "Exceptions...". Check the Common Language Runtime Exceptions checkbox and run your code. That way when the exception is thrown the process will break and highlight the line in green.

It is also recommended that you have try catch blocks to attempt to recover from, or at least log, errors.

Ragepotato
if you mean 'user-unhandled' then yeah this is already checked. The throw column is "Not" checked.
IbrarMumtaz
Sorry for the omission, I mean't the throw column.
Ragepotato
+1  A: 

First of all, you actually can update that status label in a thread-safe manner using the ProgressChanged event you're already using; BackgroundWorker.ReportProgress has an overload that takes an object parameter to pass additional information for just this purpose.

Secondly, if the problem is that you're trying to set your progress bar's value beyond the maximum, it may be that your hard-coded value of 100 does not correspond to the progress bar's Maximum property (though this seems unlikely since 100 is the default value, I believe).

So my suggested changes to your code would be:

private void worker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i < allFiles.Length; i++)
    {
        localSize += allFiles[i].Length;
        localFiles++;

        // this you can pass to ReportProgress
        string statusText = allFiles[i].Name;

        int progressPercentage = (100 * i) / allFiles.Length;
        worker.ReportProgress(progressPercentage, statusText);
    }
}

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // now that this is being done here, it is thread-safe
    string statusText = e.UserState as string;
    if (!string.IsNullOrEmpty(statusText))
    {
        toolStripStatusLabel.Text = statusText;
    }

    // your progress should be capped at the ProgressBar's Maximum,
    // rather than 100
    int progress = e.ProgressPercentage;
    int maxProgress = fileRetrievalProgressBar.Maximum;
    fileRetrievalProgressBar.Value = Math.Min(progress, maxProgress);
}
Dan Tao
BackgroundWorker already has try/catch in its logic, adding another one is not necessary. You can retrieve the exception, if any, in RunWorkerCompleted.
liggett78
@liggett78: I did not realize that! Thanks for pointing that out.
Dan Tao
This is exactly what I been thinking abt ... Thanks Dan. I'm going to give that a go tonite and see where I get. I reckon this and a few more tweaks here and there should 'ought to do it.Thanks for pointing that out Ligget78. I was ware of this already.
IbrarMumtaz
Worked Beautiful ! TY
IbrarMumtaz