2016-12-05 3 views
3

私は、出力にstdout出力とstderr出力の両方を含む別のプログラムを呼び出すプログラムを書こうとします。例えばProcessクラスからのイベントを間違った順序で受け取らないようにするにはどうすればよいですか?

、私は呼んで、このプログラムは、エラーを含むF#ファイルをコンパイルしようとしているF#コンパイラのようになります。

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 

(最初の2行はstderrstdout、残りの部分に印刷されている。)

だから私は、プロセスのクラスでこの方法を扱うプログラムを書く:

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クラスを扱うには?

+1

問題はおそらくバッファリングの違いにあります。同じようにバッファにstdoutとstderrを見てください –

+0

彼らはすでに(私が理解する限り)同じ方法でバッファリングしていますので、あなたは何を提案しているのですか? – knocte

+1

通常、出力がバッファされている間、エラーはバッファされません。これをどこかで変更するオプションがあります。 –

答えて

2

出力バッファリングの可能性があります。 stdoutstderrにそれぞれ書き込んだ後、Console.Out.Flush()Console.Error.Flush()を呼び出してみましたか?

+0

私はそれを試して、報告して戻ってきます、ありがとう – knocte

関連する問題