2014-10-05 94 views
-1

對於太長時間,我一直在嘗試運行一個外部.bat文件(爲某些統計處理調用R腳本),並將控制檯重定向到U.I.在主線程處於活動狀態時BackgroundWorker線程不會觸發

我覺得我很接近,但正如我已經得到它的工作,我遇到了一個相當大的問題!也就是說:只有當主線程結束時(通過:return;),而不是Thread.Sleep或.WaitOne()等時,纔會進行血腥的工作。

這是我在主線程中的代碼。

string batLoc = ALLRG___.RSCRBIN_LOC + "current.bat"; 
BackgroundWorker watchboxdWorker1 = new BackgroundWorker(); 
watchboxdWorker1.DoWork += frmC.WatchboxWorker1_WatchExt; 

frmC.wbResetEvent = new AutoResetEvent(false); 
watchboxdWorker1.RunWorkerAsync(batLoc); 

//Thread.Sleep(1000*20); 
//frmC.wbResetEvent.WaitOne(); 
return; 

請注意註釋掉Sleep和/或WaitOne()指令。如果我嘗試並使用這些BackgroundWorker執行,但更新U.I的'事件'不會。

在我的表單的代碼(以上FRMC)如下,

public void WatchboxWorker1_WatchExt(object sender, DoWorkEventArgs e) 
    { 
     string exeLoc = (string) e.Argument; 

     string arg1 = exeLoc;    
     string arg2 = ""; 

     ProcessStartInfo pStartInfo = new ProcessStartInfo(); 

     pStartInfo.FileName = exeLoc; 
     pStartInfo.Arguments = string.Format("\"{0}\" \"{1}\"", arg1, arg2); 

     pStartInfo.WorkingDirectory = Path.GetDirectoryName(exeLoc); 
     pStartInfo.CreateNoWindow = true; 
     pStartInfo.UseShellExecute = false; 

     pStartInfo.RedirectStandardInput = true; 
     pStartInfo.RedirectStandardOutput = true; 
     pStartInfo.RedirectStandardError = true; 

     Process process1 = new Process(); 

     process1.EnableRaisingEvents = true; 

     process1.OutputDataReceived += new DataReceivedEventHandler(wbOutputHandler); 
     process1.ErrorDataReceived += new DataReceivedEventHandler(wbErrorHandler); 

     process1.StartInfo = pStartInfo; 
     process1.SynchronizingObject = rtbWatchbox; 

     process1.Start(); 
     process1.BeginOutputReadLine(); 
     process1.BeginErrorReadLine(); 

     process1.StandardInput.Close(); 

     process1.WaitForExit(); 

     wbResetEvent.Set(); 

    } 

    public void wbOutputHandler(Object source, DataReceivedEventArgs outLine) 
    { 
     int x = 0; 

     if (!String.IsNullOrEmpty(outLine.Data)) 
     { 
      rtbWatchbox.AppendText(outLine.Data); 
     } 

    } 

    public void wbErrorHandler(Object source, DataReceivedEventArgs outLine) 
    { 
     int x = 0; 

     if (!String.IsNullOrEmpty(outLine.Data)) 
     { 
      rtbWatchbox.AppendText(outLine.Data); 
     } 
    } 

我的問題是 -

的wbOutputHandler和wbErrorHandler得到很好地解僱作爲控制檯更新 - 但只有當主如果我在主線程中使用Thread.Sleep或.WaitOne()將控制傳遞給BackgroundWorker(WatchboxWorker1_WatchExt),那麼代碼將成功運行,但wbOutputHandler和wbErrorHandler方法根本不會被觸發。實際上,如果我執行Thread.Sleep(10 * 1000),那麼外部程序按計劃開始運行,傳遞10秒,然後當主UI線程退出時,我會立即得到一個大的巨大更新。

我不想讓我的主線程關閉,我想在工作完成後繼續在那裏做東西!

[用於替代方法是一個更好的辦法當然高興]「幫我堆棧溢出,你是我唯一的希望!」

+2

您不會登錄到「控制檯」,而是登錄到某個「RichtTextBox」右邊?所以這是一個WinForms應用程序 - 然後問題是:運行UI的代碼在哪裏(有點像Application.Run(new Form1());')?順便說一句:當然UI不會更新,如果你阻止它的線程... – Carsten 2014-10-05 06:35:43

+0

PS:如果你不以這種方式退出你的應用程序,你不需要阻止! – Carsten 2014-10-05 06:36:44

+0

謝謝Carsten你神奇地有幫助。 – 2014-10-06 09:40:14

回答

0

答案是將backgroundWorker放入另一個爲UI線程創建的backgroundWorker中。考慮到將控制檯輸出打印到用戶界面的相關簡單要求,我認爲這很複雜!

我現在從UI叫我的功能如下 -

 private void btInsertBCModls_Click(object sender, EventArgs e) 
     { 

      BackgroundWorker bw = new BackgroundWorker(); 
      bw.DoWork += RC2___Scratchpad4.BC_RunExistingBCModel; 

      bw.RunWorkerAsync(this); 

     } 

接下來,我用任何RichTextBox的委託& Invoke方法,我需要從另一個線程更新 -

delegate void UpdateWriteboxCallback(String str); 

    public void wbWriteBox(string WriteString) 
    { 
     if (!String.IsNullOrEmpty(WriteString)) 
     { 
      if (rtbWatchbox.InvokeRequired) 
      { 
       UpdateWriteboxCallback at = new UpdateWriteboxCallback(wbWriteBox); 
       this.Invoke(at, new object[] { WriteString }); 
      } 
      else 
      { 
       // append richtextbox as required 
      } 
     } 
    } 

然後從在我的功能我使用另一個BackgroundWorker來運行控制檯的東西 -

public static void BC_RunExistingBCModel(object sender, DoWorkEventArgs e) 
    { 
      RC2___RhinegoldCoreForm frmC = e.Argument as RC2___RhinegoldCoreForm; 


      BackgroundWorker watchboxWorker = new BackgroundWorker(); 
      watchboxWorker.DoWork += frmC.WatchboxWorker_RunProc; 

      watchboxWorker.RunWorkerAsync(batLoc); 

      while (watchboxWorker.IsBusy) 
       Thread.Sleep(50); 

      frmC.UpdateRGCoreStatusBox4("Executed script " + m + "... "); 

    } 

而DoWork函數又調用上面的wbWriteBox函數。

public void WatchboxWorker_RunProc(object sender, DoWorkEventArgs e) 
    { 
     string exeLoc = (string) e.Argument; 

     string arg1 = exeLoc; 
     string arg2 = ""; 

     ProcessStartInfo pStartInfo = new ProcessStartInfo(); 

     pStartInfo.FileName = exeLoc; 
     pStartInfo.Arguments = string.Format("\"{0}\" \"{1}\"", arg1, arg2); 

     pStartInfo.WorkingDirectory = Path.GetDirectoryName(exeLoc); 
     pStartInfo.CreateNoWindow = true; 
     pStartInfo.UseShellExecute = false; 

     pStartInfo.RedirectStandardInput = true; 
     pStartInfo.RedirectStandardOutput = true; 
     pStartInfo.RedirectStandardError = true; 

     Process process1 = new Process(); 

     process1.EnableRaisingEvents = true; 

     process1.OutputDataReceived += (s, e1) => this.wbWriteBox(e1.Data); 
     process1.ErrorDataReceived += (s, e1) => this.wbWriteBox(e1.Data); 

     process1.StartInfo = pStartInfo; 
     process1.SynchronizingObject = rtbWatchbox; 

     process1.Start(); 
     process1.BeginOutputReadLine(); 
     process1.BeginErrorReadLine(); 

     process1.StandardInput.Close(); 

     process1.WaitForExit(); 

     //wbResetEvent.Set(); 

    } 

唷!一個棘手的解決方案,一個容易定義的問題。如果有人有更好的辦法,讓我知道。 感謝Carsten的所有幫助 - 宏偉。

相關問題