Colour Console Application

  • Thread starter Thread starter Publicjoe
  • Start date Start date
P

Publicjoe

I am working on a little app which uses colour in the console window. I have
created a class to extend the console functionality but the ClearScreen
method does not work correctly. I am enclosing a complete project
to show what happens. If anybody has an idea of how to fix
this, please let me know.

Yes I am aware that this is all in .Net 2.

Thanks in advance.

Publicjoe

----- Main.cs -----
using System;
using Publicjoe.Console;

namespace ColourTest
{
class MainClass
{
public static void Main(string[] args)
{
ConsoleExt TextChange = new ConsoleExt();
Console.WriteLine("Original Colors");
Console.WriteLine("Press Enter to Begin");
Console.ReadLine();
TextChange.TextColor((int)ConsoleExt.Foreground.Green +
(int)ConsoleExt.Foreground.Intensity);
Console.WriteLine("THIS TEXT IS GREEN");
Console.WriteLine("Press Enter to change colors again");
Console.ReadLine();
TextChange.TextColor((int)ConsoleExt.Foreground.Red +
(int)ConsoleExt.Foreground.Blue +
(int)ConsoleExt.Foreground.Intensity);
Console.WriteLine("NOW THE TEXT IS PURPLE");
Console.WriteLine("Press Enter to change colors again");
Console.ReadLine();
TextChange.TextColor((int)ConsoleExt.Foreground.Blue +
(int)ConsoleExt.Foreground.Intensity +
(int)ConsoleExt.Background.Green +
(int)ConsoleExt.Background.Intensity);
Console.WriteLine("NOW THE TEXT IS BLUE AND BACKGROUND OF IT IS
GREEN");
Console.WriteLine("Press Enter change everything back to normal");
Console.ReadLine();
TextChange.ResetColor();
TextChange.ClearScreen();
Console.WriteLine("Back to Original Colors");
Console.WriteLine("Press Enter to Terminate");
Console.ReadLine();
}
}
}
----- End -----

----- ConsoleExt.cs ----
using System;
using System.Runtime.InteropServices;

namespace Publicjoe.Console
{
public class ConsoleExt
{
private const int STD_INPUT_HANDLE = -10;
private const int STD_OUTPUT_HANDLE = -11;
private const int STD_ERROR_HANDLE = -12;

private const ushort KEY_EVENT = 1;
private const ushort MOUSE_EVENT = 2;
private const ushort WINDOW_BUFFER_SIZE_EVENT = 4;
private const ushort MENU_EVENT = 8;
private const ushort FOCUS_EVENT = 16;
private const byte EMPTY = 32;

private IntPtr hConsoleHandle;
private COORD ConsoleOutputLocation;
private CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
private int OriginalColors;

[StructLayout(LayoutKind.Sequential)]
struct COORD
{
public short x;
public short y;
}

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

[StructLayout(LayoutKind.Sequential)]
struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public int wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}

// Key press event information
struct KEY_EVENT_RECORD
{
public bool bKeyDown;
public ushort wRepeatCount;
public ushort wVirtualKeyCode;
public ushort wVirtualScanCode;
public byte AsciiChar; // we'll only process ASCII
public uint dwControlKeyState;
}

// Information about the console input
struct INPUT_RECORD
{
public ushort EventType;
public KEY_EVENT_RECORD KeyEvent;
};

[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32.dll")]
private static extern bool FillConsoleOutputCharacter(IntPtr
hConsoleHandle, byte cCharacter, int nLength, COORD dwWriteCoord, ref int
lpNumberOfCharsWritten);

[DllImport("kernel32.dll")]
private static extern bool GetConsoleScreenBufferInfo(IntPtr
hConsoleHandle, ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);

[DllImport("kernel32.dll")]
private static extern bool SetConsoleCursorPosition(IntPtr
hConsoleOutput, COORD dwCursorPosition);

[DllImport("kernel32.dll")]
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint
mode);

[DllImport("kernel32.dll")]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out
uint mode);

[DllImport("kernel32.dll")]
private static extern bool ReadConsoleInputA(IntPtr hConsoleHandle, out
INPUT_RECORD rec, uint len, out uint c);

[DllImport("kernel32.dll")]
private static extern bool SetConsoleTextAttribute(IntPtr
hConsoleOutput, int Attributes );

public enum Foreground
{
Blue = 0x00000001,
Green = 0x00000002,
Red = 0x00000004,
Intensity = 0x00000008
}

public enum Background
{
Blue = 0x00000010,
Green = 0x00000020,
Red = 0x00000040,
Intensity = 0x00000080
}

public ConsoleExt()
{
ConsoleInfo = new CONSOLE_SCREEN_BUFFER_INFO();
ConsoleOutputLocation = new COORD();
hConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsoleHandle, ref ConsoleInfo);
OriginalColors = ConsoleInfo.wAttributes;
}

/// <summary>
/// Clear the Console window
/// </summary>
public void ClearScreen()
{
int hWrittenChars = 0;
ConsoleOutputLocation.x = 0;
ConsoleOutputLocation.y = 0;
FillConsoleOutputCharacter(hConsoleHandle, EMPTY, ConsoleInfo.dwSize.x
* ConsoleInfo.dwSize.y, ConsoleOutputLocation, ref hWrittenChars);
SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
}

/// <summary>
/// Get a single character from the console
/// </summary>
/// <returns>Key that has been pressed</returns>
public int ReadKey()
{
int ch = 0;
IntPtr stdin = GetStdHandle(STD_INPUT_HANDLE);

uint oldstate;

// Save the existing mode
GetConsoleMode(stdin, out oldstate);

// Set the mode to character input
SetConsoleMode(stdin, 0);

// Read the console input until we get a single character
while(true)
{
INPUT_RECORD rec;
rec.EventType = 0;
rec.KeyEvent.bKeyDown = false;
rec.KeyEvent.wRepeatCount = rec.KeyEvent.wVirtualKeyCode =
rec.KeyEvent.wVirtualScanCode = 0;
rec.KeyEvent.AsciiChar = 0;
rec.KeyEvent.dwControlKeyState = 0;

uint NumRead;

// Read the next console event
if (!ReadConsoleInputA(stdin, out rec, 1, out NumRead))
{
// A problem has occurred, treat it as EOF
ch = -1;
break;
}

// Check that a button was pressed
if (((rec.EventType & KEY_EVENT) > 0) && rec.KeyEvent.bKeyDown)
{
if ((ch = rec.KeyEvent.AsciiChar) > 0)
{
break;
}
}
}

// Restore the old console mode
SetConsoleMode(stdin, oldstate);

// Return the character
return ch;
}

/// <summary>
/// Set the text color
/// </summary>
/// <param name="color"></param>
public void TextColor(int color)
{
SetConsoleTextAttribute(hConsoleHandle, color);
}

/// <summary>
/// Reset the text color back to normal
/// </summary>
public void ResetColor()
{
SetConsoleTextAttribute(hConsoleHandle, OriginalColors);
}
}
}
----- End -----
 
Could you send a small but complete program that demonstrates what is wrong?
Where exactly you get problems? What exception do you get and what do you do
before you get it?
 
Um, he did send us a complete code sample.
I can see the green background isn't cleared. Looking into it.
 
Well, the documentations for FillConsoleOutputCharacter remarks that attributes will not be changed. You can see this if you use a character instead of EMPTY. The green and blue foreground color is retained as well.

To overcome this I suggest you write something to the entire screen using the original color.
For instance, in your ClearScreen method you could do

public void ClearScreen()
{
ConsoleOutputLocation.x = 0;
ConsoleOutputLocation.y = 0;
int bufferLength = ConsoleInfo.dwSize.x * ConsoleInfo.dwSize.y;

SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
Console.Write(new char[bufferLength]);
SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
}
 
Hi Morten.
That did not work as I do not have a reference to the console window.
However I spotted an old C++ article on msdn at
http://msdn.microsoft.com/library/en-us/dllproc/base/clearing_the_screen.asp
so I just implemented FillConsoleOutputAttribute to give

public void ClearScreen()
{
int hWrittenChars = 0;
ConsoleOutputLocation.x = 0;
ConsoleOutputLocation.y = 0;
FillConsoleOutputCharacter(hConsoleHandle, EMPTY, ConsoleInfo.dwSize.x *
ConsoleInfo.dwSize.y, ConsoleOutputLocation, ref hWrittenChars);
FillConsoleOutputAttribute(hConsoleHandle, 0, ConsoleInfo.dwSize.x *
ConsoleInfo.dwSize.y, ConsoleOutputLocation, ref hWrittenChars);
SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
}

This works a treat.

Thanks for trying.

Publicjoe



Morten Wennevik said:
Well, the documentations for FillConsoleOutputCharacter remarks that
attributes will not be changed. You can see this if you use a character
instead of EMPTY. The green and blue foreground color is retained as well.
To overcome this I suggest you write something to the entire screen using the original color.
For instance, in your ClearScreen method you could do

public void ClearScreen()
{
ConsoleOutputLocation.x = 0;
ConsoleOutputLocation.y = 0;
int bufferLength = ConsoleInfo.dwSize.x * ConsoleInfo.dwSize.y;

SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
Console.Write(new char[bufferLength]);
SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
}
 
Thank you for posting the solution. Since FillConsoleOutputCharacter does not reset attributes calling FillConsoleOutputAttribute would indeed be the logical thing to call :)

(Note to self: study the documents closer before giving an answer!)


Hi Morten.
That did not work as I do not have a reference to the console window.
However I spotted an old C++ article on msdn at
http://msdn.microsoft.com/library/en-us/dllproc/base/clearing_the_screen.asp
so I just implemented FillConsoleOutputAttribute to give

public void ClearScreen()
{
int hWrittenChars = 0;
ConsoleOutputLocation.x = 0;
ConsoleOutputLocation.y = 0;
FillConsoleOutputCharacter(hConsoleHandle, EMPTY, ConsoleInfo.dwSize.x *
ConsoleInfo.dwSize.y, ConsoleOutputLocation, ref hWrittenChars);
FillConsoleOutputAttribute(hConsoleHandle, 0, ConsoleInfo.dwSize.x *
ConsoleInfo.dwSize.y, ConsoleOutputLocation, ref hWrittenChars);
SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
}

This works a treat.

Thanks for trying.

Publicjoe



Morten Wennevik said:
Well, the documentations for FillConsoleOutputCharacter remarks that
attributes will not be changed. You can see this if you use a character
instead of EMPTY. The green and blue foreground color is retained as well.
To overcome this I suggest you write something to the entire screen using the original color.
For instance, in your ClearScreen method you could do

public void ClearScreen()
{
ConsoleOutputLocation.x = 0;
ConsoleOutputLocation.y = 0;
int bufferLength = ConsoleInfo.dwSize.x * ConsoleInfo.dwSize.y;

SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
Console.Write(new char[bufferLength]);
SetConsoleCursorPosition(hConsoleHandle, ConsoleOutputLocation);
}
 
Back
Top