有没有一种方法可以将外部进程的结果流式传输到Visual Studio输出窗格?

我在VsPackage设置了一个自定义输出窗格,类似于以下内容:

  ///-------------------------------------------------------------------------------- /// This property gets the custom output pane. ///-------------------------------------------------------------------------------- private Guid _customPaneGuid = Guid.Empty; private IVsOutputWindowPane _customPane = null; private IVsOutputWindowPane customPane { get { if (_customPane == null) { IVsOutputWindow outputWindow = GetService(typeof(SVsOutputWindow)) as IVsOutputWindow; if (outputWindow != null) { // look for existing solution updater pane if (_customPaneGuid == Guid.Empty || ErrorHandler.Failed(outputWindow.GetPane(ref _customPaneGuid, out _customPane)) || _customPane == null) { // create a new solution updater pane outputWindow.CreatePane(ref _customPaneGuid, "My Output", 1, 1); if (ErrorHandler.Failed(outputWindow.GetPane(ref _customPaneGuid, out _customPane)) || _customPane == null) { // pane could not be created and retrieved, throw exception throw new Exception("Custom pane could not be created and/or retrieved"); } } } } if (_customPane != null) { _customPane.Activate(); } return _customPane; } } 

并使用类似于以下方法的方法将消息发送到此窗格:

  ///-------------------------------------------------------------------------------- /// This method displays a message in the output area. /// /// The title for the message. /// The message to show. /// Flag indicating whether message should be appended to existing message. ///-------------------------------------------------------------------------------- public void ShowOutput(string outputTitle, string outputMessage, bool appendMessage, bool isException) { if (appendMessage == false) { // clear output pane CustomPane.Clear(); } if (outputTitle != string.Empty) { // put output title to output pane CustomPane.OutputString("\r\n" + outputTitle); } // put output message to output pane CustomPane.OutputString("\r\n" + outputMessage); if (isException == true) { // show message box MessageBox.Show(outputTitle + "\r\n" + outputMessage, outputTitle); } } 

我有一个external process ,可以将当前解决方案的诊断结果发送到控制台。 它的设置类似于以下内容:

 ///-------------------------------------------------------------------------------- /// This method handles clicking on the Run Diagnostics submenu. /// /// The control that is source of the click. /// Handled flag. /// Cancel default flag. ///-------------------------------------------------------------------------------- protected void RunDiagnostics_Click(object inputCommandBarControl, ref bool handled, ref bool cancelDefault) { try { // set up and execute diagnostics thread RunDiagnosticsDelegate RunDiagnosticsDelegate = RunDiagnostics; RunDiagnosticsDelegate.BeginInvoke(RunDiagnosticsCompleted, RunDiagnosticsDelegate); } catch (Exception ex) { // put exception message in output pane CustomPane.OutputString(ex.Message); } } protected delegate void RunDiagnosticsDelegate(); ///-------------------------------------------------------------------------------- /// This method launches the diagnostics to review the solution. ///-------------------------------------------------------------------------------- protected void RunDiagnostics() { try { // set up diagnostics process string solutionDir = System.IO.Path.GetDirectoryName(_applicationObject.Solution.FullName); System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(@"MyDiagnostics.exe", solutionDir); procStartInfo.RedirectStandardOutput = true; procStartInfo.UseShellExecute = false; procStartInfo.CreateNoWindow = true; System.Diagnostics.Process proc = new System.Diagnostics.Process(); proc.StartInfo = procStartInfo; // execute the diagnostics proc.Start(); // put diagnostics output to output pane CustomPane.OutputString(proc.StandardOutput.ReadToEnd()); CustomPane.OutputString("Diagnostics run complete."); } catch (Exception ex) { // put exception message in output pane CustomPane.OutputString(ex.Message); } } ///-------------------------------------------------------------------------------- /// This method handles completing the run diagnostics thread. /// /// IAsyncResult. ///-------------------------------------------------------------------------------- protected void RunDiagnosticsCompleted(IAsyncResult ar) { try { if (ar == null) throw new ArgumentNullException("ar"); RunDiagnosticsDelegate RunDiagnosticsDelegate = ar.AsyncState as RunDiagnosticsDelegate; Trace.Assert(RunDiagnosticsDelegate != null, "Invalid object type"); RunDiagnosticsDelegate.EndInvoke(ar); } catch (Exception ex) { // put exception message in output pane CustomPane.OutputString(ex.Message); } } 

当我从VSPackage启动此外部external process ,我希望将这些结果(间接)流式传输到自定义输出窗格,在诊断工具报告它们时显示消息。 有没有一个好方法呢?

显然你必须使用Process.BeginOutputReadLine()方法。 可以在此处找到一个示例: System.Diagnostics.Process.BeginOutputReadLine 。

更新了RunDiagnostics,主要是利用J. Tihon的答案和一些the_drow的答案:

 ///-------------------------------------------------------------------------------- /// This method launches the diagnostics to review the solution. ///-------------------------------------------------------------------------------- protected void RunDiagnostics() { try { // set up diagnostics process string solutionDir = System.IO.Path.GetDirectoryName(_applicationObject.Solution.FullName); System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(@"MyDiagnostics.exe", solutionDir); procStartInfo.RedirectStandardOutput = true; procStartInfo.UseShellExecute = false; procStartInfo.CreateNoWindow = true; System.Diagnostics.Process proc = new System.Diagnostics.Process(); proc.StartInfo = procStartInfo; proc.StartInfo.RedirectStandardOutput = true; proc.OutputDataReceived += (object sendingProcess, DataReceivedEventArgs outLine) => ShowOutput(String.Empty, outLine.Data, true, false); // execute the diagnostics proc.Start(); proc.BeginOutputReadLine(); } catch (Exception ex) { // put exception message in output pane CustomPane.OutputString(ex.Message); } } 

虽然OutPutDataReceived + BeginOutputReadLine看起来像一个更优雅和简单的解决方案,但我会给出一个替代方案。 我用BackgroundWorker解决了这个问题,并从这里启发了ProcessOutPutHandler。 此方法还分别处理来自stdout和stderr的消息,我可以根据输出向BackgroundWorker报告进度。 这里我使用标准VS输出窗口作为输出,但也应该使用您的OutputPane:

 public class ProcessOutputHandler { public Process proc { get; set; } public string StdOut { get; set; } public string StdErr { get; set; } private IVsOutputWindowPane _pane; private BackgroundWorker _worker; ///  /// The constructor requires a reference to the process that will be read. /// The process should have .RedirectStandardOutput and .RedirectStandardError set to true. ///  /// The process that will have its output read by this class. public ProcessOutputHandler(Process process, BackgroundWorker worker) { _worker = worker; proc = process; IVsOutputWindow outputWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; Guid guidGeneral = Microsoft.VisualStudio.VSConstants.OutputWindowPaneGuid.GeneralPane_guid; int hr = outputWindow.CreatePane(guidGeneral, "Phone Visualizer", 1, 0); hr = outputWindow.GetPane(guidGeneral, out _pane); _pane.Activate(); _pane.OutputString("Starting Ui State workers.."); StdErr = ""; StdOut = ""; Debug.Assert(proc.StartInfo.RedirectStandardError, "RedirectStandardError must be true to use ProcessOutputHandler."); Debug.Assert(proc.StartInfo.RedirectStandardOutput, "RedirectStandardOut must be true to use ProcessOutputHandler."); } ///  /// This method starts reading the standard error stream from Process. ///  public void ReadStdErr() { string line; while ((!proc.HasExited) && ((line = proc.StandardError.ReadLine()) != null)) { StdErr += line; _pane.OutputString(line + "\n"); // Here I could do something special if errors occur } } ///  /// This method starts reading the standard output sream from Process. ///  public void ReadStdOut() { string line; while ((!proc.HasExited) && ((line = proc.StandardOutput.ReadLine()) != null)) { StdOut += line; _pane.OutputString(line + "\n"); if (_worker != null && line.Contains("Something I'm looking for")) { _worker.ReportProgress(20, "Something worth mentioning happened"); } } } } 

用法:

 void RunProcess(string fileName, string arguments, BackgroundWorker worker) { // prep process ProcessStartInfo psi = new ProcessStartInfo(fileName, arguments); psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; // start process using (Process process = new Process()) { // pass process data process.StartInfo = psi; // prep for multithreaded logging ProcessOutputHandler outputHandler = new ProcessOutputHandler(process,worker); Thread stdOutReader = new Thread(new ThreadStart(outputHandler.ReadStdOut)); Thread stdErrReader = new Thread(new ThreadStart(outputHandler.ReadStdErr)); // start process and stream readers process.Start(); stdOutReader.Start(); stdErrReader.Start(); // wait for process to complete process.WaitForExit(); } } 

这是从BackgroundWorker DoWork方法调用的,Worker作为参考传递。

您可以使用侦听器并将进程的标准输出附加到它。

 ConsoleTraceListener listener = new ConsoleTraceListener(process.StandardOutput); Debug.Listeners.Add(listener); 

确保在流程结束后将其删除:

 proc.Exited += () => Debug.Listeners.Remove(listener); 

您需要使用进程’ OutputDataReceived事件,而不是附加一个侦听器 :

 ConsoleTraceListener listener = new ConsoleTraceListener(); Debug.Listeners.Add(listener); proc.OutputDataReceived += (object sendingProcess, DataReceivedEventArgs outLine) => Trace.WriteLine(outLine.Data); 

确保在流程结束后将其删除:

 proc.Exited += (object sender, EventArgs e) => Debug.Listeners.Remove(listener);