Search code examples
c#.netconsole-applicationscrollbarwindows-console

Remove space left after console scrollbars in C#


I'm making a console game in C# in Visual Studio and I want to make the game to be in the full console window.

I resized the buffer and window to be the same size, but it looks like the space after the scrollbars remained, which will probably be really annoying to look at. I wonder if there's any way to remove that blank space in C#, or at least add grayed-out scrollbars. I saw a similar thread, but it was in C++.

Scrollbars

Here's a reproducible example:

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace ConsoleTest1
{
    class Program
    {
        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern SafeFileHandle CreateFile(
            string fileName,
            [MarshalAs(UnmanagedType.U4)] uint fileAccess,
            [MarshalAs(UnmanagedType.U4)] uint fileShare,
            IntPtr securityAttributes,
            [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
            [MarshalAs(UnmanagedType.U4)] int flags,
            IntPtr template);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool WriteConsoleOutput(
          SafeFileHandle hConsoleOutput,
          CharInfo[] lpBuffer,
          Coord dwBufferSize,
          Coord dwBufferCoord,
          ref SmallRect lpWriteRegion);

        [StructLayout(LayoutKind.Sequential)]
        public struct Coord
        {
            public short X, Y;

            public Coord(short x, short y)
            {
                X = x;
                Y = y;
            }
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct CharUnion
        {
            [FieldOffset(0)] public char UnicodeChar;
            [FieldOffset(0)] public byte AsciiChar;
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct CharInfo
        {
            [FieldOffset(0)] public CharUnion Char;
            [FieldOffset(2)] public short Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SmallRect
        {
            public short Left, Top, Right, Bottom;

            public SmallRect(short width, short height)
            {
                Left = Top = 0;
                Right = width;
                Bottom = height;
            }
        }


        [STAThread]
        static void Main(string[] args)
        {
            short width = 50, height = 20;
            SafeFileHandle handle = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
            CharInfo[] buffer = new CharInfo[width * height];
            SmallRect writeRegion = new SmallRect(width, height);

            Console.SetWindowSize(width, height);
            Console.SetBufferSize(width, height);

            for (int i = 0; i < buffer.Length; ++i)
            {
                buffer[i].Attributes = 0xb0;
                buffer[i].Char.UnicodeChar = ' ';
            }

            WriteConsoleOutput(handle, buffer, new Coord(width, height), new Coord(0, 0), ref writeRegion);
            Console.ReadKey();
        }
    }
}

I don't really know how to do this or even if it's possible to remove the black space in this language.


Solution

  • Finally, after a lot of head-scratching, I think I've solved this issue. Firstly, I had to add some additional WinAPI methods:

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetStdHandle(int nStdHandle);
    
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetConsoleScreenBufferInfoEx(
        IntPtr hConsoleOutput,
        ref ConsoleScreenBufferInfoEx ConsoleScreenBufferInfo);
    
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool SetConsoleScreenBufferInfoEx(
        IntPtr hConsoleOutput,
        ref ConsoleScreenBufferInfoEx ConsoleScreenBufferInfoEx);
    

    and structs:

    [StructLayout(LayoutKind.Sequential)]
    private struct ConsoleScreenBufferInfoEx
    {
        public uint cbSize;
        public Coord dwSize;
        public Coord dwCursorPosition;
        public short wAttributes;
        public SmallRect srWindow;
        public Coord dwMaximumWindowSize;
        public ushort wPopupAttributes;
        public bool bFullscreenSupported;
    
        public Colorref black, darkBlue, darkGreen, darkCyan, darkRed, darkMagenta, darkYellow, gray, darkGray, blue, green, cyan, red, magenta, yellow, white;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct Colorref
    {
        public uint ColorDWORD;
    }
    

    After that, it was time to modify the part where the buffer and window were resized:

    Console.SetWindowSize(width - 2, height);
    Console.SetBufferSize(width, height);
    
    IntPtr stdHandle = GetStdHandle(-11);
    ConsoleScreenBufferInfoEx bufferInfo = new ConsoleScreenBufferInfoEx();
    bufferInfo.cbSize = (uint)Marshal.SizeOf(bufferInfo);
    GetConsoleScreenBufferInfoEx(stdHandle, ref bufferInfo);
    ++bufferInfo.srWindow.Right;
    ++bufferInfo.srWindow.Bottom;
    SetConsoleScreenBufferInfoEx(stdHandle, ref bufferInfo);
    

    And that's it! No more ugly black space (at least on my computer, haven't tested it on other Windows versions).

    It works.