BackgroundWorker: InvalidOperationException in RunWorkerCompleted

I have WinForm with backgroundWorker:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using SeoTools.Utils; namespace SeoTools.UI { public partial class UIProgress : Form { public UIProgress(DoWorkEventHandler doWorkEventHandler, RunWorkerCompletedEventHandler runWorkerCompletedEventHandler) { InitializeComponent(); this.backgroundWorker.WorkerReportsProgress = true; this.backgroundWorker.WorkerSupportsCancellation = true; this.backgroundWorker.DoWork += doWorkEventHandler; this.backgroundWorker.RunWorkerCompleted += runWorkerCompletedEventHandler; } public void Start() { var foo = SynchronizationContext.Current; backgroundWorker.RunWorkerAsync(); } private void btnStop_Click(object sender, EventArgs e) { btnStop.Enabled = false; btnStop.Text = "Stopping..."; backgroundWorker.CancelAsync(); } private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { try { wdgProgressBar.Value = e.ProgressPercentage; if (this.Visible == false) { this.ShowDialog(); this.Update(); } } catch (InvalidOperationException) {} } private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.Hide(); //Here I get a InvalidOperationException this.Dispose(); } } } 

The first time I run this, it works great. But the second time I get an InvalidOperationException when this.Hide() called.

"Additional Information: Invalid cross-flow operation: Control 'UIProgress' is drawn from a stream other than the stream on which it was created."

The strange thing about starting foo in Start () the first time is with WindowsFormsSyncronizationContext , but in the second try System.Threading.SyncronizationContext .

The application I'm writing is an ExcelDna plugin.

EDIT

Start () is called as follows:

  UIProgress uiProgress = new UIProgress( delegate(object sender, DoWorkEventArgs args) { .... }, delegate(object sender, RunWorkerCompletedEventArgs args) { ... } ); uiProgress.Start(); 
+6
source share
6 answers

Your Start () method must be called from code that runs in the user interface thread to enable BackgroundWorker to work correctly. This was not when you received this exception. Add security code to your method so you can diagnose this case:

  public void Start() { if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) { throw new InvalidOperationException("Bug! Code called from a worker thread"); } backgroundWorker.RunWorkerAsync(); } 

Now you can set a breakpoint in the throw statement and use the debugger's Call Stack window to find out why this happened.

+7
source

You are invoking a user interface operation in a background thread. This is the reason for this exception. I would use a completely different method to make progress most appropriate - use Task with IProgress. Another way to use this is: `private void backgroundWorker_ProgressChanged (object sender, ProgressChangedEventArgs e) {

  this.UpdateOnMainThread( ( ) => { wdgProgressBar.Value = e.ProgressPercentage; if ( this.Visible == false ) { this.ShowDialog( ); this.Update( ); } } ); } private void UpdateOnMainThread( Action action ) { if ( this.InvokeRequired ) { this.BeginInvoke( ( MethodInvoker ) action.Invoke); } else { action.Invoke( ); } } private void backgroundWorker_RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e ) { this.UpdateOnMainThread( ( ) => { this.Hide( ); //Here I get a InvalidOperationException this.Dispose( ); } ); }` 
+3
source

Use the BeginInvoke() method on the form:

// http://msdn.microsoft.com/en-us/library/0b1bf3y3(v=vs.110).aspx

  private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.BeginInvoke(new InvokeDelegate(InvokeMethod)); } public delegate void InvokeDelegate(); public void InvokeMethod() { this.Hide(); this.Dispose(); } 
+1
source

I think you can find help here: BackgroundWorker hide the form window after completion . However, remember to separate the BackgroundWorker events and stop the BackgroundWorker as described here: The correct way to dispose of BackGroundWorker . The problem may be in

 this.Dispose(); 

in the backgroundWorker_RunWorkerCompleted event. With this, you delete the form page. Is that what you want to do? Or do you want to get rid of BackgroundWorker? Eliminating the form page all resources are released, so the second time may be this.Hide(); .

For more information, you can see these links: C # Form.Close vs Form.Dispose and Form. Placement method

+1
source

You should check this link. How to update GUI from another thread in C #?

There are probably all possible answers

Hope for this help

+1
source

You start a main thread call from a thread that cannot control the user interface. The easiest way is to use an anonymous delegate call.

Change this:

  if (this.Visible == false) { this.ShowDialog(); this.Update(); } 

For this:

 this.Invoke((MethodInvoker) delegate { if (this.Visible == false) { this.ShowDialog(); this.Update(); } }); 

This is not the most optimized way, but it does work incredibly fast without significant conversion. :)

+1
source

Source: https://habr.com/ru/post/979247/


All Articles