2011-04-19 40 views
4

如果您想启动另一个进程并等待(超时)完成,则可以使用the following (from MSDN)c#ProcessStartInfo.Start - 读取输出,但超时

//Set a time-out value. 
int timeOut=5000; 
//Get path to system folder. 
string sysFolder= 
    Environment.GetFolderPath(Environment.SpecialFolder.System); 
//Create a new process info structure. 
ProcessStartInfo pInfo = new ProcessStartInfo(); 
//Set file name to open. 
pInfo.FileName = sysFolder + @"\eula.txt"; 
//Start the process. 
Process p = Process.Start(pInfo); 
//Wait for window to finish loading. 
p.WaitForInputIdle(); 
//Wait for the process to exit or time out. 
p.WaitForExit(timeOut); 
//Check to see if the process is still running. 
if (p.HasExited == false) 
    //Process is still running. 
    //Test to see if the process is hung up. 
    if (p.Responding) 
     //Process was responding; close the main window. 
     p.CloseMainWindow(); 
    else 
     //Process was not responding; force the process to close. 
     p.Kill(); 

MessageBox.Show("Code continuing..."); 

如果要启动另一个进程并读取其输出,那么你可以使用following pattern (from SO)

// Start the child process. 
Process p = new Process(); 
// Redirect the output stream of the child process. 
p.StartInfo.UseShellExecute = false; 
p.StartInfo.RedirectStandardOutput = true; 
p.StartInfo.FileName = "Write500Lines.exe"; 
p.Start(); 
// Do not wait for the child process to exit before 
// reading to the end of its redirected stream. 
// p.WaitForExit(); 
// Read the output stream first and then wait. 
string output = p.StandardOutput.ReadToEnd(); 
p.WaitForExit(); 

如何将二者结合起来阅读所有输入,而不是陷在僵局和有如果运行进程出错,超时?

+0

你可能有兴趣在[MedallionShell](https://github.com/madelson/MedallionShell )库,它可以轻松处理进程io流并为进程分配超时 – ChaseMedallion 2014-08-30 00:07:55

回答

17

如果输出缓冲区填充了更多的4KB数据,此技术将挂起。一种更加简单的方法是在将某些内容写入输出流时注册代表进行通知。 I've already suggested this method before in another post

ProcessStartInfo processInfo = new ProcessStartInfo("Write500Lines.exe"); 
processInfo.ErrorDialog = false; 
processInfo.UseShellExecute = false; 
processInfo.RedirectStandardOutput = true; 
processInfo.RedirectStandardError = true; 

Process proc = Process.Start(processInfo); 

// You can pass any delegate that matches the appropriate 
// signature to ErrorDataReceived and OutputDataReceived 
proc.ErrorDataReceived += (sender, errorLine) => { if (errorLine.Data != null) Trace.WriteLine(errorLine.Data); }; 
proc.OutputDataReceived += (sender, outputLine) => { if (outputLine.Data != null) Trace.WriteLine(outputLine.Data); }; 
proc.BeginErrorReadLine(); 
proc.BeginOutputReadLine(); 

proc.WaitForExit(); 
4

您不必将两者结合 - Process类有一个事件在输出发送到StandardOutput-OutputDataReceived时触发。

如果您订阅该事件,您将能够在输出到达时读取输出,并且在主程序循环中仍然可以超时。

+4

如果这样做,不要忘记在Process类上设置EnableRaisingEvents属性。每次都给我。 – JohnC 2011-04-19 15:10:45

+1

根据MSDN,doco EnableRaisingEvents只适用于Exited事件而不是OutputDataReceived。 http://msdn.microsoft.com/en-us/library/system.diagnostics.process.enableraisingevents.aspx – Ryan 2011-04-19 16:31:49

+0

我想知道为什么我的OutputDataReceived事件永远不会发射...... Upvoted JohnC似乎太快了。 – 2014-08-23 03:12:52

0

只需将WaitForExit()调用下面的第一个示例添加到第二个示例。

+0

什么是第一个 - ReadToEnd或WaitForExit?谨慎阐述? – Ryan 2011-04-19 15:21:20

+0

说要注册事件的答案比我的好。我会这样做 – 2011-04-19 15:39:00

0

您也可以使用APM,就像这样:

定义为ReadToEnd的电话委托:

private delegate string ReadToEndDelegate(); 

然后使用委托来调用这样的方法:

ReadToEndDelegate asyncCall = reader.ReadToEnd; 
IAsyncResult asyncResult = asyncCall.BeginInvoke(null, null); 
asyncResult.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(10)); 
asyncCall.EndInvoke(asyncResult); 

编辑:为清晰起见删除错误处理。

2

,你可以尝试修改第一种方法是这样的

Process p = Process.Start(pInfo); 
string output = string.Empty; 
Thread t = new Thread(() => output = p.StandardOutput.ReadToEnd()); 
t.Start(); 
//Wait for window to finish loading. 
p.WaitForInputIdle(); 
//Wait for the process to exit or time out. 
p.WaitForExit(timeOut); 
1
void OpenWithStartInfo() 
{ 
    ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe", "Default2.aspx"); 
    startInfo.WindowStyle = ProcessWindowStyle.Minimized; 
    Process p = Process.Start(startInfo); 
    p.WaitForInputIdle(); 
    //p.WaitForExit(2); 
    p.Kill(); 
}