0

I have recently been working on an application where I wanted to display the progress of another thread in the status bar via the ToolStripProgressBar control that is contained in the StatusBar control. Before I attempted to add this code I originally had the code changing the text of a ToolStripStatusLabel control and to do this I used the Invoke method with delegates and everything worked fine. However, I found that when I attempted this with the ToolStripProgressBar the call to the status bar's Invoke method failed without a notification (no error, no exception, nothing). What I have since learned is that to use a progress bar in this way I had to use a BackgroundWorker control. So my code works but I don't understand why I couldn't use the Invoke method that already seemed to work.

Some examples of what worked and what didn't:

This worked

 public delegate void changeStatusMessage(String message);
 public changeStatusMessage changeStatusMessageDelegate;
 public void changeStatusMessageMethod(String message){
      if(statusbar.InvokeRequired){
           statusbar.Invoke(changeStatusMessageDelegate, new Object[] {message});
      }else{
           toolstripLabel.Text = message;
      }
 }

This did not work

 public delegate void incrementProgressBar(int value);
 public incrementProgressBar incrementProgressBarDelegate;
 public void incrementProgressBarMethod(int value){
      if(statusbar.InvokeRequired){
           statusbar.Invoke(incrementProgressBarDelegate, new Object[] {value});
      }else{
           toolstripProgress.Increment(value);
      }
 }

In the example that didn't work the InvokeRequired property is true so the Invoke method is called and then nothing happens. Where as I expected it to call the incrementProgressBarMethod again this time where InvokeRequired is false and thus allowing the Increment method to fire.

I would really like to know why this doesn't work. As I said I have already retooled to use a BackgroundWorker, I just want an explanation.

svick
  • 236,525
  • 50
  • 385
  • 514
JRSofty
  • 1,216
  • 1
  • 24
  • 49
  • 1
    There's nothing special about a TSPB that would hand you an easy explanation. Control.Invoke() tends to easily deadlock if the main thread is blocked so "nothing happens" is not uncommon. Stick with BackgroundWorker. – Hans Passant Oct 05 '11 at 08:28
  • I never said I wanted an "Easy" explanation, complicated ones would work as well. Telling me simply that Control.Invoke() can cause deadlocking isn't really enough for me to understand the reason. The Control.Invoke() is provided for a reason, why can't I use it for this one control, when a control handled in a similar way with the same code works without problem. – JRSofty Oct 05 '11 at 17:48
  • Post repro code that demonstrates the problem and anybody can test and you'll get your answer. – Hans Passant Oct 05 '11 at 17:51

1 Answers1

0

Invoke calls postmessage API and enqueue message on windows message. If UI thread is blocked, then you can have a deadlock, because it cant push queued message, nothing will happens. The other side of invoke required is not fired, and if something is waiting for it, bang, deadlock.

This is the reason you need to be careful with Invoke.

How to invoke a function on parent thread in .NET?

But your problem is on creation of the delegate, it is a null delegate, you need to create the delegate on same thread that it is being called by invoke, because other way, the underling system will fail on marshaling the delegate (its a pointer).

    private void changeStatusMessageMethod(String message)
    {
        if (this.InvokeRequired)
        {
            var changeStatusMessageDelegate = new changeStatusMessage(changeStatusMessageMethod);
            this.Invoke(changeStatusMessageDelegate, new Object[] { message });
        }
        else
        {
            toolstripLabel.Text = message;
        }
    }
    delegate void incrementProgressBar(int value);
    private void incrementProgressBarMethod(int value)
    {
        if (this.InvokeRequired)
        {
            var incrementProgressBarDelegate = new incrementProgressBar(incrementProgressBarMethod);
            this.Invoke(incrementProgressBarDelegate, new Object[] { value });
        }
        else
        {
            toolstripProgress.Increment(value);
        }
    }

This works on dotnet framework v4

    private void button1_Click(object sender, EventArgs e)
    {
        var t = new System.Threading.Thread(new System.Threading.ThreadStart(x));
        t.Start();
    }

    private void x()
    {
        do
        {
            changeStatusMessageMethod(DateTime.Now.ToString());
            System.Threading.Thread.Sleep(1000);
        } while (true);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        var t = new System.Threading.Thread(new System.Threading.ThreadStart(y));
        t.Start();
    }

    private void y()
    {
        do
        {
            incrementProgressBarMethod(1);
            System.Threading.Thread.Sleep(1000);
        } while (true);
    }
Community
  • 1
  • 1
Luiz Felipe
  • 1,123
  • 8
  • 14