Pergunta

When I run msbuild at the command line it shows pretty colours in the console.

However when I run it from C# with Process.Start, the output appears in black and white. How can I keep the colours?

var info = new ProcessStartInfo("msbuild")
{
    UseShellExecute = false,
    CreateNoWindow = true,
    RedirectStandardError = true,
    RedirectStandardOutput = true,            
};

using (var p = Process.Start(info) )
{
    p.ErrorDataReceived += (s, e) => Console.Error.WriteLine(e.Data);
    p.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
    p.BeginErrorReadLine();
    p.BeginOutputReadLine();
    p.WaitForExit();
}

Also, while we're here, does it matter than I run Process.Start before BeginOutputReadLine ? Will any output be lost?


Motivation, for those interested. A project I work on uses a custom build tool (re-inventing the wheel imho). It uses msbuild but behind convoluted layers of indirection (simplified model above). Msbuild's helpful colours are lost. I'd like to save them.

Foi útil?

Solução

   p.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);

Process.OutputDataReceived reads text, not colors. The output redirection feature that's underneath this only redirect stdout text, not the console color attributes. You get the exact same thing when you run msbuild with the > redirect operator from the command line to send its output to a text file. You'll of course see bland text when you open the text file in Notepad.

Parsing the redirected output to re-color your own output is drastically impractical. You are stuck with bland. Then again, programmers don't complain often about the look-and-feel of the Error List window in the IDE :)

Outras dicas

Thats it, there is no other way to do it. Your code first starts the process and then appends the eventhandler. So there will be maybe some data that are lost, but that depends on how fast the cpu processes the code. You should better append the eventhandler first and then start the process. (see below)

using (var p = new Process())
{
    p.StartInfo = new ProcessStartInfo("msbuild")
    {
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
    };
    p.ErrorDataReceived += (s, e) => ErrorLine(e.Data);
    p.OutputDataReceived += (s, e) => OutputLine(e.Data);
    p.BeginErrorReadLine();
    p.BeginOutputReadLine();
    p.Start();
    p.WaitForExit();
}
void ErrorLine(string text)
{
    Console.ForegroundColor = ConsoleColor.White;
    Console.BackgroundColor = ConsoleColor.DarkRed;
    Console.Error.WriteLine(text);
    Console.ResetColor();
}
void OutputLine(string text)
{
    Console.Error.WriteLine(text);
}

I don't know about how can do this specifically for msbuild with all the warnings/errors/other things that do different colours, but you can change the console colour by using Console.ForegroundColor = ConsoleColor.Red; before you write to it, and reset it with Console.ResetColor();

So you would change ErrorDataRecieved subscription to do change the colour to red before you write, and reset the colour after you write the output.

Potential solution to this problem. It is now possible to solve this problem because the Console infrastructure was almost entirely reworked on Windows. Introducing Windows Pseudo Console

Creating the MsBuild using a ConPTY will give out full VT output.

 public void Start(string command, int consoleWidth = 80, int consoleHeight = 30)
    {
        using (var inputPipe = new PseudoConsolePipe())
        using (var outputPipe = new PseudoConsolePipe())
        using (var pseudoConsole = PseudoConsole.Create(inputPipe.ReadSide, outputPipe.WriteSide, consoleWidth, consoleHeight))
        using (var process = ProcessFactory.Start(command, PseudoConsole.PseudoConsoleThreadAttribute, pseudoConsole.Handle))
        {
            // copy all pseudoconsole output to a FileStream and expose it to the rest of the app
            ConsoleOutStream = new FileStream(outputPipe.ReadSide, FileAccess.Read);
            OutputReady.Invoke(this, EventArgs.Empty);

            // Store input pipe handle, and a writer for later reuse
            _consoleInputPipeWriteHandle = inputPipe.WriteSide;
            _consoleInputWriter = new StreamWriter(new FileStream(_consoleInputPipeWriteHandle, FileAccess.Write))
            {
                AutoFlush = true
            };

            // free resources in case the console is ungracefully closed (e.g. by the 'x' in the window titlebar)
            OnClose(() => DisposeResources(process, pseudoConsole, outputPipe, inputPipe, _consoleInputWriter));

            WaitForExit(process).WaitOne(Timeout.Infinite);
        }
    }

Source: https://github.com/microsoft/terminal/blob/07d06f62aa5a883f70cbe8572bf8ea1f8577f53f/samples/ConPTY/GUIConsole/GUIConsole.ConPTY/Terminal.cs

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top