class/ConsoleBufferWriter.cs

using System;
using System.Runtime.InteropServices;
 
public class ConsoleBufferWriter
{
    [StructLayout(LayoutKind.Sequential)]
    public struct COORD
    {
        public short X;
        public short Y;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct CHAR_INFO
    {
        public ushort UnicodeChar;
        public short Attributes;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct SMALL_RECT
    {
        public short Left;
        public short Top;
        public short Right;
        public short Bottom;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct CONSOLE_SCREEN_BUFFER_INFO
    {
        public COORD dwSize;
        public COORD dwCursorPosition;
        public short wAttributes;
        public SMALL_RECT srWindow;
        public COORD dwMaximumWindowSize;
    }
 
    const int STD_OUTPUT_HANDLE = -11;
 
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr GetStdHandle(int nStdHandle);
 
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool WriteConsoleOutput(IntPtr hConsoleOutput, [In] CHAR_INFO[] lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpWriteRegion);
 
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool SetConsoleOutputCP(uint wCodePageID);
 
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
 
    // Enum for console colors
    public enum ConsoleColor
    {
        Black = 0,
        DarkBlue = 1,
        DarkGreen = 2,
        DarkCyan = 3,
        DarkRed = 4,
        DarkMagenta = 5,
        DarkYellow = 6,
        Gray = 7,
        DarkGray = 8,
        Blue = 9,
        Green = 10,
        Cyan = 11,
        Red = 12,
        Magenta = 13,
        Yellow = 14,
        White = 15
    }
 
    public static void WriteTextAtPosition(string text, int x, int y, ConsoleColor foregroundColor)
    {
        IntPtr hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
         
        // Get current console attributes
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        GetConsoleScreenBufferInfo(hConsole, out csbi);
         
        // Extract current background color (bits 4-7)
        short currentBackground = (short)(csbi.wAttributes & 0xF0);
         
        // Combine current background with new foreground color
        short attributes = (short)(currentBackground | (short)foregroundColor);
 
        // Ensure the console is using a Unicode code page (UTF-8)
        SetConsoleOutputCP(65001);
 
        CHAR_INFO[] buffer = new CHAR_INFO[text.Length];
        for (int i = 0; i < text.Length; i++)
        {
            buffer[i].UnicodeChar = text[i];
            buffer[i].Attributes = attributes; // Use combined attributes
        }
 
        COORD bufferSize = new COORD { X = (short)text.Length, Y = 1 };
        COORD bufferCoord = new COORD { X = 0, Y = 0 };
        SMALL_RECT writeRegion = new SMALL_RECT
        {
            Left = (short)x,
            Top = (short)y,
            Right = (short)(x + text.Length - 1),
            Bottom = (short)y
        };
 
        WriteConsoleOutput(hConsole, buffer, bufferSize, bufferCoord, ref writeRegion);
    }
}