C# 在进程运行时在文本框中显示子进程的标准输出
C# Display standard output of the child process in the textbox while process is working
我有一个带有文本框 output_txtB 的表单,我想 运行 命令,执行一些命令并在文本框中显示标准输出。我想在执行每个命令后检索标准输出,而子进程正在运行。我在这里找到了一些解决方案:
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 事件会在进程退出后触发并显示完整的进程输出。
这是 Windows Forms 和 WPF 在 UI 线程外对 UI 对象进行操作时的常见错误。
将 Process.OutputDataReceived 更改为:
process.OutputDataReceived += (s, args) =>
{
this.output_txtB.BeginInvoke((MethodInvoker)delegate() { this.output_txtB.AppendText(args.Data + Environment.NewLine); });
};
你应该使用 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";
}
我有一个带有文本框 output_txtB 的表单,我想 运行 命令,执行一些命令并在文本框中显示标准输出。我想在执行每个命令后检索标准输出,而子进程正在运行。我在这里找到了一些解决方案:
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 事件会在进程退出后触发并显示完整的进程输出。
这是 Windows Forms 和 WPF 在 UI 线程外对 UI 对象进行操作时的常见错误。
将 Process.OutputDataReceived 更改为:
process.OutputDataReceived += (s, args) =>
{
this.output_txtB.BeginInvoke((MethodInvoker)delegate() { this.output_txtB.AppendText(args.Data + Environment.NewLine); });
};
你应该使用 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";
}