1

I noticed in one of my application that calling Console.WriteLine() doesn't work every time.

Meaning, that the written string isn't terminated with a line break and the next call to Console.WriteLine() will be printed on the same line.

To test the behaviour, I have written the following reproducible app

@Wyck has contributed simpler code to replicate the issue:

for (var i = 0; i < 30; i++) 
Console.WriteLine(i.ToString().PadRight(100 + i, 'a'));

Here my original code with some bloat to it:

internal class Program
{
    static void Main(string[] args)
    {
        var rnd = new Random();

        for (var i = 0; i < 200; i++)
        {
            Thread.Sleep(rnd.Next(0, 10));
            var str = i.ToString().PadRight(rnd.Next(90, 130), 'a');

            switch (rnd.Next(0, 3))
            {
                case 0:
                    ConsoleEx.WriteLineInfo(str);
                    break;
                case 1:
                    ConsoleEx.WriteLineWarning(str);
                    break;
                case 2:
                    ConsoleEx.WriteLineError(str);
                    break;
            }
        }

        Console.ReadKey();
    }
}

internal static class ConsoleEx
{
    #region Static Public Methods

    public static void WriteLine(string str, ConsoleColor foregroundColor)
    {
        //var oldForegroundColor = Console.ForegroundColor;
        //Console.ForegroundColor = foregroundColor;
        Console.WriteLine(str);
        //Console.ForegroundColor = oldForegroundColor;
    }
    public static void WriteLineError(string str) => WriteLine(str, ConsoleColor.Red);
    public static void WriteLineInfo(string str) => WriteLine(str, ConsoleColor.Green);
    public static void WriteLineWarning(string str) => WriteLine(str, ConsoleColor.Yellow);

    #endregion
}

Things I noticed

  • I could only replicate the issue by actually executing the generated *.exe, so never saw the issue while debugging.
  • using redirect ConsoleApp.exe > output.txt doesn't seem to replicate the issue
  • changing the ForegroundColor doesn't seem to contribute to the error

there for I believe it is maybe related to the Windows-Terminal Version I am using 1.16.10262.0

Here a screenshot of the issue:

enter image description here

enter image description here

Every number should be on a separate line, since all outputs are called with Console.WriteLine().

Is there something I can do in C#, or should I head over to https://github.com/microsoft/terminal/issues and report a bug?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Rand Random
  • 7,300
  • 10
  • 40
  • 88
  • Just out of curiosity - what happens if you comment out the line containing the Thread.Sleep() call? I have seen similar behavior when using Thread.Sleep() -- see the following: https://stackoverflow.com/questions/8815895/why-is-thread-sleep-so-harmful and https://web.archive.org/web/20190109032259/http://zvolkov.com/clog/2009/07/10/net-4-0-and-better-ways-to-wait-for-queued-threads-to-complete/ – bdcoder May 26 '23 at 14:44
  • Have you tried with another terminal, PS, for example? – Marco Olimpio May 26 '23 at 14:45
  • @MarcoOlimpio - PS within the windows terminal app does produce the same result: https://i.stack.imgur.com/w7t2K.png – Rand Random May 26 '23 at 14:49
  • 1
    A simpler repro: `for (var i = 0; i < 30; i++) Console.WriteLine(i.ToString().PadRight(100 + i, 'a'));` – Wyck May 26 '23 at 14:59
  • @bdcoder - without thread sleep the output disappears completly (only the last view lines remain) but everything above the scrollviewer threshhold is gone: https://i.stack.imgur.com/U9reA.png – Rand Random May 26 '23 at 14:59
  • @RandRandom - Interesting - but the numbers DO appear on a separate line. Why the first 175 lines are not present is another mystery! Like you, I usually redirect output to a text file and that seems to work. May want to change the terminal buffer size. – bdcoder May 26 '23 at 15:02
  • @Wyck - thanks for sharing the easier code to replicate the issue, after digging around in my orignal code finding an error on my side I didn't go the extra mile and was happy to finally have something reproducible – Rand Random May 26 '23 at 15:09
  • @bdcoder - I forgot to resize the window prior to taking the screenshot - as my error only seems show when you resize the window, and the terminal app fails to display a line break – Rand Random May 26 '23 at 15:11
  • @RandRandom - found this as well: https://developercommunity.visualstudio.com/t/if-the-length-is-a-multiple-of-120-consolewritelin/1604579 – bdcoder May 26 '23 at 15:38
  • The Console.WriteLine adds the endline for the environment where it executes at the end of the string. In the case, can you try to change for Write() and you can add "\r\n" to windows env. I've executed the example of @Wyck in linqpad7 and with the interpreter (csi), and it worked like it should - https://imgur.com/a/laRtcTC. Do you have another thread that puts something in the default output? – Marco Olimpio May 26 '23 at 16:07

2 Answers2

2

You didn't change the width of the terminal window by any chance? - When narrowing it and expanding it again you can get a similar effect. Console.WriteLine(str); seems to work correctly - I tested it at home in the debugger window and Terminal Windows window.

Version: 1.16.10262.0

printscreen

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Loel
  • 23
  • 6
  • after my application has completed I am resizing the termincal window to get rid of wrapping text because of overflow – Rand Random May 26 '23 at 14:38
  • The OP *did* specify that the issue was not seen in debugging – Hans Kesting May 26 '23 at 14:39
  • notice: your screenshot shows the error with `49aaa....` - as it should be on a seperate line – Rand Random May 26 '23 at 14:39
  • well yes - but after displaying the string on the console - originally it was ok, then by changing the width of the console I got this "error". – Loel May 26 '23 at 14:44
  • so you mean to say this proofs that it is a visual bug of the terminal app and there is nothing I can do in c# and should report it as bug to terminal github page? – Rand Random May 26 '23 at 14:46
  • This is what it looks like to me - as a visual error of the terminal - report it, of course - because it is indeed an error. – Loel May 26 '23 at 14:48
  • Thanks, for taking your time to reproduce the error. When I get confirmation from the devs on windows terminal team, I will come back to the question. – Rand Random May 26 '23 at 14:56
2

I don't have all the details here because it's a black box, but I think you've exposed a quirk (it may be legacy DOS behaviour) in how the terminal stores information in its screen buffer and how it reformats that data in the screen buffer to fit to a window of a different size.

The feature is called Wrap text output on resize.

Wrap text output on resize

Because of the way the terminal software buffers records the screen output by storing the printable characters and the coordinates to which they were displayed, it can't easily obviously tell the difference between a line that had a line ending at the last column and one that ran straight through because the line ending is not a printable character. This is because the cursor can be relocated to write to any location on the screen, yet you probably expect the screen buffer to be interpreted as an aggregate of the final printed output so that scrolling and copying do something reasonable.

The buffer basically works like a 2D array of printed characters. Non-printable characters (including the new-line) cannot be copied and pasted from the terminal buffer, for example, and have to be inferred from the layout. Yet line endings do seem to be preserved via a little bit of (perhaps not 100% correct the way you want it to be), magic in the implementation. When you resize the terminal it has to rearrange the printed characters from a 2D array of one width to a 2D array of a different width. But it clearly remembers something about the original layout at the time it was printed because the line endings are preserved mostly. But when the new-line is the last character of output, there's some ambiguity about what the buffer is supposed to look like with respect to word wrapping, so I think the implementation is just doing its best to make sense of things and do the right thing in most cases. (Again, think of how the ANSI output position controls work and the complexity of functionality it has to support.)

It has nothing to do with sleep :)

Again, there's probably some MS-DOS or ANSI control sequence legacy behaviour it's trying to preserve.

Wyck
  • 10,311
  • 6
  • 39
  • 60