Csharp/C#教程:同步捕获进程输出(即“发生时”)分享


同步捕获进程输出(即“发生时”)

我正在尝试启动一个进程并捕获输出,已经走了很远的路,但我还没有达到我想要的解决方案。

具体来说,我试图从我正在编写的小型实用程序应用程序重置我的开发机器上的IIS。 我通过实验得出结论,安全的方法是在子进程中运行iisreset.exe

如果在命令提示符下运行iisreset.exe ,则会在此过程中收到反馈。 运行iisreset需要几秒钟,并且会生成几行反馈,其间会有暂停。

我想捕获这些反馈并将其呈现在我的Windows窗体应用程序(在ListBox )中,并且我已经成功了。 我仍然担心的是,在子进程完成之前我不会得到它。 我想在创建行时立即逐行获取子进程的输出。

我曾尝试做我的作业,阅读/测试例如:

还有几个内容相似的。 大多数(全部?)异步获取输出(例如,使用Process.ReadToEnd() )。 我希望输出同步,根据MSDN文档涉及建立事件处理程序等我已经尝试过。 它可以工作,但在进程退出之前不会调用事件处理程序。 我从iisreset.exe获取输出,但直到它完成。

为了排除这与iisreset.exe特别有关的可能性,我写了一个小的控制台应用程序,它生成一些输出,暂停之间:

 namespace OutputGenerator { class Program { static void Main(string[] args) { System.Console.WriteLine("OutputGenerator starting and pausing for 10 seconds.."); System.Threading.Thread.Sleep(10000); System.Console.WriteLine("Pausing for another 10 seconds.."); System.Threading.Thread.Sleep(10000); System.Console.WriteLine("Exiting!"); } } } 

使用它测试结果表明我在需要时可以直接获取捕获的数据。 因此,在某种程度上,似乎iisreset.exe输出数据的方式在这里发挥作用。

以下是执行捕获的程序代码(Windows窗体应用程序):

 using System; using System.Windows.Forms; using System.Diagnostics; namespace OutputCapturer { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnRun_Click(object sender, EventArgs e) { // Running this will show all output after the process has exited //String path = @"C:Windowssystem32iisreset.exe"; // Running this will show all output "when it happens" String path = @"C:OutputGenerator.exe"; var p = new Process(); p.StartInfo.FileName = path; p.StartInfo.UseShellExecute = false; // ShellExecute = true not allowed when output is redirected.. p.StartInfo.RedirectStandardOutput = true; p.StartInfo.CreateNoWindow = true; p.OutputDataReceived += OutputDataReceived; p.Start(); p.BeginOutputReadLine(); } private delegate void OutputDataToTextboxDelegate(String s); void OutputDataToTextbox(String s) { tbxOutput.Text += s + Environment.NewLine; tbxOutput.Refresh(); } private void OutputDataReceived(object sender, DataReceivedEventArgs e) { if (e.Data != null && e.Data.ToString() != "") { // Must run the update of the textbox in the same thread that created it.. tbxOutput.Invoke( new OutputDataToTextboxDelegate(OutputDataToTextbox), DateTime.Now.ToString() + ": " + e.Data.ToString() ); } } } } 

认为这是一个EOL编码问题( iisreset.exe的输出apearing作为我的应用程序的一行)),我运行了一个调试会话。 不。 StandardOutput的事件处理程序被多次调用(来自iisreset.exe每个输出行一次),但是这些调用在进程退出后会在一次突发中出现。

我很乐意,如果我能从iisreset.exe获取输出“当它发生时”,这样我就可以将它显示为进度指示。

我已经看到另一个具有相同/类似问题的线程, 来自进程输出的异步捕获不能正常工作 ,但没有解决方案。

我有点难过。

自动执行printfs / stdouts

C相当于autoflush(每次写入后刷新stdout)?

这救了我的屁股……

似乎六个变量是正确的,并且这与iisreset.exe ,并不是为每一行刷新它的缓冲区。 (我仍然想知道是什么让它在普通的命令行上运行 – 即cmd.exe做什么?)

无论如何..我尝试了apacay建议的,写了这个:

 private void btnRun_Click(object sender, EventArgs e) { // Running this will show the output after the process has finished //String path = @"C:Windowssystem32iisreset.exe"; // Running this will show all output "when it happens" String path = @"C:OutputGenerator.exe"; var p = new Process(); p.StartInfo.FileName = path; p.StartInfo.UseShellExecute = false; // ShellExecute = true not allowed when output is redirected.. p.StartInfo.RedirectStandardOutput = true; p.StartInfo.CreateNoWindow = true; p.Start(); StreamReader sr = p.StandardOutput; while (!sr.EndOfStream) { String s = sr.ReadLine(); if (s != "") { tbxOutput.Text += DateTime.Now.ToString() + ": " + s + Environment.NewLine; } tbxOutput.Refresh(); } } 

请注意,当我得到每一行时,我正在加时间戳。 对于我的OutputGenerator,我得到了这个:

 2011-07-06 17:49:11: OutputGenerator starting and pausing for 10 seconds.. 2011-07-06 17:49:21: Pausing for another 10 seconds.. 2011-07-06 17:49:31: Exiting! 

对于iisreset.exe,我得到了这个:

  2011-07-06 17:57:11: Attempting stop... 2011-07-06 17:57:11: Internet services successfully stopped 2011-07-06 17:57:11: Attempting start... 2011-07-06 17:57:11: Internet services successfully restarted 

在命令行上运行iisreset.exe,这些行之间会暂停,持续时间可能超过10秒。

这个案子现在或多或少已经关闭了。 并不是说我满意,但我似乎在道路尽头。 我不情愿地忍受它..

总结一下:在一般情况下,很有可能在生成时同步捕获输出。 该线程提供了两种方法的代码 – 通过建立事件处理程序,以及“轮询”流。 在我的具体情况下,有一些东西与iisreset.exe生成输出,以防止这种情况。

感谢参与和贡献的人!

嗯….你可以踢它老派。 可以使用旧式DOS命令( foo.exe | bar.exe )将输出重定向到另一个程序的输入。 编写一个从标准读取的程序,每次流刷新时都会得到它。

编辑

您还可以将输出重定向到命名管道并从中读取。 这也将是“正好发生”。

好吧,我尝试过一个我知道有用的帮助类: http : //csharptest.net/browse/src/Library/Processes/ProcessRunner.cs

 ProcessRunner runner = new ProcessRunner("iisreset.exe"); runner.OutputReceived += OutputDataReceived; runner.Start("/RESTART", "/STATUS"); 

但是,这仍然无法解决此特定可执行文件的问题。 似乎iisreset是以这样的方式编写的,这是不可能的。 甚至从命令行运行以下命令:

 iisreset.exe /RESTART /STATUS > temp.txt 

在重新启动所有服务之后,仍然没有任何内容写入文本文件’temp.txt’。

至于你的示例代码,我建议你阅读我前一段时间写的post: 如何正确使用System.Diagnostics.Process 。 具体来说,您不是在读取std :: err流或重定向和关闭std :: in流。 这可能会导致程序中出现非常不理想的结果。 您可以查看上面链接的示例包装类,了解如何使用输出事件执行此操作,或者如果您想直接读取需要使用两个自己的线程的流。

  static void Main() { ProcessStartInfo psi = new ProcessStartInfo(@"C:Windowssystem32iisreset.exe", "/RESTART /STATUS"); psi.CreateNoWindow = true; psi.UseShellExecute = false; psi.RedirectStandardError = true; psi.RedirectStandardOutput = true; psi.RedirectStandardInput = true; ManualResetEvent output_complete = new ManualResetEvent(false); ManualResetEvent error_complete = new ManualResetEvent(false); Process p = Process.Start(psi); new ReadOutput(p.StandardOutput, output_complete); new ReadOutput(p.StandardError, error_complete); p.StandardInput.Close(); p.WaitForExit(); output_complete.WaitOne(); error_complete.WaitOne(); } private class ReadOutput { private StreamReader _reader; private ManualResetEvent _complete; public ReadOutput(StreamReader reader, ManualResetEvent complete) { _reader = reader; _complete = complete; Thread t = new Thread(new ThreadStart(ReadAll)); t.Start(); } void ReadAll() { int ch; while(-1 != (ch = _reader.Read())) { Console.Write((char) ch); } _complete.Set(); } } 

我写这篇文章只是为了看看是否有任何事情发生。 直到最后仍然没有任何结果,所以我认为你从iisreset获得异步输出只是SOL。

我遇到了这个问题,当我的日志在一个readtoend中读取太长时间时,我必须解决它。

这就是我为解决它而做的。 到目前为止,它一直在做Ok。

  myProcess.StartInfo.FileName = path; myProcess.StartInfo.Arguments = args; myProcess.StartInfo.UseShellExecute = false; myProcess.StartInfo.ErrorDialog = false; myProcess.StartInfo.CreateNoWindow = true; myProcess.StartInfo.RedirectStandardError = true; myProcess.StartInfo.RedirectStandardInput = (stdIn != null); myProcess.StartInfo.RedirectStandardOutput = true; myProcess.Start(); int index; OpenLogFile(myLog); //LOGGGGGGGGGGGGG if (myProcess.StartInfo.RedirectStandardInput) { StreamWriter sw = myProcess.StandardInput; sw.Write(stdIn + Convert.ToChar(26)); } StreamReader sr = myProcess.StandardOutput; /*stdOut = new ArrayLi */ while (!sr.EndOfStream) { //LOGGGGGGGGGGGGG Log(sr.ReadLine(), true); } 

这是OpenLogFile

  private void OpenLogFile(string fileName) { if (file == StreamWriter.Null) { file = new StreamWriter(fileName, true); file.AutoFlush = true; } } 

当然,Log是一个在别处做某事的函数。 但问题的解决方案在于:

  while (!sr.EndOfStream) { //LOGGGGGGGGGGGGG Log(sr.ReadLine(), true); } 

当流阅读器仍在阅读时,您可以在日志出来时将其写下来。

对于我的具体情况,解决方案是摩西先生在上面的评论中建议的,即运行iisreset /stop然后是iisreset /start

我需要一个正确的答案,而不是评论,以便将其标记为我的“接受的答案”,所以这个答案更多的是管理而不是新的贡献。 信誉应该交给摩西先生.. ?

上述就是C#学习教程:同步捕获进程输出(即“发生时”)分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/cdevelopment/1011640.html

(0)
上一篇 2021年12月30日
下一篇 2021年12月30日

精彩推荐