// STDOUT/STDERR private bool CheckOCRProcessSuccess(string ocr_parameters) { // Fire up the process using (Process process = ProcessSpawning.SpawnChildProcess("QiqqaOCR.exe", ocr_parameters, ProcessPriorityClass.BelowNormal)) { Stopwatch clk = Stopwatch.StartNew(); long duration = 0; using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { // Wait a few minutes for the OCR process to exit while (true) { duration = clk.ElapsedMilliseconds; if (!Utilities.Shutdownable.ShutdownableManager.Instance.IsShuttingDown && !StillRunning) { break; } if (process.WaitForExit(1000)) { break; } if (duration >= Constants.MAX_WAIT_TIME_MS_FOR_QIQQA_OCR_TASK_TO_TERMINATE + Constants.EXTRA_TIME_MS_FOR_WAITING_ON_QIQQA_OCR_TASK_TERMINATION) { break; } } // Check that we had a clean exit if (!process.HasExited || 0 != process.ExitCode) { bool has_exited = process.HasExited; if (!has_exited) { try { process.Kill(); // wait for the completion signal; this also helps to collect all STDERR output of the application (even while it was KILLED) process.WaitForExit(1000); } catch (Exception ex) { Logging.Error(ex, "There was a problem killing the OCR process after timeout ({0} ms)", duration); } } Logging.Error("There was a problem while running OCR with parameters: {0}\n--- Exit Code: {1}\n--- {3}\n{2}", ocr_parameters, process.ExitCode, process_output_reader.GetOutputsDumpString(), (has_exited ? $"Exit code: {process.ExitCode}" : $"Timeout: {duration} ms")); return(false); } else { return(true); } } } }
internal static void Install(BundleLibraryManifest manifest, string library_bundle_filename) { WPFDoEvents.AssertThisCodeIs_NOT_RunningInTheUIThread(); WebLibraryDetail web_library_detail = WebLibraryManager.Instance.GetLibrary(manifest.Id); if (null != web_library_detail) { MessageBoxes.Info("You already have a version of this Bundle Library. Please ensure you close all windows that use this library after the latest has been downloaded."); } string library_directory = WebLibraryDetail.GetLibraryBasePathForId(manifest.Id); Directory.CreateDirectory(library_directory); // Unzip the bundle string parameters = String.Format("-y x \"{0}\" -o\"{1}\"", library_bundle_filename, library_directory); using (Process process = ProcessSpawning.SpawnChildProcess(ConfigurationManager.Instance.Program7ZIP, parameters)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { process.WaitForExit(); Logging.Info("7ZIP Log Bundle Install progress:\n{0}", process_output_reader.GetOutputsDumpStrings()); } } // Reflect this new bundle WebLibraryDetail new_web_library_detail = WebLibraryManager.Instance.UpdateKnownWebLibraryFromBundleLibraryManifest(manifest, suppress_flush_to_disk: false); WPFDoEvents.InvokeInUIThread(() => { MainWindowServiceDispatcher.Instance.OpenLibrary(new_web_library_detail); }); }
private static MemoryStream ReadEntireStandardOutput(string process_parameters, ProcessPriorityClass priority_class) { Process process = ProcessSpawning.SpawnChildProcess("pdfdraw.exe", process_parameters, priority_class); process.ErrorDataReceived += (sender, e) => { }; process.BeginErrorReadLine(); // Read image from stdout StreamReader sr = process.StandardOutput; FileStream fs = (FileStream)sr.BaseStream; MemoryStream ms = new MemoryStream(128 * 1024); int total_size = StreamToFile.CopyStreamToStream(fs, ms); // Check that the process has exited properly process.WaitForExit(1000); if (!process.HasExited) { Logging.Error("PDFRenderer process did not terminate, so killing it"); try { Logging.Info("Killing PDFRenderer process"); process.Kill(); Logging.Info("Killed PDFRenderer process"); } catch (Exception) { Logging.Error("These was an exception while trying to kill the PDFRenderer process"); } } return(ms); }
private static MemoryStream ReadEntireStandardOutput(string process_parameters, ProcessPriorityClass priority_class) { Stopwatch clk = Stopwatch.StartNew(); // STDOUT/STDERR Logging.Debug("PDFDRAW :: ReadEntireStandardOutput command: pdfdraw.exe {0}", process_parameters); using (Process process = ProcessSpawning.SpawnChildProcess("pdfdraw.exe", process_parameters, priority_class, stdout_is_binary: true)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process, stdout_is_binary: true)) { // Read image from stdout using (StreamReader sr = process.StandardOutput) { using (FileStream fs = (FileStream)sr.BaseStream) { long elapsed = clk.ElapsedMilliseconds; Logging.Debug("PDFDRAW :: ReadEntireStandardOutput setup time: {0} ms for parameters:\n {1}", elapsed, process_parameters); MemoryStream ms = new MemoryStream(256 * 1024); int total_size = StreamToFile.CopyStreamToStream(fs, ms); long elapsed2 = clk.ElapsedMilliseconds; Logging.Debug("PDFDRAW image output {0} bytes in {1} ms (output copy took {2} ms) for command:\n pdfdraw.exe {3}", total_size, elapsed2, elapsed2 - elapsed, process_parameters); // Check that the process has exited properly process.WaitForExit(1000); if (!process.HasExited) { Logging.Debug("PDFRenderer process did not terminate, so killing it.\n{0}", process_output_reader.GetOutputsDumpString()); try { process.Kill(); // wait for the completion signal; this also helps to collect all STDERR output of the application (even while it was KILLED) process.WaitForExit(1000); } catch (Exception ex) { Logging.Error(ex, "There was a problem killing the PDFRenderer process after timeout ({0} ms)", elapsed2 + 1000); } Logging.Error("PDFRenderer process did not terminate, so killed it. Commandline:\n {0}\n{1}", process_parameters, process_output_reader.GetOutputsDumpString()); throw new ApplicationException($"PDFRenderer process did not terminate, so killed it.\n Commandline: pdfdraw.exe {process_parameters}"); } else if (process.ExitCode != 0) { Logging.Error("PDFDRAW did fail with exit code {0} for commandline:\n {1}\n{2}", process.ExitCode, process_parameters, process_output_reader.GetOutputsDumpString()); throw new ApplicationException($"PDFRenderer::PDFDRAW did fail with exit code {process.ExitCode}.\n Commandline: pdfdraw.exe {process_parameters}"); } return(ms); } } } } }
bool CheckOCRProcessSuccess(string ocr_parameters, int SECONDS_TO_WAIT) { // Fire up the process using (Process process = ProcessSpawning.SpawnChildProcess("QiqqaOCR.exe", ocr_parameters, ProcessPriorityClass.BelowNormal)) { DateTime process_start_time = DateTime.UtcNow; using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { // Wait a few minutes for the OCR process to exit while (true) { if (!still_running) { break; } if (process.WaitForExit(500)) { break; } if (DateTime.UtcNow.Subtract(process_start_time).TotalSeconds >= SECONDS_TO_WAIT) { break; } } // Check that we had a clean exit if (!process.HasExited || 0 != process.ExitCode) { Logging.Error("There was a problem while running OCR with parameters: {0}", ocr_parameters); Logging.Info("Parameters: {0}", ocr_parameters); Logging.Info(process_output_reader.GetOutputsDumpString()); if (!process.HasExited) { try { process.Kill(); } catch (Exception ex) { Logging.Error(ex, "There was a problem killing the OCR process"); } } return(false); } else { return(true); } } } }
internal static void CheckForInstall() { bool should_install = false; if (!Directory.Exists(InstallationDirectory)) { Logging.Info("XULRunner directory {0} does not exist, so installing it.", InstallationDirectory); should_install = true; } else { IEnumerable <string> directory_contents = Directory.EnumerateFiles(InstallationDirectory, "*.*", SearchOption.AllDirectories); int directory_contents_count = directory_contents.Count(); if (46 != directory_contents_count) { string directory_contents_string = StringTools.ConcatenateStrings(directory_contents, "\n\t"); Logging.Warn("XULRunner directory {0} does not contain all necessary files (only {2} files), so reinstalling it. The contents were:\n\t{1}", InstallationDirectory, directory_contents_string, directory_contents_count); should_install = true; } } if (should_install) { Logging.Info("Installing XULRunner into {0}.", InstallationDirectory); Directory.CreateDirectory(InstallationDirectory); // STDOUT/STDERR string process_parameters = String.Format("x -y \"{0}\" -o\"{1}\"", XULPackageFilename.Value, UnpackDirectoryDirectory.Value); using (Process process = ProcessSpawning.SpawnChildProcess(ConfigurationManager.Instance.Program7ZIP, process_parameters, ProcessPriorityClass.Normal)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { process.WaitForExit(); Logging.Info("XULRunner installer:\n{0}", process_output_reader.GetOutputsDumpString()); } } Logging.Info("XULRunner installed."); } }
internal static void DoBundle() { try { // Try get this info for us which could be useful... // If things are really broken, it may fail, that's ok. string environment_details_filename = null; try { string environmentDetails = "Generated at:" + DateTime.UtcNow.ToString("yyyyMMdd HH:mm:ss") + Environment.NewLine; environmentDetails += ComputerStatistics.GetCommonStatistics(); environment_details_filename = TempFile.GenerateTempFilename("txt"); File.WriteAllText(environment_details_filename, environmentDetails); } catch (Exception ex) { Logging.Warn(ex, "Could not get environment details"); } // Get the destination location SaveFileDialog save_file_dialog = new SaveFileDialog(); save_file_dialog.AddExtension = true; save_file_dialog.CheckPathExists = true; save_file_dialog.DereferenceLinks = true; save_file_dialog.OverwritePrompt = true; save_file_dialog.ValidateNames = true; save_file_dialog.DefaultExt = "7z"; save_file_dialog.Filter = "7Z files (*.7z)|*.7z|All files (*.*)|*.*"; save_file_dialog.FileName = "QiqqaLogs.7z"; // Generate and save if (true == save_file_dialog.ShowDialog()) { string target_filename = save_file_dialog.FileName; string file_list = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), @"Quantisle/Qiqqa/Logs", @"Qiqqa*.log*")); if (environment_details_filename != null) { file_list += " \"" + environment_details_filename + "\""; } // Delete the target filename if it exists... FileTools.Delete(target_filename); // STDOUT/STDERR string process_parameters = String.Format("a -t7z -mmt=on -mx9 -ssw \"{0}\" \"{1}\"", target_filename, file_list); using (Process process = ProcessSpawning.SpawnChildProcess(ConfigurationManager.Instance.Program7ZIP, process_parameters, ProcessPriorityClass.Normal)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { process.WaitForExit(); Logging.Info("7ZIP Log Bundling progress:\n{0}", process_output_reader.GetOutputsDumpString()); } MessageBoxes.Info("The Qiqqa logs with some diagnostic info have been zipped to the location you specified. Please upload it as issue attachment in your issue filed at https://github.com/jimmejardine/qiqqa-open-source/issues if the support team has requested it. Many thanks!"); FileTools.BrowseToFileInExplorer(target_filename); } } FileTools.Delete(environment_details_filename); } catch (Exception ex) { Logging.Warn(ex, "Problem zipping logs"); MessageBoxes.Error("Unfortunately there was a problem creating the log bundle. Please zip them manually, they are found at C:\\Temp\\Qiqqa.log*. There may be more than one. Thanks!"); } }
public static Process StartGhostscriptProcess(string ghostscript_parameters, ProcessPriorityClass priority_class) { // STDOUT/STDERR return(ProcessSpawning.SpawnChildProcess(ExecutablePath, ghostscript_parameters, priority_class, stdout_is_binary: true)); }
// STDOUT/STDERR private bool CheckOCRProcessSuccess(string ocr_parameters, out OCRExecReport report) { // Fire up the process using (Process process = ProcessSpawning.SpawnChildProcess("QiqqaOCR.exe", ocr_parameters, ProcessPriorityClass.BelowNormal)) { Stopwatch clk = Stopwatch.StartNew(); long duration = 0; using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { // Wait a few minutes for the OCR process to exit while (true) { duration = clk.ElapsedMilliseconds; if (!ShutdownableManager.Instance.IsShuttingDown && !StillRunning) { break; } if (process.WaitForExit(1000)) { break; } if (duration >= Constants.MAX_WAIT_TIME_MS_FOR_QIQQA_OCR_TASK_TO_TERMINATE + Constants.EXTRA_TIME_MS_FOR_WAITING_ON_QIQQA_OCR_TASK_TERMINATION) { break; } } bool has_exited = process.HasExited; if (!has_exited) { try { process.Kill(); // wait for the completion signal; this also helps to collect all STDERR output of the application (even while it was KILLED) process.WaitForExit(1000); } catch (Exception ex) { Logging.Error(ex, "There was a problem killing the OCR process after timeout ({0} ms)", duration); } } while (!process.HasExited) { process.WaitForExit(1000); } // Give it some extra settling time to let all the IO events fire: Thread.Sleep(100); report = new OCRExecReport { OCRParameters = ocr_parameters, exitCode = process.ExitCode, OCRStdioOut = process_output_reader.GetOutputsDumpStrings(), hasExited = has_exited, durationMS = duration }; } // Check that we had a clean exit if (!report.hasExited || 0 != report.exitCode) { Logging.Error("There was a problem while running OCR with parameters: {0}\n--- Exit Code: {1}\n--- {3}\n{2}", report.OCRParameters, report.exitCode, report.OCRStdioOut, (report.hasExited ? $"Exit code: {report.exitCode}" : $"Timeout: {report.durationMS} ms")); return(false); } else { if (report.OCRStdioOut.stderr.Contains("ERROR")) { Logging.Error("Succeeded running OCR with parameters: {0}\n--- Exit Code: {1}\n--- {3}\n{2}", report.OCRParameters, report.exitCode, report.OCRStdioOut, (report.hasExited ? $"Exit code: {report.exitCode}" : $"Timeout: {report.durationMS} ms")); } else { Logging.Info("Succeeded running OCR with parameters: {0}\n--- Exit Code: {1}\n--- {3}\n{2}", report.OCRParameters, report.exitCode, report.OCRStdioOut, (report.hasExited ? $"Exit code: {report.exitCode}" : $"Timeout: {report.durationMS} ms")); } return(true); } } }
public static Process StartGhostscriptProcess(string ghostscript_parameters, ProcessPriorityClass priority_class) { return(ProcessSpawning.SpawnChildProcess(ExecutablePath, ghostscript_parameters, priority_class)); }
internal static void GrabWebPage_REMOTE(string title, string url, bool may_try_again_on_exception) { StatusManager.Instance.UpdateStatus("HTMLToPDF", "Converting HTML to PDF"); // Strip off the trailing # cos Web2PDF hates is if (url.Contains('#')) { string old_url = url; url = url.Substring(0, url.IndexOf('#')); Logging.Info("Stripping the # off the original, from '{0}' to '{1}'", old_url, url); } string filename = Path.GetTempFileName() + ".pdf"; try { // Spawn the conversion process { string user_override_global = ""; string user_override_page = ""; string process_parameters = String.Format( "{0} {1} --footer-right \"Page [page] of [topage]\" {2} --footer-left \"{3}\" --header-left \"{4}\" --header-right \"Created using www.qiqqa.com\" \"{5}\"" , user_override_global , url , user_override_page , url.Replace('"', '\'') , title.Replace('"', '\'') , filename ); // STDOUT/STDERR using (Process process = ProcessSpawning.SpawnChildProcess(ConfigurationManager.Instance.ProgramHTMLToPDF, process_parameters, ProcessPriorityClass.Normal)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { process.WaitForExit(); Logging.Info("HTMLToPDF:\n{0}", process_output_reader.GetOutputsDumpString()); } } } StatusManager.Instance.UpdateStatus("HTMLToPDF", "Converting HTML to PDF: adding to library"); PDFDocument pdf_document = Library.GuestInstance.AddNewDocumentToLibrary_SYNCHRONOUS(filename, url, url, null, null, null, true, true); pdf_document.Title = title; pdf_document.Year = Convert.ToString(DateTime.Now.Year); pdf_document.DownloadLocation = url; WPFDoEvents.InvokeInUIThread(() => { PDFReadingControl pdf_reading_control = MainWindowServiceDispatcher.Instance.OpenDocument(pdf_document); pdf_reading_control.EnableGuestMoveNotification(); }, priority: DispatcherPriority.Background ); } catch (Exception ex) { // Give it a 2nd try... if (may_try_again_on_exception) { GrabWebPage_REMOTE(title, url, false); } else { throw new UsefulTextException("Problem converting HTML page to PDF. Please try again later.", String.Format("There has been a problem converting this web page '{0}' with title '{1}' to a PDF.", url, title), ex); } } finally { FileTools.Delete(filename); } StatusManager.Instance.UpdateStatus("HTMLToPDF", "Converting HTML to PDF: done"); }
internal static void DoBundle() { string target_filename = null; try { // Get the destination location SaveFileDialog save_file_dialog = new SaveFileDialog(); save_file_dialog.AddExtension = true; save_file_dialog.CheckPathExists = true; save_file_dialog.DereferenceLinks = true; save_file_dialog.OverwritePrompt = true; save_file_dialog.ValidateNames = true; save_file_dialog.DefaultExt = "7z"; save_file_dialog.Filter = "7Z files (*.7z)|*.7z|All files (*.*)|*.*"; save_file_dialog.FileName = "QiqqaLogs.7z"; // Generate and save if (true == save_file_dialog.ShowDialog()) { target_filename = save_file_dialog.FileName; } } catch (Exception ex) { Logging.Warn(ex, "Problem zipping logs"); MessageBoxes.Error("Unfortunately there was a problem creating the log bundle. Please zip them manually, they are found at C:\\Temp\\Qiqqa.log*. There may be more than one. Thanks!"); target_filename = null; } if (target_filename != null) { int progress = 1; int wait_period = 300; const int MAX_PROGRESS = 100; StatusManager.Instance.UpdateStatus("LogBundler", "Bundling the logfile. Please wait...", progress, MAX_PROGRESS); SafeThreadPool.QueueUserWorkItem(o => { string environment_details_filename = null; try { // Delete the target filename if it exists... FileTools.Delete(target_filename); // Note: Path.GetFullPath() throws an exception when you feed it wildcards, e.g. '*' // hence we construct the search path in two steps: const string MAGIC_FILENAME = @"QQQ"; string file_list = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), @"Quantisle/Qiqqa/Logs", MAGIC_FILENAME)); // and then we replace the 'magic file name' with the real thing: file_list = $"\"{Path.GetSuffixedDirectoryName(file_list)}Qiqqa*.log*\""; // Try get this info for us which could be useful... // If things are really broken, it may fail, that's ok. try { string environmentDetails = "Generated at:" + DateTime.UtcNow.ToString("yyyyMMdd HH:mm:ss") + Environment.NewLine; environmentDetails += ComputerStatistics.GetCommonStatistics(ConfigurationManager.GetCurrentConfigInfos()); environmentDetails += "\r\nConfiguration Bits:\r\n"; environmentDetails += $"Background Tasks: {(ConfigurationManager.Instance.ConfigurationRecord.DisableAllBackgroundTasks ? "Disabled ALL" : "Normal (Enabled)")}\r\n"; environmentDetails += $"Library OCR Task: {(ConfigurationManager.Instance.ConfigurationRecord.Library_OCRDisabled ? "Disabled" : "Normal (Enabled)")}\r\n"; environment_details_filename = TempFile.GenerateTempFilename("txt"); File.WriteAllText(environment_details_filename, environmentDetails); } catch (Exception ex) { Logging.Warn(ex, "Could not get environment details"); } if (environment_details_filename != null) { file_list += " \"" + environment_details_filename + "\""; } // STDOUT/STDERR string process_parameters = String.Format("a -t7z -mmt=on -mx9 -ssw \"{0}\" {1}", target_filename, file_list); Logging.Info($"Bundling the logfiles via command:\n {ConfigurationManager.Instance.Program7ZIP} {process_parameters}"); using (Process process = ProcessSpawning.SpawnChildProcess(ConfigurationManager.Instance.Program7ZIP, process_parameters, ProcessPriorityClass.Normal)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { while (!process.WaitForExit(wait_period)) { progress = Math.Min(MAX_PROGRESS - 5, progress + 1); if (progress >= 25) { wait_period = 1000; } StatusManager.Instance.UpdateStatus("LogBundler", "Bundling the logfile. Please wait...", progress, MAX_PROGRESS); } Logging.Info("7ZIP Log Bundling progress:\n{0}", process_output_reader.GetOutputsDumpStrings()); } MessageBoxes.Info($"The Qiqqa logs with some diagnostic info have been zipped to the location you specified:\n{target_filename}\n\nPlease upload it as issue attachment in your issue filed at https://github.com/jimmejardine/qiqqa-open-source/issues if the support team has requested it. Many thanks!"); FileTools.BrowseToFileInExplorer(target_filename); } } catch (Exception ex) { Logging.Warn(ex, "Problem zipping logs"); MessageBoxes.Error("Unfortunately there was a problem creating the log bundle. Please zip them manually, they are found at C:\\Temp\\Qiqqa.log*. There may be more than one. Thanks!"); target_filename = null; } if (environment_details_filename != null) { FileTools.Delete(environment_details_filename); } }); } }
private static ExecResultAggregate ReadEntireStandardOutput(string pdfDrawExe, string process_parameters, bool binary_output, ProcessPriorityClass priority_class) { WPFDoEvents.AssertThisCodeIs_NOT_RunningInTheUIThread(); ExecResultAggregate rv = new ExecResultAggregate { executable = pdfDrawExe, process_parameters = process_parameters, stdoutIsBinary = binary_output }; Stopwatch clk = Stopwatch.StartNew(); // STDOUT/STDERR Logging.Debug("PDFDRAW :: ReadEntireStandardOutput command: pdfdraw.exe {0}", process_parameters); using (Process process = ProcessSpawning.SpawnChildProcess(pdfDrawExe, process_parameters, priority_class, stdout_is_binary: true)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process, stdout_is_binary: true)) { // Read image from stdout using (StreamReader sr = process.StandardOutput) { using (FileStream fs = (FileStream)sr.BaseStream) { long elapsed = clk.ElapsedMilliseconds; Logging.Debug("PDFDRAW :: ReadEntireStandardOutput setup time: {0} ms for parameters:\n {1}", elapsed, process_parameters); rv.stdoutStream = new MemoryStream(1024 * 1024); int total_size = StreamToFile.CopyStreamToStream(fs, rv.stdoutStream); long elapsed2 = clk.ElapsedMilliseconds; Logging.Debug("PDFDRAW image output {0} bytes in {1} ms (output copy took {2} ms) for command:\n {4} {3}", total_size, elapsed2, elapsed2 - elapsed, process_parameters, pdfDrawExe); // Check that the process has exited properly process.WaitForExit(1000); if (!process.HasExited) { Logging.Debug("PDFRenderer process did not terminate, so killing it.\n{0}", process_output_reader.GetOutputsDumpStrings().stderr); try { process.Kill(); // wait for the completion signal; this also helps to collect all STDERR output of the application (even while it was KILLED) process.WaitForExit(3000); } catch (Exception ex) { Logging.Error(ex, "There was a problem killing the PDFRenderer process after timeout ({0} ms)", elapsed2 + 1000); } // grab stderr output for successful runs and log it anyway: MuPDF diagnostics, etc. come this way: var outs = process_output_reader.GetOutputsDumpStrings(); rv.errOutputDump = outs; Logging.Error($"PDFRenderer process did not terminate, so killed it. Commandline:\n {pdfDrawExe} {process_parameters}\n{outs.stderr}"); rv.error = new ApplicationException($"PDFRenderer process did not terminate, so killed it.\n Commandline: {pdfDrawExe} {process_parameters}"); rv.exitCode = 0; if (process.HasExited) { rv.exitCode = process.ExitCode; } if (rv.exitCode == 0) { rv.exitCode = -666; // timeout } } else if (process.ExitCode != 0) { // grab stderr output for successful runs and log it anyway: MuPDF diagnostics, etc. come this way: var outs = process_output_reader.GetOutputsDumpStrings(); rv.errOutputDump = outs; Logging.Error("PDFDRAW did fail with exit code {0} for commandline:\n {3} {1}\n{2}", process.ExitCode, process_parameters, outs.stderr, pdfDrawExe); rv.error = new ApplicationException($"PDFRenderer::PDFDRAW did fail with exit code {process.ExitCode}.\n Commandline: {pdfDrawExe} {process_parameters}"); rv.exitCode = process.ExitCode; } else { // grab stderr output for successful runs and log it anyway: MuPDF diagnostics, etc. come this way: var outs = process_output_reader.GetOutputsDumpStrings(); rv.errOutputDump = outs; Logging.Error("PDFDRAW did SUCCEED with exit code {0} for commandline:\n {3} {1}\n{2}", process.ExitCode, process_parameters, outs.stderr, pdfDrawExe); rv.error = null; rv.exitCode = process.ExitCode; } return(rv); } } } } }