很长时间以来,我一直在尝试运行外部.bat文件(调用R脚本进行一些统计处理),并让控制台重定向到U.I。
我认为我很接近,但正如我已经开始工作一样,我遇到了一个相当大的问题!那就是:一旦主线程结束(通过: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在控制台更新时被触发 - 但只有在主线程退出时(使用return;)....如果我在主线程中使用Thread.Sleep或.WaitOne()将控制传递给BackgroundWorker(WatchboxWorker1_WatchExt),然后代码成功运行,但wbOutputHandler和wbErrorHandler方法根本没有被触发。
事实上,如果我执行Thread.Sleep(10 * 1000),那么外部程序按计划开始运行,10秒后通过,然后当主UI线程退出时,我会立即获得一个巨大的更新。
我不想让我的主线程关闭,我想在工人完成后继续做那些事情!
[当然对于更好的方法的替代方法感到高兴]
"帮我Stack Overflow,你是我唯一的希望!"
答案 0 :(得分:0)
答案是将backgroundWorker放在另一个为UI线程创建的backgroundWorker中。考虑到将控制台输出打印到UI的相对简单的要求,我觉得很复杂!
我现在通过UI调用我的函数,如下所示 -
private void btInsertBCModls_Click(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += RC2___Scratchpad4.BC_RunExistingBCModel;
bw.RunWorkerAsync(this);
}
接下来我使用委托&在我需要从另一个线程更新的任何richTextBox上调用方法 -
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提供的所有帮助 - 非常棒。