2011-04-19 75 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(); 
}