Read from location on console C#
I need to read text from a particular location in the console, say 5,5.
If I were to need to write to this location, it would simply be:
Console.SetCursorPosition(5, 5);
Console.Write("My text");
Is there any way i can read in a similar way?
Just to clarify: I don't want to stop to take an input from the user, there's a chance even that the input won't be from the user, but something previously printed out. I literally want some sort of: Console.GetCharAtLocation(5,5) or something similar.
Here is a C# code utility that can read what's currently in the Console buffer (not the window, the buffer):
Sample usage:
class Program
{
static void Main(string[] args)
{
// read 10 lines from the top of the console buffer
foreach (string line in ConsoleReader.ReadFromBuffer(0, 0, (short)Console.BufferWidth, 10))
{
Console.Write(line);
}
}
}
Utility:
public class ConsoleReader
{
public static IEnumerable<string> ReadFromBuffer(short x, short y, short width, short height)
{
IntPtr buffer = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(typeof(CHAR_INFO)));
if (buffer == null)
throw new OutOfMemoryException();
try
{
COORD coord = new COORD();
SMALL_RECT rc = new SMALL_RECT();
rc.Left = x;
rc.Top = y;
rc.Right = (short)(x + width - 1);
rc.Bottom = (short)(y + height - 1);
COORD size = new COORD();
size.X = width;
size.Y = height;
const int STD_OUTPUT_HANDLE = -11;
if (!ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE), buffer, size, coord, ref rc))
{
// 'Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr ptr = buffer;
for (int h = 0; h < height; h++)
{
StringBuilder sb = new StringBuilder();
for (int w = 0; w < width; w++)
{
CHAR_INFO ci = (CHAR_INFO)Marshal.PtrToStructure(ptr, typeof(CHAR_INFO));
char[] chars = Console.OutputEncoding.GetChars(ci.charData);
sb.Append(chars[0]);
ptr += Marshal.SizeOf(typeof(CHAR_INFO));
}
yield return sb.ToString();
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct CHAR_INFO
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] charData;
public short attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
private struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public short wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadConsoleOutput(IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(int nStdHandle);
}
A simplified demo that works in Windows 10 for reading a single character from the specified (X, Y)
position on the screen. Tested with .NET 4.7.2.
First, here's a line of code which populates the Console with a demo grid. Note that it should render in the top left corner of your screen in order for the demo to work.
static void Populate_Console()
{
Console.Clear();
Console.Write(@"
┌───────┐
1│C D E F│
2│G H I J│
3│K L M N│
4│O P Q R│
└───────┘
2 4 6 8 ".Trim());
}
It should look like this:
Now let's read some characters back. To start you'll need the native console handle for stdout. Here's the P/Invoke method for getting it from Win32:
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr GetStdHandle(int num);
Now for the cool part; this seems to be the only answer on this page so far which uses the ReadConsoleOutputCharacter
Win32 function. Although it doesn't let you get the character color attributes, this approach does save all the trouble of dealing with copy rectangles and having to use CreateConsoleScreenBuffer
to allocate screen buffers and copy between them.
There are separate Ansi and Unicode versions, and you need to call the proper one depending on the code page that's active in the Console window. I show both P/Invoke signatures here, but for simplicity, in the example I'll just continue with the Ansi version:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.Bool)] // ̲┌──────────────────^
static extern bool ReadConsoleOutputCharacterA(
IntPtr hStdout, // result of 'GetStdHandle(-11)'
out byte ch, // A̲N̲S̲I̲ character result
uint c_in, // (set to '1')
uint coord_XY, // screen location to read, X:loword, Y:hiword
out uint c_out); // (unwanted, discard)
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)] // ̲┌───────────────────^
static extern bool ReadConsoleOutputCharacterW(
IntPtr hStdout, // result of 'GetStdHandle(-11)'
out Char ch, // U̲n̲i̲c̲o̲d̲e̲ character result
uint c_in, // (set to '1')
uint coord_XY, // screen location to read, X:loword, Y:hiword
out uint c_out); // (unwanted, discard)
You may notice I've stripped down the marshaling on these to the bare minimum needed for the purposes of my example code, which is designed to only fetch one character at a time. Therefore, you will probably find that c_in
must always be 1
, due to the managed pointer declarations ‘out byte ch
’ and ‘out Char ch
’.
That's really all you need; calling the appropriate P/Invoke function as described above is mostly self-explanatory if you limit yourself to reading a single character. To show this with a trivial example, I'll finish with the cute demo program, that reads four characters back from the Console
, along a diagonal of the grid we drew above.
static void Windows_Console_Readback()
{
var stdout = GetStdHandle(-11);
for (uint coord, y = 1; y <= 4; y++)
{
coord = (5 - y) * 2; // loword <-- X coord to read
coord |= y << 16; // hiword <-- Y coord to read
if (!ReadConsoleOutputCharacterA(
stdout,
out byte chAnsi, // result: single ANSI char
1, // # of chars to read
coord, // (X,Y) screen location to read (see above)
out _)) // result: actual # of chars (unwanted)
throw new Win32Exception();
Console.Write(" " + (Char)chAnsi + " ");
}
}
And there you have it...
This functionality doesn't exist. It's theoretically possible for you to override the input and output streams on the console to keep your own copy of the console buffer that you could read from, but it would be non-trivial (and probably couldn't support all of the edge cases such as an external program hooking into your console and reading/writing to it).
Forget about it, too much trouble, you could read from buffer and get all the current console output, but that would be too much.
My suggestion is to create a ConsoleWriter delegation, you choose how, could be a class or just an static method, and this writer would keep the last line in a property so each time you would Console.WriteLine something you just call your delegation, with your implementation and at the end it calls Console.WriteLine.