2014-12-07 87 views
6

我有一個BackgroundWorker一個WinForm:的BackgroundWorker:出現InvalidOperationException在RunWorkerCompleted

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(); 
     }  
    } 
} 

我第一次運行這個它工作正常。但撥打this.Hide()時第二次獲得InvalidOperationException

「附加信息:跨線程操作無效:控制'UIProgress'從其創建的線程以外的線程訪問。」

奇怪的是在開始第一次運行foo()從一個WindowsFormsSyncronizationContext,但在第二次嘗試這是一個System.Threading.SyncronizationContext

我正在寫的應用程序是一個ExcelDna插件。

編輯

start()被稱爲是這樣的:

UIProgress uiProgress = new UIProgress(
       delegate(object sender, DoWorkEventArgs args) 
       { 
        .... 
       }, 
       delegate(object sender, RunWorkerCompletedEventArgs args) 
        { 
         ... 
        } 
      ); 
      uiProgress.Start(); 
+0

如何調用'Start'? – kennyzx 2014-12-07 15:23:00

+0

@kennyzx我已更新我的問題 – 2014-12-07 17:21:24

+1

我在SyncronizationContext上發現了一箇舊的[post](http://blogs.msdn.com/b/mattdotson/archive/2006/02/13/531315.aspx),該技術,你可以保存'WindowsFormsSyncronizationContext'供以後使用。不知道如何切換到另一個'SyncronizationContext',也許是Excel-DNA環境...需要一些艱苦的調試時間。 – kennyzx 2014-12-08 13:41:25

回答

7

你的start()方法必須從UI線程上運行,允許BackgroundWorker的正常運行代碼調用。這不是當你得到這個例外。保護代碼添加到您的方法,這樣你就可以診斷這個硬傷:

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

現在,您可以設置throw語句斷點,並使用調試器的調用堆棧窗口,找出爲什麼發生這種情況。

+0

InvokeRequired對我來說總是假的。 – 2014-12-07 17:17:25

+0

這意味着UIProgress對象是在錯誤的線程上創建的。代碼已更新。它在發佈時更具普遍性,但是當您將測試放入構造函數時,您會得到更好的堆棧跟蹤。 – 2014-12-12 11:33:22

1

使用表單上的BeginInvoke()方法:

// 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(); 
    } 
3

您正在呼籲後臺線程UI操作。這是該例外的原因。我會使用完全不同的方法使進度形式最好的是使用Task with IProgress。另一種方法是使用此: `私人無效backgroundWorker_ProgressChanged(對象發件人,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(); 
    }); 

}` 
1

我想你可以在這裏找到一些幫助:BackgroundWorker hide form window upon completion。 但是,不要忘記分離BackgroundWorker事件並停止BackgroundWorker它自己像這裏解釋:Proper way to Dispose of a BackGroundWorker。 這個問題可以在

this.Dispose(); 

backgroundWorker_RunWorkerCompleted事件。因爲你正在處理表單頁面。那是你想要做的嗎?或者你想處置BackgroundWorker?處理表單頁面所有資源都被釋放,所以第二次做this.Hide();可能是一個錯誤。

欲瞭解更多信息,你可以看到這個鏈接:C# Form.Close vs Form.DisposeForm.Dispose Method

1

你'從無法操作UI的線程調用主線程。 最簡單的方法是使用匿名委託調用。

更改此:

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

對於這一點:

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

這不是最優化的方式,但赫然快速做這項工作沒有太多的重新編碼。 :)

相關問題