我有一个带有textBox output_txtB 的表单,我想运行cmd,执行一些命令并在文本框中显示标准输出。我想在执行每个命令后检索标准输出,而子进程正在工作。我在这里找到了一些解决方案:
Redirect the output (stdout, stderr) of a child process to the Output window in Visual Studio
C# get process output while running
但它没有解决我的具体问题,因为我想在文本框中显示标准输出(不在控制台或其他地方),并在执行每个命令后立即执行 - 我不想在之后检索完整输出流程退出。
我试图在子进程中使用OutputDataReceived事件,如上面的链接所示,但是有一个问题,当我想引用在另一个线程上创建的文本框时 - 它会抛出一个InvalidOperationException。 这是我的代码:
Process process = new Process();
process.EnableRaisingEvents = true;
process.StartInfo.FileName = "cmd";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.OutputDataReceived += (sender, args) =>
{
//here throws an exception
output_txtB.AppendText(args.Data + Environment.NewLine);
};
process.Start();
process.BeginOutputReadLine();
//commands I want to execute
process.StandardInput.WriteLine("example.exe");
process.StandardInput.WriteLine("script.bat");
process.StandardInput.WriteLine("ipconfig /all");
process.StandardInput.WriteLine("dir");
process.StandardInput.Close();
process.WaitForExit();
例外的其他信息是:
跨线程操作无效:控制'output_txtB'从其创建的线程以外的线程访问。
任何想法,如何检索子进程的标准输出,并在子进程工作时将其显示在文本框中?
编辑: 请参阅“example.exe”代码:
Console.WriteLine("line 1");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("line 2");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("line 3");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("line 4");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("line 5");
我想要实现的目标是每次在文本框 行X 中显示,该过程在标准输出中接收它。但即使我使用Stefano的解决方案,似乎OutputDataReceived事件会在进程退出后触发并显示完整的进程输出。
答案 0 :(得分:1)
在UI线程外部对UI对象进行操作时,这是Windows窗体和WPF中的常见错误。
将Process.OutputDataReceived更改为:
process.OutputDataReceived += (s, args) =>
{
this.output_txtB.BeginInvoke((MethodInvoker)delegate() { this.output_txtB.AppendText(args.Data + Environment.NewLine); });
};
答案 1 :(得分:1)
你应该使用Invoke()。
在其他主题中,您无法更新自己的观看次数。修改视图的逻辑应该在UI线程上完成,而Invoke()仅用于此。
很可能你在后台运行一个线程并从进程中读取输出。在此线程中,如果要更新UI,请使用Control.Invoke()方法,如下所示。
我更喜欢这种语法,因为" natural"它是:
myControl.Invoke((Action)delegate
{
//You can put your UI update logic here
myControl.Text = "Hello World from a different thread";
});
此外,您不需要直接在正在修改的控件上调用Invoke。在任何控件中(或直接在您的表单上)调用invoke将意味着其中的代码将在UI线程上运行,这意味着您也可以更新其他UI。
myControl.Invoke((Action)delegate
{
//You can put your UI update logic here
myControl.Text = "Hello World from a different thread";
myOtherControl.Text = "Look I can update other controls";
});
myForm.Invoke((Action)delegate
{
myControl.Text = "Can even call invoke from my Form";
}