2

After recieving the cross thread exception error, I looked it up on the MSDN.

I tried implementing some of the code there but can't get the Callback to work.

addItemCallback d = new addItemCallback(addItem);

this is located in the addItem() method below.

I am trying to have a list of items add themselves to a listbox, eventually updating the form everytime one value is found, rather than all of them being added once the backgrounder has finished work.

private void startWork()
{        
    progressBar1.Value = 0;

    progressBar1.Maximum = 901242;

    backgroundWorker1.RunWorkerAsync();    
}

private void getList()
{
    if (pathFound)
    {
        for (int i = 0; i < numberOfPaths; i++)
        {
            Microsoft.Win32.RegistryKey mainPath = secondaryPath.OpenSubKey("application " + Convert.ToString(i));

            if (mainPath != null)
            {
                    this.addItem((string)mainPath.GetValue("Name"));
            }

            backgroundWorker1.ReportProgress(i);
        }
    }

    pathListBox.Sorted = true;
}

private void addItem(string item)
{
    if (this.pathListBox.InvokeRequired)
    {

        //addItemCallback d = new addItemCallback(addItem); 

        //not sure what this callBack is, can't get it to work, Callback isnt found.

        this.Invoke(d, new object[] { item });
    }

    else 
    {
        this.pathListBox.Items.Add(item);
    }
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    getList();
}

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

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    this.progressBar1.Visible = false;
}

WORK SO FAR

Code 1. When I use the background_doWork and call a method from it, the progressbar hangs in random spots and stops responding, on closing the form i get an object exception as I've closed the form while it is still trying to do work.

Code 2. When I place all the code in the background_doWork rather than call a method from it, the progress bar will work sometimes, every second or every 3rd attempt at running the program it finishes.

What would be causing this?

-----CODE 1-----------

    public Form1()
    {
        InitializeComponent();

        start();
    }

    int number = 900000;

    public void start()
    {
        progressBar1.Value = 0;

        progressBar1.Maximum = number;

        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        getList();
    }

    private void getList()
    {
        Microsoft.Win32.RegistryKey mainPath = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node");

        for (int i = 0; i < number; i++)
        {
            Microsoft.Win32.RegistryKey mainPath = secondaryPath.OpenSubKey("application " + Convert.ToString(i));

            if (mainPath != null)
            {
                this.addItem((string)mainPath.GetValue("Name"));
            }

            backgroundWorker1.ReportProgress(i);
        }
    }

    private void addItem(string item)
    {
        try
        {

            if (this.listBox1.InvokeRequired)
            {
                this.Invoke(new Action<string>(addItem), item);
            }

            else
            {
                this.listBox1.Items.Add(item);
            }
        }

        catch
        {
            MessageBox.Show("Error - Closed Object before it finished working.");
        }

        //this.steamGamesListBox.Sorted = true;
    }

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

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.progressBar1.Visible = false;
    }

------CODE 2 --------

    public Form1()
    {
        InitializeComponent();

        start();
    }

    int number = 900000;

    public void start()
    {
        progressBar1.Value = 0;

        progressBar1.Maximum = number;

        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        Microsoft.Win32.RegistryKey steamApps64 = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");

        for (int i = 0; i < number; i++)
        {
            Microsoft.Win32.RegistryKey steamApps = steamApps64.OpenSubKey("Steam App " + Convert.ToString(i));

            if (steamApps != null)
            {
                this.addItem((string)steamApps.GetValue("DisplayName"));
            }

            backgroundWorker1.ReportProgress(i);
        }
    }

    private void addItem(string item)
    {
        try
        {

            if (this.listBox1.InvokeRequired)
            {
                this.Invoke(new Action<string>(addItem), item);
            }

            else
            {
                this.listBox1.Items.Add(item);
            }
        }

        catch
        {
            MessageBox.Show("Error - Closed Object before it finished working.");
        }

        //this.steamGamesListBox.Sorted = true;
    }

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

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.progressBar1.Visible = false;
    }
deepseapanda
  • 3,717
  • 8
  • 32
  • 39

2 Answers2

3

You either need to define a custom delegate type (your addItemCallback), or simply use a generic Action delegate:

private void addItem(string item)
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new Action<string>(addItem), item);
        return;
    }

    this.pathListBox.Items.Add(item);
}

This is usually the simplest way to do it, since you don't need to introduce additional delegate types.

Note that the object[] parameter is defined with the params keyword, so you don't need to instantiate a new object array, but rather simply pass your arguments to the method.

vgru
  • 49,838
  • 16
  • 120
  • 201
  • Ah this works great! I only have one issue and that is when I run it, the list updates, but the progress bar gets stuck in the middle. It looks like the program can't get past this.Invoke(new Action(addItem), item); - once i close the program I get a object disposed exception (because I closed it), any ideas why it would be getting stuck here? – deepseapanda Feb 24 '12 at 10:33
  • Usually, it's better to use `BeginInvoke` instead of `Invoke` to avoid deadlocks (check my updated post). And I would also remove `pathListBox.Sorted = true;` from your background method (you should have no interaction with controls from that thread). I am not sure why this would happen "in the middle", though. I presume that you should be calling `backgroundWorker1.ReportProgress(100 * i / numberOfPaths);`, but right now you probably have around 50 paths so your processing actually ends there (that's my guess). Try making these changes and see how it goes. – vgru Feb 24 '12 at 10:40
  • Note that in this case you can use `BeginInvoke` because the order of items being added is not important, they will be inserted in place if `Sorted` property is set to `true`. On the other hand, if you have problems with this, you can try changing the structure completely: use the worker to fill a `List` (you can do it inside the background thread) and then pass the list to the `ListBox` when you're finished. Depending on the type of the work being done and the number of items, this might work well also. – vgru Feb 24 '12 at 10:47
  • Don't do this, it will be horribly slow and still makes the UI thread go catatonic. Let your worker fill a list of items, call pathListBox.Items.AddRange() in a RunWorkerCompleted event handler. – Hans Passant Feb 24 '12 at 10:50
  • using invoke, the list is updated 1 at a time, with the progress bar hanging at random locations. Using BeginInvoke fixed the hang, but as the list is updated, it stays all white, with the vertical scroll increasing, once progress bar has reached the end all the values are then shown :P – deepseapanda Feb 24 '12 at 10:51
  • @HansPassant: what will be horribly slow? Isn't `ProgressChanged` also invoked asynchronously (`Post` vs `Send`)? – vgru Feb 24 '12 at 10:56
  • Begin/Invoke is very expensive, takes about a millisecond. The UI thread is getting mercilessly hammered here by these requests and doesn't get around doing its regular duties anymore. Like painting and responding to input. "It stays all white". – Hans Passant Feb 24 '12 at 11:11
  • @cheeseman: It depends on the amount of work being done between these events. In your case, each iteration lasts very shortly and the UI thread doesn't have a chance to repaint, as Hans said. Switching back to `Invoke` would indeed give the thread enough time to refresh. And you should only get the `ObjectDisposedException` if you close the form before the worker has finished, so you are probably not cancelling the worker in that case (you should do that inside your `FormClosing` event or similar, check [this thread](http://stackoverflow.com/a/123791/69809) for an example). – vgru Feb 24 '12 at 11:22
  • Ah ok, yeah the only reason I close the form is because the progressbar hangs, I have no idea why...It hangs in Invoke, but works perfectly in BeginInvoke. The only issue is with the painting in BeginInvoke :P – deepseapanda Feb 24 '12 at 11:57
  • The code you posted doesn't look like it should hang. Is there anything else going on in this form? And is the code you posted completely equal to your actual code? – vgru Feb 24 '12 at 12:15
  • Everything is the same, it was a copy paste, just added the invoke in. The value of numberOfPaths in the loop is 901242, could this be the issue? – deepseapanda Feb 24 '12 at 12:29
  • Well, you certainly shouldn't hard-code this value inside `startWork()`, but instead use `numberOfPaths` to make sure it's the same value. You should try to use the debugger to find out where your app hangs. At that point, break execution and use the Threads window to select the "Worker Thread" (location should be inside `getList`), and try to examine where exactly it is waiting. – vgru Feb 24 '12 at 12:37
  • I can get it working sometimes :P Added some code to my main post of 2 different versions of code. I dont understand why it would work sometimes and not others :( – deepseapanda Feb 24 '12 at 14:24
0

add delegate void addItemCallback(string item);

Milan Halada
  • 1,943
  • 18
  • 28