假設我想編寫一個調用另一個程序的程序,該程序的輸出包含stdout
輸出和stderr
輸出。如何避免以錯誤的順序從Process類接收事件?
例如,該程序我打電話將F#編譯器試圖編譯含有誤差的F#的文件:(前兩行被打印到stdout
,其餘stderr
)
F# Compiler for F# 4.0 (Open Source Edition)
Freely distributed under the Apache 2.0 Open Source License
/builds/someLib.fs(27,42): error FS0001: Type mismatch. Expecting a
string * string
but given a
string * string * 'a
The tuples have differing lengths of 2 and 3
所以我寫一個程序與過程類處理是這樣的:
type OutChunk = StdOut of string | StdErr of string
type OutputBuffer = list<OutChunk>
type ProcessResult = { ExitCode: int; Output: OutputBuffer }
module Process =
let Execute (command: string, args: string, hidden: bool)
: ProcessResult =
// I know, this shit below is mutable, but it's a consequence of dealing with .NET's Process class' events
let outputBuffer = new System.Collections.Generic.List<OutChunk>()
let outputBufferLock = new Object()
use outWaitHandle = new AutoResetEvent(false)
use errWaitHandle = new AutoResetEvent(false)
let startInfo = new ProcessStartInfo(command, args)
startInfo.UseShellExecute <- false
startInfo.RedirectStandardOutput <- true
startInfo.RedirectStandardError <- true
use proc = new System.Diagnostics.Process()
proc.StartInfo <- startInfo
let outReceived (e: DataReceivedEventArgs): unit =
if (e.Data = null) then
outWaitHandle.Set() |> ignore
else
if not (hidden) then
Console.WriteLine(e.Data)
lock outputBufferLock (fun _ -> outputBuffer.Add(OutChunk.StdOut(e.Data)))
let errReceived (e: DataReceivedEventArgs): unit =
if (e.Data = null) then
errWaitHandle.Set() |> ignore
else
if not (hidden) then
Console.Error.WriteLine(e.Data)
lock outputBufferLock (fun _ -> outputBuffer.Add(OutChunk.StdErr(e.Data)))
proc.OutputDataReceived.Add outReceived
proc.ErrorDataReceived.Add errReceived
proc.Start() |> ignore
let exitCode =
try
proc.BeginOutputReadLine()
proc.BeginErrorReadLine()
proc.WaitForExit()
proc.ExitCode
finally
outWaitHandle.WaitOne() |> ignore
errWaitHandle.WaitOne() |> ignore
{ ExitCode = exitCode; Output = List.ofSeq(outputBuffer) }
let rec PrintToScreen (outputBuffer: OutputBuffer) =
match outputBuffer with
| [] ->()
| head::tail ->
match head with
| StdOut(out) -> Console.WriteLine(out)
| StdErr(err) -> Console.Error.WriteLine(err)
PrintToScreen(tail)
然而,即使我使用的鎖在c上述頌歌防止競爭條件寫入可變列表時,有時當我運行調用F#編譯器的F#程序,然後我打電話PrintToScreen
功能,我得到的流混合:
F# Compiler for F# 4.0 (Open Source Edition)
/builds/someLib.fs(27,42): error FS0001: Type mismatch. Expecting a
Freely distributed under the Apache 2.0 Open Source License
string * string
but given a
string * string * 'a
The tuples have differing lengths of 2 and 3
(正如你可以看到,許可證文本應該在編譯器錯誤之前到達,但它沒有)
這怎麼可能?如何處理惡魔System.Diagnostics.Process
類以正確的順序接收流/事件?
問題可能是緩衝差異。嘗試看stdout和stderr以相同的方式緩衝 –
他們已經以同樣的方式緩衝(據我所知),所以不知道你建議什麼? – knocte
正常情況下,輸出緩衝時無錯誤。有一個選項可以在某處進行更改。 –