using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Magic.Samples.ConsoleSample
{
/// <summary>
/// Console horizontal text alignment.
/// </summary>
public enum ConsoleTextAlignment
{
/// <summary>
/// Text is left aligned.
/// </summary>
Left,
/// <summary>
/// Text is right aligned.
/// </summary>
Right,
/// <summary>
/// Text is centered.
/// </summary>
Center
}
/// <summary>
/// Console standard devices.
/// </summary>
public enum ConsoleStandardDevice
{
/// <summary>
/// The input device.
/// </summary>
Input = SafeNativeMethods.STD_INPUT_HANDLE,
/// <summary>
/// The output device.
/// </summary>
Output = SafeNativeMethods.STD_OUTPUT_HANDLE,
/// <summary>
/// The error device (usually the output device.)
/// </summary>
Error = SafeNativeMethods.STD_ERROR_HANDLE
}
/// <summary>
/// Extension methods for the console.
/// </summary>
public static class ConsoleExtensions
{
/// <summary>
/// Clears the screen buffer.
/// </summary>
public static void ClearScreen()
{
// Clearing the screen starting from the first cell
COORD location = new COORD();
location.X = 0;
location.Y = 0;
ClearScreen(location);
}
/// <summary>
/// Clears the screen buffer starting from a specific location.
/// </summary>
/// <param name="location">The location of which to start clearing the screen buffer.</param>
public static void ClearScreen(COORD location)
{
// Clearing the screen starting from the specified location
// Setting the character to a white space means clearing it
// Setting the count to 0 means clearing to the end, not a specific length
FillConsoleBuffer(location, 0, SafeNativeMethods.WHITE_SPACE);
}
/// <summary>
/// Fills a specific cells with a specific character starting from a specific location.
/// </summary>
/// <param name="location">The location of which to start filling.</param>
/// <param name="count">The number of cells starting from the location to fill.</param>
/// <param name="character">The character to fill with.</param>
public static void FillConsoleBuffer(COORD location, uint count, char character)
{
// Getting the console output device handle
IntPtr handle = GetStandardDevice(ConsoleStandardDevice.Output);
uint length;
// If count equals 0 then user require clearing all the screen
if (count == 0)
{
// Getting console screen buffer info
CONSOLE_SCREEN_BUFFER_INFO info = GetBufferInfo(ConsoleStandardDevice.Output);
// All the screen
length = (uint)(info.dwSize.X * info.dwSize.Y);
}
else
length = count;
// The number of written characters
uint numChars;
// Calling the Win32 API function
SafeNativeMethods.FillConsoleOutputCharacter(handle, character, length, location, out numChars);
// Setting the console cursor position
SetCursorPosition(location);
}
/// <summary>
/// Rettrieves a handle for a specific device.
/// </summary>
/// <param name="device">The device of which to retrieve the handle for.</param>
/// <returns>The handle for the specified device.</returns>
public static IntPtr GetStandardDevice(ConsoleStandardDevice device)
{
// Calling the Win32 API function
return SafeNativeMethods.GetStdHandle((int)device);
}
/// <summary>
/// Writes an empty line to the console buffer on the current position of the cursor.
/// </summary>
public static void WriteLine()
{
WriteLine(string.Empty);
}
/// <summary>
/// Writes specific text followed by a line terminator to the console buffer on the current position of the cursor.
/// </summary>
/// <param name="txt">The text to write.</param>
public static void WriteLine(string txt)
{
WriteLine(txt, ConsoleTextAlignment.Left);
}
/// <summary>
/// Writes specific text followed by a line terminator to the console buffer on the current position of the cursor with the specified line alignemnt.
/// </summary>
/// <param name="txt">The text to write.</param>
/// <param name="alignment">The horizontal alignment of the text.</param>
public static void WriteLine(string txt, ConsoleTextAlignment alignment)
{
Write(txt + Environment.NewLine, alignment);
}
/// <summary>
/// Writes specific text to the console buffer on the current position of the cursor.
/// </summary>
/// <param name="txt">The text to write.</param>
public static void Write(string txt)
{
Write(txt, ConsoleTextAlignment.Left);
}
/// <summary>
/// Writes specific text to the console buffer on the current position of the cursor with the specified line alignment.
/// </summary>
/// <param name="txt">The text to write.</param>
/// <param name="alignment">The horizontal alignment of the text.</param>
public static void Write(string txt, ConsoleTextAlignment alignment)
{
if (alignment == ConsoleTextAlignment.Left)
InternalWrite(txt);
else
{
// Determining the location of which to begin writing
CONSOLE_SCREEN_BUFFER_INFO info = GetBufferInfo(ConsoleStandardDevice.Output);
COORD pos = new COORD();
if (alignment == ConsoleTextAlignment.Right)
pos.X = (short)(info.dwSize.X - txt.Length);
else // Center
pos.X = (short)((info.dwSize.X - txt.Length) / 2);
pos.Y = info.dwCursorPosition.Y;
// Changing the cursor position
SetCursorPosition(pos);
// Now writing on the current position
InternalWrite(txt);
}
}
/// <summary>
/// Writing a specific text to the console output buffer starting from the current cursor position.
/// </summary>
/// <param name="txt">The text to write.</param>
private static void InternalWrite(string txt)
{
// Required for the WriteConsole() function
// It is the number of characters written
uint count;
// Getting the output handle
IntPtr handle = GetStandardDevice(ConsoleStandardDevice.Output);
// Calling the Win32 API function
SafeNativeMethods.WriteConsole(handle, txt, (uint)txt.Length, out count, 0);
}
/// <summary>
/// Shows or hides the cursor.
/// </summary>
/// <param name="show">Specifies whether to show the cursor or not.</param>
public static void ShowCursor(bool show)
{
CONSOLE_CURSOR_INFO info;
// Getting the output device
IntPtr handle = GetStandardDevice(ConsoleStandardDevice.Output);
// Getting the cursor info
SafeNativeMethods.GetConsoleCursorInfo(handle, out info);
// Determining the visibility of the cursor
info.bVisible = show;
// Setting the cursor info
SafeNativeMethods.SetConsoleCursorInfo(handle, ref info);
}
/// <summary>
/// Read the next line from the input device.
/// </summary>
/// <returns></returns>
public static string ReadText()
{
// The buffer
// Maximum number of characters is 256
StringBuilder buffer = new StringBuilder(256);
// Required for the function call
uint count;
// Getting the input device that's used for receiving user input
SafeNativeMethods.ReadConsole(GetStandardDevice(ConsoleStandardDevice.Input), buffer,
(uint)buffer.Capacity, out count, 0);
// Returning the user input cutting up the line terminator
return buffer.ToString().Substring(0, (int)(count - Environment.NewLine.Length));
}
/// <summary>
/// Retrieves the buffer info of the specified device.
/// </summary>
/// <param name="device">The device of which to retrieve its information.</param>
/// <returns>The buffer info of the specified device.</returns>
public static CONSOLE_SCREEN_BUFFER_INFO GetBufferInfo(ConsoleStandardDevice device)
{
// Returning the handle for the selected device
IntPtr handle = GetStandardDevice(device);
// Getting console screen buffer information
CONSOLE_SCREEN_BUFFER_INFO info;
SafeNativeMethods.GetConsoleScreenBufferInfo(handle, out info);
return info;
}
/// <summary>
/// Sets the cursor position in the buffer.
/// </summary>
/// <param name="pos">The coordinates of which to move the cursor to.</param>
public static void SetCursorPosition(COORD pos)
{
// Getting the console output device handle
IntPtr handle = SafeNativeMethods.GetStdHandle(SafeNativeMethods.STD_OUTPUT_HANDLE);
// Moving the cursor to the new location
SafeNativeMethods.SetConsoleCursorPosition(handle, pos);
}
/// <summary>
/// Writes the buffer information to the screen.
/// </summary>
/// <param name="info">The information of which to write.</param>
public static void WriteBufferInfo(CONSOLE_SCREEN_BUFFER_INFO info)
{
// Discovering console screen buffer information
WriteLine("Console Buffer Info:");
WriteLine("--------------------");
WriteLine("Cursor Position:");
WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "\t{0}, {1}",
info.dwCursorPosition.X, info.dwCursorPosition.Y));
// Is this information right?
WriteLine("Maximum Window Size:");
WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "\t{0}, {1}",
info.dwMaximumWindowSize.X,
info.dwMaximumWindowSize.Y));
// Is this information right?
WriteLine("Screen Buffer Size:");
WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "\t{0}, {1}",
info.dwSize.X, info.dwSize.Y));
WriteLine("Screen Buffer Bounds:");
WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "\t{0}, {1}, {2}, {3}",
info.srWindow.Left, info.srWindow.Top,
info.srWindow.Right, info.srWindow.Bottom));
WriteLine("--------------------");
}
/// <summary>
/// Writes the specific text followed by a line terminator to the left and moves it to the far right.
/// </summary>
/// <param name="txt">The text of which to write.</param>
public static void MoveText(string txt)
{
// First, writing the text
WriteLine(txt);
// Getting the handle for the output device
IntPtr handle = GetStandardDevice(ConsoleStandardDevice.Output);
// Getting the screen buffer info for the output device
CONSOLE_SCREEN_BUFFER_INFO screenInfo = GetBufferInfo(ConsoleStandardDevice.Output);
// Selecting the text to be moved
SMALL_RECT rect = new SMALL_RECT();
rect.Left = 0; // The 1st cell
rect.Top = (short)(screenInfo.dwCursorPosition.Y - 1); // The row of the text
rect.Bottom = (short)(rect.Top); // Only a single line
while (true)
{
// Moving to the right
rect.Right = (short)(rect.Left + (txt.Length - 1));
// Do not move it nore if we are in the far right of the buffer
if (rect.Right == (screenInfo.dwSize.X - 1))
break;
// The character to fill the empty cells created after the move with
CHAR_INFO charInfo = new CHAR_INFO();
charInfo.Char = SafeNativeMethods.WHITE_SPACE; // For clearing the cells
// Calling the API function
SafeNativeMethods.ScrollConsoleScreenBuffer(handle, ref rect, IntPtr.Zero, new COORD() { X = (short)(rect.Left + 1), Y = rect.Top }, ref charInfo);
// Blocking the thread for the user to see the effect
System.Threading.Thread.Sleep(100);
// Moving the rectangle
rect.Left++;
}
}
}
}