2011-12-21 98 views
2

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

我尝试使用:

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

但冻结UI线程在等待和防止出现输出。这:

p.Start(); 
p.BeginOutputReadLine(); 

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

工作更好,但是完全错误/一个坏主意,并没有实际等待整个期间。

我也简单地尝试过使用BackgroundWorker来启动shell进程,但我不确定如何让UI线程等待而不会阻止工作者完成。

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

所有外壳命令与运行:

ProcessStartInfo info = new ProcessStartInfo 
{ 
    UseShellExecute = false, 
    CreateNoWindow = true, 
    RedirectStandardOutput = true, 
    RedirectStandardError = true, 

    FileName = "cmd.exe", 
    Arguments = "/c " + command 
}; 

重定向输出和有效外壳执行该命令。

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

回答

1

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

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

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

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; 
    } 
2

从的ProcessStartInfo创建过程中,

澄清:SI的ProcessStartInfo需要像

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 
} 
+0

显然,如果你想捕捉标准错误的数据,你必须重复练习与p.ErrorDataReceievd + =的ErrorHandler – 2011-12-21 17:31:53

+0

这并不妨碍该函数创建并返回从启动过程。我的输出成功重定向,我更专注于等待无阻塞。 – ssube 2011-12-21 17:50:24