douglasg14b / BetterConsoleTables

Faster, colorable, more configurable, and more robust console colors & tables for C# console applications
GNU Lesser General Public License v3.0
92 stars 16 forks source link

PadRowInConsole can generate an ArgumentOutOfRangeException when outputting to file instead of console #35

Closed JonathanNorton closed 10 months ago

JonathanNorton commented 10 months ago

BetterConsoles.Tables.TableBase.PadRowInConsole can generate an ArgumentOutOfRangeException when outputting to a file using: >> file.txt. This happens when the table is too large horizontally to fit within the console window.

System.ArgumentOutOfRangeException: Positive number required.
Parameter name: width
Actual value was -1.
   at System.Console.SetWindowSize(Int32 width, Int32 height)
   at BetterConsoles.Tables.TableBase`3.PadRowInConsole(String renderedRow)
   at BetterConsoles.Tables.TableBase`3.GenerateDivider(Int32[] columnLengths, Char innerDelimiter, Char divider, Char left, Char right)
   at BetterConsoles.Tables.Table.ToString(Int32[] columnLengths)
   at BetterConsoles.Tables.Table.ToString()

https://github.com/douglasg14b/BetterConsoleTables/blob/020fbe1c402760353277d560c3daf062f8be7a70/BetterConsoles.Tables/Tables/TableBase.cs#L382

Tested on .NET Framework 4.8 and BetterConsoleTables 2.0.4-rc1

JonathanNorton commented 10 months ago

The below code can reproduce the issue. It generates tables of increasing widths from 40 chars to 300 chars until the program runs to completion or the program crashes.

using BetterConsoles.Tables.Configuration;
using BetterConsoles.Tables;
using System;

namespace WriteToFileCrashGenerator
{
    internal class Program
    {
        static void Main(string[] args)
        {
            for (int i = 40; i < 200; i++)
            {
                Table table = new Table();
                table.Config = TableConfig.Unicode();

                GenerateColumns(table, i);

                Console.Write(table.ToString());
            }
        }

        private static void GenerateColumns(Table table, int length)
        {
            const int max_col = 10;

            while (length > 0)
            {
                table.AddColumn(new string('*', Math.Min(length,  max_col)));
                length -= max_col;
            }
        }
    }
}

This can be run in a console window and output to a console window without generating an exception. However redirecting the console output to a file will generate an exception. See below:

PS C:\Users\admin\Documents\Code\Test\WriteToFileCrashGenerator\bin\Debug> .\WriteToFileCrashGenerator.exe >> test.txt

Unhandled Exception: System.ArgumentOutOfRangeException: Positive number required.
Parameter name: width
Actual value was -1.
   at System.Console.SetWindowSize(Int32 width, Int32 height)
   at BetterConsoles.Tables.TableBase`3.PadRowInConsole(String renderedRow)
   at BetterConsoles.Tables.TableBase`3.GenerateDivider(Int32[] columnLengths, Char innerDelimiter, Char divider, Char left, Char right)
   at BetterConsoles.Tables.Table.ToString(Int32[] columnLengths)
   at BetterConsoles.Tables.Table.ToString()
   at WriteToFileCrashGenerator.Program.Main(String[] args) in C:\Users\admin\Documents\Code\CS\MdbToText\WriteToFileCrashGenerator\Program.cs:line 24
PS C:\Users\admin\Documents\Code\Test\WriteToFileCrashGenerator\bin\Debug>
JonathanNorton commented 10 months ago

The line at fault is this one:

https://github.com/douglasg14b/BetterConsoleTables/blob/020fbe1c402760353277d560c3daf062f8be7a70/BetterConsoles.Tables/Tables/TableBase.cs#L400

Console.LargestWindowWidth returns zero when redirecting the output to a file. The value assigned to Console.WindowWidth needs to be validated before it is assigned. e.g.

var new_width = Math.Min(renderedRow.Length + 1, Console.LargestWindowWidth - 1);
if (new_width > 0)
{
    Console.WindowWidth = new_width;
}
douglasg14b commented 10 months ago

10/10 issue reporting, thank you!

Looking into it now.

douglasg14b commented 10 months ago

You should be good to go! Again, thank you for the report that was essentially a PR for it's details ^_^

Any sort of improvements or enhancements you would like to see? I'm planning on swinging back around to this lib to do some major refactoring & enhancements in the future.