| File: BackEnd\RedirectConsoleWriter.cs | Web Access |
| Project: Microsoft.Build.Framework.csproj (Microsoft.Build.Framework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using System.Threading; namespace Microsoft.Build.BackEnd; /// <remarks> /// Disposed instances remain writable but discard output because third-party code may retain /// <see cref="Console.Out"/> across server requests. Stale output must not reach a later client. /// </remarks> internal sealed class RedirectConsoleWriter : TextWriter { private readonly Action<string> _writeCallback; private readonly Timer _timer; private readonly LockType _lock = new LockType(); private readonly StringWriter _bufferWriter; private TextWriter _destination; private bool _disposed; public RedirectConsoleWriter(Action<string> writeCallback) { _writeCallback = writeCallback; _bufferWriter = new StringWriter(formatProvider: null); _destination = _bufferWriter; _timer = new Timer(TimerCallback, null, 0, 40); } public override Encoding Encoding => _bufferWriter.Encoding; [AllowNull] public override string NewLine { get { lock (_lock) { return base.NewLine; } } set { lock (_lock) { base.NewLine = value; _bufferWriter.NewLine = value; } } } public override void Flush() { lock (_lock) { if (!_disposed) { FlushInternal(); } } } public override void Write(char value) { lock (_lock) { _destination.Write(value); } } public override void Write(char[]? buffer) { lock (_lock) { _destination.Write(buffer); } } public override void Write(char[] buffer, int index, int count) { lock (_lock) { _destination.Write(buffer, index, count); } } public override void Write(bool value) { lock (_lock) { _destination.Write(value); } } public override void Write(int value) { lock (_lock) { _destination.Write(value); } } public override void Write(uint value) { lock (_lock) { _destination.Write(value); } } public override void Write(long value) { lock (_lock) { _destination.Write(value); } } public override void Write(ulong value) { lock (_lock) { _destination.Write(value); } } public override void Write(float value) { lock (_lock) { _destination.Write(value); } } public override void Write(double value) { lock (_lock) { _destination.Write(value); } } public override void Write(decimal value) { lock (_lock) { _destination.Write(value); } } public override void Write(string? value) { lock (_lock) { _destination.Write(value); } } public override void Write(object? value) { lock (_lock) { _destination.Write(value); } } public override void Write(string format, object? arg0) { lock (_lock) { _destination.Write(format, arg0); } } public override void Write(string format, object? arg0, object? arg1) { lock (_lock) { _destination.Write(format, arg0, arg1); } } public override void Write(string format, object? arg0, object? arg1, object? arg2) { lock (_lock) { _destination.Write(format, arg0, arg1, arg2); } } public override void Write(string format, params object?[] arg) { lock (_lock) { _destination.Write(format, arg); } } public override void WriteLine() { lock (_lock) { _destination.WriteLine(); } } public override void WriteLine(char value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(decimal value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(char[]? buffer) { lock (_lock) { _destination.WriteLine(buffer); } } public override void WriteLine(char[] buffer, int index, int count) { lock (_lock) { _destination.WriteLine(buffer, index, count); } } public override void WriteLine(bool value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(int value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(uint value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(long value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(ulong value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(float value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(double value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(string? value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(object? value) { lock (_lock) { _destination.WriteLine(value); } } public override void WriteLine(string format, object? arg0) { lock (_lock) { _destination.WriteLine(format, arg0); } } public override void WriteLine(string format, object? arg0, object? arg1) { lock (_lock) { _destination.WriteLine(format, arg0, arg1); } } public override void WriteLine(string format, object? arg0, object? arg1, object? arg2) { lock (_lock) { _destination.WriteLine(format, arg0, arg1, arg2); } } public override void WriteLine(string format, params object?[] arg) { lock (_lock) { _destination.WriteLine(format, arg); } } private void TimerCallback(object? state) { lock (_lock) { if (!_disposed) { FlushInternal(flushIncompleteSurrogate: false); } } } protected override void Dispose(bool disposing) { if (disposing) { _timer.Dispose(); lock (_lock) { if (!_disposed) { try { FlushInternal(); } finally { _destination = TextWriter.Null; _disposed = true; _bufferWriter.Dispose(); } } } } base.Dispose(disposing); } private void FlushInternal(bool flushIncompleteSurrogate = true) { StringBuilder buffer = _bufferWriter.GetStringBuilder(); int length = buffer.Length; // Each packet is encoded independently; a periodic flush must not split a surrogate pair. if (length > 0 && char.IsHighSurrogate(buffer[length - 1])) { if (flushIncompleteSurrogate) { buffer[length - 1] = '\uFFFD'; } else { length--; } } if (length == 0) { return; } string captured = buffer.ToString(0, length); buffer.Remove(0, length); _writeCallback(captured); _bufferWriter.Flush(); } }