我有一个小应用程序,它有一个信息文本框,可以输出执行的命令。
目前我进行了设置,以便Console.Write[WriteLine]
正确添加到文本框中。该代码如下:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// bind the console output to the new text box
var writer = new TextBoxStreamWriter(x_OutputTextBox);
Console.SetOut(writer);
Console.SetError(writer);
}
}
internal class TextBoxStreamWriter : TextWriter
{
static TextBox _text = null;
public TextBoxStreamWriter(TextBox outputBox)
{
_text = outputBox;
}
public override void Write(string value)
{
base.Write(value);
_text.Dispatcher.Invoke(() => _text.AppendText(string.Format("{0} - {1}", DateTime.Now, value)));
}
public override void WriteLine(string value)
{
base.WriteLine(value);
_text.Dispatcher.Invoke(() => _text.AppendText(string.Format("{0} - {1}", DateTime.Now, value + Environment.NewLine)));
}
public override Encoding Encoding
{
get { return Encoding.UTF8; }
}
}
这一切都很好,但是当我尝试从批处理文件输出回声结果时,我遇到了问题。我已经查看过很多关于这个主题的问题/答案,例如:View Output in a Batch (.bat) file from C# code但这些选项并不适用于我。 当调用WriteLine
或Write
函数的覆盖时,它只会挂起并且不会写任何内容。我该如何解决?
我的Process
实施方式如下:
Process process = new Process();
process.OutputDataReceived += ReadOutput;
process.ErrorDataReceived += ReadErrorOutput;
process.EnableRaisingEvents = true;
//process.StartInfo = new ProcessStartInfo(@"cmd.exe", @"/c " + Path.Combine(Environment.CurrentDirectory, "BatchFile", "test.bat"))
process.StartInfo = new ProcessStartInfo(Path.Combine(Environment.CurrentDirectory, "BatchFile", "test2.bat"))
{
UseShellExecute = false,
Verb = "runas",
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
//WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "BatchFile", "Information.bat"),
WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "BatchFile") + @"\",
};
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
输出重定向:
private void ReadOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data == null)
return;
Console.Write(e.Data);
}
private void ReadErrorOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data == null)
return;
Console.Write(e.Data);
}
目前批处理文件非常简单:
echo off
echo Finding Information
echo .......................
echo foo bar is cool
echo this day kinda sucks
echo .......................
echo All Processes Complete!
答案 0 :(得分:1)
您的问题是您已经使UI线程陷入僵局。
您还没有共享一个完整的代码示例,以确定执行外部进程的代码的确切上下文,但根据您的问题描述,几乎可以肯定在UI线程中运行的某些代码中找到它,例如Button
对象的Click
事件处理程序,如下所示:
private void Button_Click(object sender, RoutedEventArgs e)
{
Process process = new Process();
process.OutputDataReceived += ReadOutput;
process.ErrorDataReceived += ReadErrorOutput;
process.EnableRaisingEvents = true;
process.StartInfo = new ProcessStartInfo(Path.Combine(Environment.CurrentDirectory, "BatchFile", "test2.bat"))
{
UseShellExecute = false,
Verb = "runas",
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "BatchFile") + "\\",
};
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
}
另一方面,当收到数据时,您尝试通过调用TextBox
将控制转移到UI线程(以便可以安全地访问Dispatcher.Invoke()
):
_text.Dispatcher.Invoke(...)
Invoke()
方法只能在UI线程可用于接收消息时完成。但是你的其他代码在那里阻止了UI线程,因为它等待进程完成,防止其他任何事情发生。
最明显的解决方法是删除调用process.WaitForExit();
。如果在执行该过程的方法中实际上没有剩余的代码可以执行,那么这是合适的。
但是,如果在代码中你没有共享,实际上有一些代码需要在进程完成时执行,你可以在不阻塞UI线程的情况下完成它。反过来做 的最明显的方法是为Process.Exited
事件添加一个处理程序,当事件退出时会引发(当然)。
当然,该代码也可能需要在UI线程上执行。在这种情况下,您需要再次致电Dispatcher.Invoke()
。事实上,这很好,但是代码确实开始变得有点笨拙和笨拙。另一种方法是将async
/ await
与Exited
事件相结合,以简化代码的外观。例如:
private async void Button_Click(object sender, RoutedEventArgs e)
{
Process process = new Process();
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
process.OutputDataReceived += ReadOutput;
process.ErrorDataReceived += ReadErrorOutput;
process.Exited += (sender, e) => tcs.SetResult(true);
process.EnableRaisingEvents = true;
process.StartInfo = new ProcessStartInfo(Path.Combine(Environment.CurrentDirectory, "BatchFile", "test2.bat"))
{
UseShellExecute = false,
Verb = "runas",
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "BatchFile") + "\\",
};
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
bool result = await tcs.Task;
// Do your additional post-process work here
}
这将有效地允许该方法暂停执行并等待进程退出,而不会实际导致线程本身被阻止。该方法返回await
表达式,然后在任务完成后稍后恢复执行该方法。