Clearing the Console Screen using API






4.81/5 (8 votes)
Learn how to clear the console screen via API calls. In addition, learn some console techniques such as moving the text around the screen.
Overview
In addition to clearing the console screen, this lesson teaches you some about PInvoking, marshaling, and memory management. Also you will learn additional techniques like clearing a specific portion of the screen, and changing the cursor position. Moreover, you will dig into IL and see how System.Console.Clear()
method do it. More than that you will learn how to reverse-engineer a .NET assembly and discover the code inside.
In addition, the sample application shows how to perform I/O operations on console using Win32 API calls, and how to show/hide the console cursor. Moreover, it teaches you how to move the text around the console screen.
Table of Contents
Table of contents of this article:
- Overview
- Table of Contents
- Introduction
- GetStdHandle() Function
- A Note about Marshaling
- GetConsoleScreenBufferInfo() Function
- A Note about Memory Management
- A Look Inside the Memory
- FillConsoleOutputCharacter() Function
- SetConsoleCursorPosition() Function
- Putting Things Together
- Clearing the Console Screen
- A Look Inside the .NET Library
- References
- Code Sample (The Tiny Console Library)
- Summary
Introduction
System.Console
has been added a new method, it is the Clear()
method for clearing the console screen.
For versions before 2.0, or if you need to do it the hard-way, or even if you want to have more control over the clearing progress such as clearing a specific portion of the screen, you must dig into Win32 API and call special functions for doing so.
Clearing the console screen done through four Win32 API functions, GetStdHandle()
, GetConsoleScreenBufferInfo()
, FillConsoleOutputCharacter()
and SetConsoleCursorPosition()
. It is worth noting that all these functions located in Kernel32.dll library.
Be sure that the System.Console.Clear()
method in .NET 2.0 and descendant call these four functions -and more- initially.
GetStdHandle() Function
GetStdHandle()
first.
GetStdHandle()
simply returns the handle for the standard input, output, error device ("stream" in .NET methodology.) This function takes a single argument specifies which standard device (stream) we wish to retrieve the handle for.
The syntax of this function -in C- is as following:
HANDLE GetStdHandle(
DWORD nStdHandle
);
The nStdHandle argument can take one of three values:
- STD_INPUT_HANDLE = -10
Specifies the standard input device (stream.) - STD_OUTPUT_HANDLE = -11
Specifies the standard output device (stream.) - STD_ERROR_HANDLE = -12
Specifies the standard error device (stream.) It is always the output device (stream.)
PInvoke stands for Platform Invocations. It is the mechanism for the .NET to interoperate with its "girlfriend" Win32 API.The PInvoke method for
GetStdHandle()
is as following - code assumes that you add a using statement for the System.Runtime.InteropServices
namespace:
[DllImport("Kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle);
The DllImport
attribute is required for PInvoke methods so that you can specify the DLL which the function resides in. Also DllImport
have a very important role when you need to change the name of the method. If you need to change the name of the PInvoke method then you must set the property EntryPoint
of DllImportAttribute
to the original name of the function in the DLL. If you have not set this property, .NET will search the DLL for the method and will throw a System.EntryPointNotFoundException
exception if it is not found.
"static
" and "extern
" are modifiers required for PInvoke methods.
Because some data types do not exist in .NET, you need to find a substitute. In other words, you need to marshal unmanaged Win32 data types to the new managed .NET types. So we have marshaled HANDLE
to System.IntPtr
and DWORD
to System.Int32
.
Only a single output handle serves the console application (the same for input and error devices,) so do not close that opened handle using the CloseHandle()
function because it will result that you cannot write to the console any more, unless you open it again.
A Note about Marshaling
DWORD
, and because DWORD
is a 32-bit unsigned integer we have to marshal it as System.UInt32
. However, we marshaled it in GetStdHandle()
as System.Int32
! While unsigned integers (inculding DWORD
) do not accept negative numbers, GetStdHandle()
requires DWORD
. Actually, it is OK because negative values can be stored in DWORD
in a special way. For example, -10 stored as FFFFFFF6, -11 stored as FFFFFFF5, and so on. And that what GetStdHandle()
actually does.
In addition, you may notice that we have marshaled HANDLE
as System.IntPtr
, because IntPtr
is the best type fits to any Win32 handle. Moreover, .NET supports managed handles via the abstract SafeHandle
and CriticalHandle
classes.
It is worth mentioning that System.Runtime.InteropServices.MarshalAsAttribute
attribute can have a noticeable effect over the marshaling process.
Good resources describe the marshaling process and the Win32 data types can be found in the references section.
Managed code is the .NET code that runs inside the CLR (Common Language Runtime,) because CLR manages and controls this code. Conversely, unmanaged code is the code other than the managed code. It is legal that you write code in .NET that runs outside the CLR such as direct memory management and that code called Unsafe Code because it is error-prone and may result to an unpredictable behavior. Also, MC++ allows you to write unmanaged code together with the managed code.
GetConsoleScreenBufferInfo() Function
GetConsoleScreenBufferInfo()
. This method retrieves information about a specific console screen buffer (in other words, device.) This information is like console screen buffer size, the location and bounds of the console window, and the cursor position inside the screen.
The definition of this function is as following:
BOOL GetConsoleScreenBufferInfo(
HANDLE hConsoleOutput,
[out] SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo
);
struct CONSOLE_SCREEN_BUFFER_INFO {
COORD dwSize;
COORD dwCursorPosition;
WORD wAttributes;
SMALL_RECT srWindow;
COORD dwMaximumWindowSize;
}
struct COORD {
SHORT X;
SHORT Y;
}
struct SMALL_RECT {
SHORT Left;
SHORT Top;
SHORT Right;
SHORT Bottom;
}
Lengthy, isn't it? Yeah, that's true. Beside the GetConsoleScreenBufferInfo()
, there're three more structures, because the type of second argument of the function is the first structure, and the first structure in turn references the second and third structures.
The CONSOLE_SCREEN_BUFFER_INFO
structure represents the console information. This information is:
- The size of the console screen buffer in character rows and columns.
- The position of the cursor in the console screen in character rows and columns.
- Characteristics of the character written to the console like the fore color and back color.
- The location and bounds of the console window.
- The maximum size for the console window if we take into account the font size and the screen buffer size.
What is screen buffer and window size and what is the difference? Start the Command Prompt and right click its icon in the taskbar and go to the layout tab of the properties dialog box. Take some time playing with the values in this tab. Window size is the size of the console window -like any other window.- Screen buffer size determines the amount of text which the console screen can hold, so you always see the vertical scroll bar because the buffer height is bigger than the window height.Here's the PInvoke method and the marshaling types because .NET does not have such those three structures:
[DllImport("Kernel32.dll")]
public static extern int GetConsoleScreenBufferInfo
(IntPtr hConsoleOutput,
out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public ushort wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
Because .NET does not contain such these three structures, and we do not have any substitutes, so we have to marshal it, we have created it manually and mapped unmanaged to the managed data types. And because memory layout of such these object is very important, we added the StructLayout
attribute and specified LayoutKind.Sequential
that informs the marshaller that this structure will laid-out in memory sequentially so the first field comes before the second and so on. Also you might notice that WORD
unmanaged data type is an unsigned 32-bit integer so we marshaled it as System.UInt32
. Also SHORT
is signed 16-bit integer so it is very easy to marshal it as System.
Int16
. Refer to the references section for more information about Win32 data types.
BOOL
defined as a 32-bit signed integer, so we have marshaled the return value of the function as Int32 notBoolean
becauseBoolean
reserves 4-bits only. It will work well withBoolean
, but it is better usingInt32
. Though, if you want to use Boolean, is helpful decorating the return value with theMarshalAs
attribute.
Also, it is very efficient to use theSystem.Runtime.InteropServices.InAttribute
andSystem.Runtime.InteropServices.OutAttribute
to give a notation to the marshaller. The code sample demonstrates this.
A Note about Memory Management
System.Object
are heap-based (like most objects.) And you know also that heap objects managed by the Garbage Collector (GC) and they may remain in the memory for a long time -actually even if you called System.GC
many times.- On the other hand, stack objects are removed from the memory immediately when their scope ends. In addition, you need to know that stack is filled downwards. See figure 1.
CONSOLE_SCREEN_BUFFER_INFO
must be laid-out so dwSize
comes before dwCursorPosition
and so on. See figure 2.
CONSOLE_SCREEN_BUFFER_INFO
structure in the stack and how it will be rendered. See figure 3.
- Objects are stored downwards in the stack by the order they were created.
- Object containing objects also stored downwards by the order they were declared.
- Every object has a size. And this size determined by its containing objects -if it is not a primitive type of course.-
You can get the size of an object using two ways, theNow, we know why thesizeof
keyword, andSystem.Runtime.InteropServices.Marshal.SizeOf()
method. The second method is preferred. Do you know why? Try, think, and then answer.
StructLayout
attribute is required. And why sequential layout is required. But what if you prefer to change the order of fields? Answer is very simple. Change the LayoutKind
to Explicit
and decorate every field with FieldOffset
attribute and specify its location from the beginning of the structure. In the following code fields are reversed but they perfectly laid-out in memory using the FieldOffset
attribute:
[StructLayout(LayoutKind.Explicit)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
[FieldOffset(18)]
public COORD dwMaximumWindowSize;
[FieldOffset(10)]
public SMALL_RECT srWindow;
[FieldOffset(8)]
public ushort wAttributes;
[FieldOffset(4)]
public COORD dwCursorPosition;
[FieldOffset(0)]
public COORD dwSize;
}
If you set theAlso, from the diagrams illustrated -specially the last one- we can also write ourLayoutKind
toAuto
, you will not be able to interoperate with unmanaged code using the type. Although, if you have omitted the wholeStructLayoutAttribute
.
CONSOLE_SCREEN_BUFFER_INFO
structure to be as following and remove the other two structures:
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
public short dwSizeX;
public short dwSizeY;
public short dwCursorPositionX;
public short dwCursorPositionY;
public ushort wAttributes;
public short srWindowLeft;
public short srWindowTop;
public short srWindowRight;
public short srWindowBottom;
public short dwMaximumWindowSizeX;
public short dwMaximumWindowSizeY;
}
Sounds odd, isn't it? You can also set theIt is also possible to union two fields -or more- into one. For example, merging the two 16-bit integersLayoutKind
toExplicit
and start working.
dwSizeX
and dwSizeY
into one 32-bit integer, dwSize
. It will work very well! In addition, you can divide them in your code using System.Collections.Specialized.BitVector32
structure.
A Look Inside the Memory
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public ushort wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
Next, we will initialize the structures with some data to see how it stored in the memory.
unsafe static void Main()
{
CONSOLE_SCREEN_BUFFER_INFO info
= new CONSOLE_SCREEN_BUFFER_INFO();
info.dwSize = new COORD();
info.dwSize.X = 0xA1;
info.dwSize.Y = 0xA2;
info.dwCursorPosition = new COORD();
info.dwCursorPosition.X = 0xB1;
info.dwCursorPosition.Y = 0xB2;
info.wAttributes = 0xFFFF;
info.srWindow = new SMALL_RECT();
info.srWindow.Left = 0xC1;
info.srWindow.Top = 0xC2;
info.srWindow.Right = 0xC3;
info.srWindow.Bottom = 0xC4;
info.dwMaximumWindowSize = new COORD();
info.dwMaximumWindowSize.X = 0xD1;
info.dwMaximumWindowSize.Y = 0xD2;
uint memoryAddress =
(uint)&info;
Console.WriteLine(
"Memory Address: 0x{0:X}",
memoryAddress);
Console.WriteLine("Press any key . . .");
Console.ReadKey(true);
// You can add a break point on the last line,
// or you can use this function to break the code.
System.Diagnostics.Debugger.Break();
}
This code assumes that you enabled the unsafe code from the Build tab of the project properties. Also, it assumes that you run your code in the debugging mode by pressing F5, or by clicking Start Debugging from the Debug menu.Now, and after breaking the code, click Debug -> Windows -> Memory -> Memory 1 to open a memory window. Figure 4 shows how to open a memory window. And figure 5 shows the memory window opened. Click on the figures to enlarge.
FillConsoleOutputCharacter() Function
BOOL FillConsoleOutputCharacter(
HANDLE hConsoleOutput,
TCHAR cCharacter,
DWORD nLength,
COORD dwWriteCoord,
[out] LPDWORD lpNumberOfCharsWritten
);
This function takes four input arguments and a single output one, and it returns a value determines whether the function succeeded or failed. If the return value is non-zero (true) then the function succeeded, otherwise failed (that's for GetConsoleBufferInfo()
and SetConsoleCursorPosition()
also.)
The five arguments are:
- hConsoleOutput:
A handle to an opened console output device to write to. - cCharacter:
The character which to fill the buffer portion with. - nLength:
The number of characters to write. - dwWriteCoord:
A COORD structure defines where to begin writing (the first cell.) - lpNumberOfCharsWritten:An output parameters determines the number of characters written.
COORD
structure because we have created it earlier.
[DllImport("Kernel32.dll")]
public static extern int FillConsoleOutputCharacter
(IntPtr hConsoleOutput, char cCharacter, uint nLength,
COORD dwWriteCoord, out uint lpNumberOfCharsWritten);
Notice that the unmanaged data types DWORD andLPDOWRD
are marshaled toSystem.UInt32
. For more information about unmanaged data types see the references section.
SetConsoleCursorPosition() Function
BOOL SetConsoleCursorPosition(
HANDLE hConsoleOutput,
COORD dwCursorPosition
);
This function takes two arguments the first is a handle for an opened console output device. The second is a value specifies the new cursor position. Note that the new cursor position must be inside the console screen buffer.
The PInvoke method for this function is:
[DllImport("Kernel32.dll")]
public static extern int SetConsoleCursorPosition
(IntPtr hConsoleOutput, COORD dwCursorPosition);
Putting Things Together
public const int STD_OUTPUT_HANDLE = -11;
public const char WHITE_SPACE = ' ';
[DllImport("Kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("Kernel32.dll")]
public static extern int GetConsoleScreenBufferInfo
(IntPtr hConsoleOutput,
out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
[DllImport("Kernel32.dll")]
public static extern int FillConsoleOutputCharacter
(IntPtr hConsoleOutput, char cCharacter, uint nLength,
COORD dwWriteCoord, out uint lpNumberOfCharsWritten);
[DllImport("Kernel32.dll")]
public static extern int SetConsoleCursorPosition
(IntPtr hConsoleOutput, COORD dwCursorPosition);
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public ushort wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
Clearing the Console Screen
static void Main()
{
Console.WriteLine("Writing some text to clear.");
Console.WriteLine("Press any key to clear . . . ");
Console.ReadKey(true);
ClearConsoleScreen();
}
public static void ClearConsoleScreen()
{
// Getting the console output device handle
IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);
// Getting console screen buffer info
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(handle, out info);
// Discovering console screen buffer info
Console.WriteLine("Console Buffer Info:");
Console.WriteLine("--------------------");
Console.WriteLine("Cursor Position:");
Console.WriteLine("t{0}, {1}",
info.dwCursorPosition.X, info.dwCursorPosition.Y);
// Is this information right?
Console.WriteLine("Maximum Window Size:");
Console.WriteLine("t{0}, {1}",
info.dwMaximumWindowSize.X,
info.dwMaximumWindowSize.Y);
// Is this information right?
Console.WriteLine("Screen Buffer Size:");
Console.WriteLine("t{0}, {1}",
info.dwSize.X, info.dwSize.Y);
Console.WriteLine("Screen Buffer Bounds:");
Console.WriteLine("t{0}, {1}, {2}, {3}",
info.srWindow.Left, info.srWindow.Top,
info.srWindow.Right, info.srWindow.Bottom);
Console.WriteLine("--------------------");
// Location of which to begin clearing
COORD location = new COORD();
location.X = 0;
location.Y = 0;
// What about clearing starting from
// the second line
// location.Y = 1;
// The number of written characters
uint numChars;
FillConsoleOutputCharacter(handle, WHITE_SPACE,
(uint)(info.dwSize.X * info.dwSize.Y),
location, out numChars);
// The new cursor location
COORD cursorLocation = new COORD();
cursorLocation.X = 0;
cursorLocation.Y = 0;
SetConsoleCursorPosition(handle, cursorLocation);
}
Also we can step further and write code that clears a specific portion of the console screen buffer, try this code:
static void Main()
{
// Require the user to enter his password
AuthenticateUser();
}
public static void AuthenticateUser()
{
Console.WriteLine("Please enter your password:");
Console.Write("> "); // Two characters right
string input = Console.ReadLine();
while (input != "MyPassword")
{
COORD location = new COORD();
// The third character
location.X = 2;
// The second line
location.Y = 1;
ClearConsoleScreen(location);
input = Console.ReadLine();
}
// User authenticated
Console.WriteLine("Authenticated!");
}
public static void ClearConsoleScreen
(COORD location)
{
// Getting the console output device handle
IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);
// Getting console screen buffer info
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(handle, out info);
// The number of written characters
uint numChars;
FillConsoleOutputCharacter(handle, WHITE_SPACE,
(uint)(info.dwSize.X * info.dwSize.Y),
location, out numChars);
SetConsoleCursorPosition(handle, location);
}
A Look Inside the .NET Library
System.Console.Clear()
method. This library is the core library that every .NET application must reference, it defines the core classes and components that are essential for every application. Also, it contains classes that are required by the CLR (Common Language Runtime.)
If you are using .NET 2.0 or higher you can continue this section, otherwise, you may pass this section because the Clear()
method is new with the .NET 2.0.
MSIL, CIL, and IL all refer to the same thing the intermediate Language.
MSCORLIB stands for Microsoft Common Object Runtime Library.
Using the IL Disassembler
Now open the namespace System, then step down to the System.Console
class and double-click the Clear()
method to show the IL instructions. Take a look on how the Clear()
method do it.
Using Another Tool
If MSIL seems odd, you may try another perfect tools that can reverse-engineer your assembly to your preferred language (like C# or VB.NET for instance.) Some famous tools are Lutz Roeder's .NET Reflector and XenoCode Fox.
For me, I prefer the first tool because it is faster, simpler, and supports many features.
Of course you can reflect (reverse-engineer) an assembly and learn nice tricks from the code inside.
References
Sample Code (The Tiny Console Library)
This sample code demonstrates some of the hidden functionality of Console applications, such as moving a text around the screen and clearing a specific portion of the screen.
Summary
So you have learned how to clear the console screen using Win32 API. In addition, you learned many techniques including PInvoking, marshaling, memory management, and how to reverse-engineer a .NET assembly and get its code out. Moreover, you learned many ideas to apply when working with unmanaged code via .NET. Be sure to check the sample application - it provides you with many features that you will not find in the .NET Framework SDK (nor many other SDKs I think.) It illustrates many techniques including how to clear a specific portion of the screen and how to move a text around the screen. In addition, it shows all common console operations, like reading and writing to the screen buffer, done through the Win32 API and .NET. D