C#等待shell命令在读取异步时返回

我需要从C#应用程序运行外部进程,但在继续执行之前等待它返回。 同时,我需要从stdout获取并更新包含进度信息的文本框。 由于命令可能会运行几分钟并在此期间打印信息,因此显示绝对必要; 但是可能会运行多个命令并且必须按顺序排列,因此等待也是如此。

我试图使用:

p = Process.Start(); p.BeginOutputReadLine(); p.WaitForExit(); 

但是在等待时冻结了UI线程并阻止了输出的出现。 这个:

 p.Start(); p.BeginOutputReadLine(); while (!p.HasExited) { Application.DoEvents(); Thread.Sleep(100); } 

效果更好,但是完全错误/坏主意并且实际上并没有等待整个期间。

我还简要地尝试使用BackgroundWorker来启动shell进程,但是我不确定如何让UI线程在没有阻塞的情况下等待工作者完成。

我想要做的是提供类似于ShowDialog()DialogResult ShellExecute(String cmd) ,如果用户允许命令完成,单击取消或命令的返回代码,则返回OK / Cancel(/ fail)结果。 在命令完成(或取消)之前不应返回。

所有shell命令都运行于:

 ProcessStartInfo info = new ProcessStartInfo { UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, FileName = "cmd.exe", Arguments = "/c " + command }; 

重定向输出并有效地执行命令。

我将如何正确地创建一个启动异步进程的函数,但仍然等待直到完成返回?

从ProcessStartInfo创建您的流程,

澄清:ProcessStartinfo si需要类似的东西

 si.CreateNoWindow=true; si.RedirectStandardOutput=true; si.RedirectStandardError=true; si.StandardOutputEncoding=Encoding.UTF8; si.StandardErrorEncoding=Encoding.UTF8; si.WindowStyle=ProcessWindowStyle.Hidden; 

然后呢

 p.Start(); 

使用

 p.OutoutDataReceived+=OutputHandler; 

 private static void OutputHandler(object theProcess, DataReceivedEventArgs evtdata) { //evtdata.Data has the output data //use it, display it, or discard it } 

重写了大部分代码以使其正常工作。

ProgressForm类提供QueueCommand方法,获取所需的shell命令和前/后委托。 显示时,进度表单使用后台工作程序执行每个命令,处理返回代码并在适当时执行下一个命令。

后台工作程序等待每个shell命令完成( Process.WaitForExit() ),同时异步提供UI线程输出。 完成后,它会调用一个启用成功/确定按钮并隐藏进度条的方法。

 void m_Worker_DoWork(object sender, DoWorkEventArgs e) { int exec = 1; while (CommandQueue.Count > 0) { if (e.Cancel) { e.Result = 1; return; } WriteLine("Running command {0}, {1} remaining.", exec++, CommandQueue.Count); StagedCommand command = CommandQueue.Peek(); try { if (command.Pre != null) command.Pre(); int result = ShellExec(command.Command); if (command.Post != null) command.Post(); CommandQueue.Dequeue(); if (result != 0) { e.Result = result; return; } } catch (Exception exc) { WriteLine("Error: {0}", exc.Message); e.Result = 1; return; } } WriteLine("All commands executed successfully."); e.Result = 0; return; } int ShellExec(String command) { WriteLine(command); Style = ProgressBarStyle.Marquee; ProcessStartInfo info = new ProcessStartInfo { UseShellExecute = false, LoadUserProfile = true, ErrorDialog = false, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, RedirectStandardOutput = true, StandardOutputEncoding = Encoding.UTF8, RedirectStandardError = true, StandardErrorEncoding = Encoding.UTF8, FileName = "cmd.exe", Arguments = "/c " + command }; Process shell = new Process(); shell.StartInfo = info; shell.EnableRaisingEvents = true; shell.ErrorDataReceived += new DataReceivedEventHandler(ShellErrorDataReceived); shell.OutputDataReceived += new DataReceivedEventHandler(ShellOutputDataReceived); shell.Start(); shell.BeginErrorReadLine(); shell.BeginOutputReadLine(); shell.WaitForExit(); return shell.ExitCode; }