4

我有一个VsPackage类似于以下内容的自定义输出窗格:

    ///--------------------------------------------------------------------------------
    /// <summary>This property gets the custom output pane.</summary>
    ///--------------------------------------------------------------------------------
    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;
        }
    }

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

    ///--------------------------------------------------------------------------------
    /// <summary>This method displays a message in the output area.</summary>
    /// 
    /// <param name="outputTitle">The title for the message.</param>
    /// <param name="outputMessage">The message to show.</param>
    /// <param name="appendMessage">Flag indicating whether message should be appended to existing message.</param>
    ///--------------------------------------------------------------------------------
    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将当前解决方案的诊断结果发送到控制台。它的设置类似于以下内容:

///-------------------------------------------------------------------------------- 
/// <summary>This method handles clicking on the Run Diagnostics submenu.</summary> 
///  
/// <param term='inputCommandBarControl'>The control that is source of the click.</param> 
/// <param term='handled'>Handled flag.</param> 
/// <param term='cancelDefault'>Cancel default flag.</param> 
///-------------------------------------------------------------------------------- 
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(); 

///-------------------------------------------------------------------------------- 
/// <summary>This method launches the diagnostics to review the solution.</summary> 
///-------------------------------------------------------------------------------- 
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); 
    } 
} 

///-------------------------------------------------------------------------------- 
/// <summary>This method handles completing the run diagnostics thread.</summary> 
///  
/// <param name="ar">IAsyncResult.</param> 
///-------------------------------------------------------------------------------- 
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); 
    } 
} 

当我从 启动它external processVSPackage,我想将这些结果(间接)流式传输到自定义输出窗格,在诊断工具报告它们时显示消息。有什么好的方法吗?

4

4 回答 4

3

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

于 2011-12-01T17:53:34.580 回答
3

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

///--------------------------------------------------------------------------------  
/// <summary>This method launches the diagnostics to review the solution.</summary>  
///--------------------------------------------------------------------------------  
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);  
    }  
}  
于 2011-12-01T19:26:26.087 回答
1

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

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);
于 2011-12-01T17:39:05.840 回答
1

尽管 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;

    /// <summary>  
    /// The constructor requires a reference to the process that will be read.  
    /// The process should have .RedirectStandardOutput and .RedirectStandardError set to true.  
    /// </summary>  
    /// <param name="process">The process that will have its output read by this class.</param>  
    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.");
    }

    /// <summary>  
    /// This method starts reading the standard error stream from Process.  
    /// </summary>  
    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
        }
    }
    /// <summary>  
    /// This method starts reading the standard output sream from Process.  
    /// </summary>  
    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();
   }
}

这是从 BackgroundWorkerDoWork方法中调用的,Worker 作为引用传递。

于 2011-12-02T08:16:36.573 回答