Lire le processus StandardOutput avant la nouvelle ligne reçue

J’essaie de faire quelque chose qui semble être hors de scope pour l’object System.Diagnostics.Process. Des réponses acceptables peuvent proposer une approche différente dans la mesure où il utilise .net 4.5 / c # 5.

Mon programme appelle gdalwarp.exe pour exécuter de longs processus sur des fichiers Tiff volumineux. Galwarp.exe affiche dans ce format.

Creating output file that is 6014P x 4988L. Processing input file [FileName].tiff. Using band 4 of source image as alpha. Using band 4 of destination image as alpha. 0...10...20...30...40...50...60...70...80...90...100 - done. 

La dernière ligne arrive lentement pour indiquer la progression. J’aimerais lire cette ligne chaque fois qu’elle change afin de pouvoir déplacer une barre de progression en tenant l’utilisateur informé.

J’ai d’abord essayé de lire Process.StandardOutput mais il ne fournit aucune donnée tant que le processus n’est pas terminé. Deuxièmement, j’ai essayé d’appeler Process.BeginOutputReadLine() et de connecter l’événement Process.OutputDataReceived mais il ne se déclenche que lorsqu’une ligne est terminée.

Voici l’appel à Execute GDalWarp.exe.

  public static void ResizeTiff(ssortingng SourceFile, ssortingng DestinationFile, float ResolutionWidth, float ResolutionHeight, Guid ProcessId) { var directory = GDalBin; var exe = Path.Combine(directory, "gdalwarp.exe"); var args = " -ts " + ResolutionWidth + " " + ResolutionHeight + " -r cubic -co \"TFW=YES\" \"" + SourceFile + "\" \"" + DestinationFile + "\""; ExecuteProcess(exe, args, null, directory, 0, null, true, true, 0); } 

Voici mon code de travail dans une fonction statique qui lit uniquement la sortie après la fin du processus.

 public static ssortingng ExecuteProcess(ssortingng FilePath, ssortingng Args, ssortingng Input, ssortingng WorkingDir, int WaitTime = 0, Dictionary EnviroVariables = null, bool Trace = false, bool ThrowError = true, int ValidExitCode = 0) { var processInfo = "FilePath: " + FilePath + "\n" + (WaitTime > 0 ? "WaitTime: " + WaitTime.ToSsortingng() + " ms\n" : "") + (!ssortingng.IsNullOrEmpty(Args) ? "Args: " + Args + "\n" : "") + (!ssortingng.IsNullOrEmpty(Input) ? "Input: " + Input + "\n" : "") + (!ssortingng.IsNullOrEmpty(WorkingDir) ? "WorkingDir: " + WorkingDir + "\n" : "") + (EnviroVariables != null && EnviroVariables.Count > 0 ? "Environment Variables: " + ssortingng.Join(", ", EnviroVariables.Select(a => a.Key + "=" + a.Value)) + "\n" : ""); if(Trace) Log.Debug("Running external process with the following parameters:\n" + processInfo); var startInfo = (ssortingng.IsNullOrEmpty(Args)) ? new ProcessStartInfo(FilePath) : new ProcessStartInfo(FilePath, Args); if (!ssortingng.IsNullOrEmpty(WorkingDir)) startInfo.WorkingDirectory = WorkingDir; startInfo.UseShellExecute = false; startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardError = true; startInfo.CreateNoWindow = true; if (!ssortingng.IsNullOrEmpty(Input)) startInfo.RedirectStandardInput = true; if (EnviroVariables != null) foreach (KeyValuePair entry in EnviroVariables) startInfo.EnvironmentVariables.Add(entry.Key, entry.Value); var process = new Process(); process.StartInfo = startInfo; if (process.Start()) { if (Input != null && Input != "") { process.StandardInput.Write(Input); process.StandardInput.Close(); } var standardError = ""; var standardOutput = ""; int exitCode = 0; var errorReadThread = new Thread(new ThreadStart(() => { standardError = process.StandardError.ReadToEnd(); })); var outputReadTread = new Thread(new ThreadStart(() => { standardOutput = process.StandardOutput.ReadToEnd(); })); errorReadThread.Start(); outputReadTread.Start(); var sw = Stopwatch.StartNew(); bool timedOut = false; try { while (errorReadThread.IsAlive || outputReadTread.IsAlive) { Thread.Sleep(50); if (WaitTime > 0 && sw.ElapsedMilliseconds > WaitTime) { if (errorReadThread.IsAlive) errorReadThread.Abort(); if (outputReadTread.IsAlive) outputReadTread.Abort(); timedOut = true; break; } } if (!process.HasExited) process.Kill(); if (timedOut) throw new TimeoutException("Timeout occurred during execution of an external process.\n" + processInfo + "Standard Output: " + standardOutput + "\nStandard Error: " + standardError); exitCode = process.ExitCode; } finally { sw.Stop(); process.Close(); process.Dispose(); } if (ThrowError && exitCode != ValidExitCode) throw new Exception("An error was returned from the execution of an external process.\n" + processInfo + "Exit Code: " + exitCode + "\nStandard Output: " + standardOutput + "\nStandard Error: " + standardError); if (Trace) Log.Debug("Process Exited with the following values:\nExit Code: {0}\nStandard Output: {1}\nStandard Error: {2}", exitCode, standardOutput, standardError); return standardOutput; } else return null; } 

Quelqu’un peut-il m’aider à lire cette sortie en temps réel?

Voici une solution à votre problème, mais un peu délicate, car gdalwarp.exe bloque la sortie standard, vous pouvez redirect sa sortie vers un fichier et lire les modifications qui y sont apscopes. Il était possible d’utiliser FileSystemWatcher pour détecter les modifications de fichier, mais ce n’est parfois pas assez fiable. Une méthode d’interrogation simple des changements de taille de fichier est utilisée ci-dessous dans outputReadThread si OutputCallback n’est pas null.

Voici un appel à ExecuteProcess avec rappel pour recevoir immédiatement la sortie du processus.

  public static void ResizeTiff(ssortingng SourceFile, ssortingng DestinationFile, float ResolutionWidth, float ResolutionHeight, Guid ProcessId) { var directory = GDalBin; var exe = Path.Combine(directory, "gdalwarp.exe"); var args = " -ts " + ResolutionWidth + " " + ResolutionHeight + " -r cubic -co \"TFW=YES\" \"" + SourceFile + "\" \"" + DestinationFile + "\""; float progress = 0; Action callback = delegate(ssortingng fullOutput, ssortingng newOutput) { float value; if (float.TryParse(newOutput, out value)) progress = value; else if (newOutput == ".") progress += 2.5f; else if (newOutput.StartsWith("100")) progress = 100; }; ExecuteProcess(exe, args, null, directory, 0, null, true, true, 0, callback); } 

Voici une fonction permettant d’appeler n’importe quel processus et de recevoir les résultats au fur et à mesure.

  public static ssortingng ExecuteProcess(ssortingng FilePath, ssortingng Args, ssortingng Input, ssortingng WorkingDir, int WaitTime = 0, Dictionary EnviroVariables = null, bool Trace = false, bool ThrowError = true, int ValidExitCode = 0, Action OutputChangedCallback = null) { var processInfo = "FilePath: " + FilePath + "\n" + (WaitTime > 0 ? "WaitTime: " + WaitTime.ToSsortingng() + " ms\n" : "") + (!ssortingng.IsNullOrEmpty(Args) ? "Args: " + Args + "\n" : "") + (!ssortingng.IsNullOrEmpty(Input) ? "Input: " + Input + "\n" : "") + (!ssortingng.IsNullOrEmpty(WorkingDir) ? "WorkingDir: " + WorkingDir + "\n" : "") + (EnviroVariables != null && EnviroVariables.Count > 0 ? "Environment Variables: " + ssortingng.Join(", ", EnviroVariables.Select(a => a.Key + "=" + a.Value)) + "\n" : ""); ssortingng outputFile = ""; if (OutputChangedCallback != null) { outputFile = Path.GetTempFileName(); Args = "/C \"\"" + FilePath + "\" " + Args + "\" >" + outputFile; FilePath = "cmd.exe"; } var startInfo = (ssortingng.IsNullOrEmpty(Args)) ? new ProcessStartInfo(FilePath) : new ProcessStartInfo(FilePath, Args); if (!ssortingng.IsNullOrEmpty(WorkingDir)) startInfo.WorkingDirectory = WorkingDir; startInfo.UseShellExecute = false; startInfo.CreateNoWindow = true; if (OutputChangedCallback == null) { startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardError = true; } else { startInfo.RedirectStandardOutput = false; startInfo.RedirectStandardError = false; } if (!ssortingng.IsNullOrEmpty(Input)) startInfo.RedirectStandardInput = true; if (EnviroVariables != null) foreach (KeyValuePair entry in EnviroVariables) startInfo.EnvironmentVariables.Add(entry.Key, entry.Value); var process = new Process(); process.StartInfo = startInfo; if (process.Start()) { if (Trace) Log.Debug("Running external process with the following parameters:\n" + processInfo); try { if (!ssortingng.IsNullOrEmpty(Input)) { process.StandardInput.Write(Input); process.StandardInput.Close(); } var standardError = ""; var standardOutput = ""; int exitCode = 0; Thread errorReadThread; Thread outputReadThread; if (OutputChangedCallback == null) { errorReadThread = new Thread(new ThreadStart(() => { standardError = process.StandardError.ReadToEnd(); })); outputReadThread = new Thread(new ThreadStart(() => { standardOutput = process.StandardOutput.ReadToEnd(); })); } else { errorReadThread = new Thread(new ThreadStart(() => { })); outputReadThread = new Thread(new ThreadStart(() => { long len = 0; while (!process.HasExited) { if (File.Exists(outputFile)) { var info = new FileInfo(outputFile); if (info.Length != len) { var content = new StreamReader(File.Open(outputFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)).ReadToEnd(); var newContent = content.Subssortingng((int)len, (int)(info.Length - len)); len = info.Length; OutputChangedCallback.Invoke(content, newContent); } } Thread.Sleep(10); } })); } errorReadThread.Start(); outputReadThread.Start(); var sw = Stopwatch.StartNew(); bool timedOut = false; try { while (errorReadThread.IsAlive || outputReadThread.IsAlive) { Thread.Sleep(50); if (WaitTime > 0 && sw.ElapsedMilliseconds > WaitTime) { if (errorReadThread.IsAlive) errorReadThread.Abort(); if (outputReadThread.IsAlive) outputReadThread.Abort(); timedOut = true; break; } } if (!process.HasExited) process.Kill(); if (timedOut) throw new TimeoutException("Timeout occurred during execution of an external process.\n" + processInfo + "Standard Output: " + standardOutput + "\nStandard Error: " + standardError); exitCode = process.ExitCode; } finally { sw.Stop(); process.Close(); process.Dispose(); } if (ThrowError && exitCode != ValidExitCode) throw new Exception("An error was returned from the execution of an external process.\n" + processInfo + "Exit Code: " + exitCode + "\nStandard Output: " + standardOutput + "\nStandard Error: " + standardError); if (Trace) Log.Debug("Process Exited with the following values:\nExit Code: {0}\nStandard Output: {1}\nStandard Error: {2}", exitCode, standardOutput, standardError); return standardOutput; } finally { FileUtilities.AttemptToDeleteFiles(new ssortingng[] { outputFile }); } } else throw new Exception("The process failed to start.\n" + processInfo); }