I have been having an issue when passing in unicode codes into my double buffer. Now I have tried stepping through the code, and I will point out where the unicode character is still correct. (Look at the draw method)
I think the issue lies somewhere in the final "print" method.
To give a quick overview of what happens:
- The buffer is created, allowing the draw function to insert characters into the buffer
- The print function will send the buffer to the console so that it is displayed
From the example I provide, I pass in the unicode character '\u2580', but it prints the ascii character '80'.
Using this link:
http://www.kreativekorp.com/charset/font.php?font=Consolas
I can correctly print out Basic Latin and Latin 1, but nothing else.
After doing further research, I do not believe the issue lies with the console code page. In addition to doing testing switching code pages (which had no effect), I can still do Console.Out.WriteLine("\u2580") and get the correct unicode character.
To provide some additional information... below is the final function that is called (as a result of the print method)
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
The documentation for this can be found here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx
Now I can guarantee that when the lpbuffer is passed in, it has a multidimensional array, of which the first value (in this case) is composed of the attribute, and the character - which IS still correct at this time, checked using debugger.
I have provided the full code to allow it to be run.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Text;
namespace DoubleBuffer
{
///<summary>
///This class allows for a double buffer in Visual C# cmd promt.
///The buffer is persistent between frames.
///</summary>
class buffer
{
private int width;
private int height;
private int windowWidth;
private int windowHeight;
private SafeFileHandle h;
private CharInfo[] buf;
private SmallRect rect;
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[StructLayout(LayoutKind.Sequential)]
public struct Coord
{
private short X;
private short Y;
public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
};
[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)]
public char UnicodeChar;
[FieldOffset(0)]
public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(0)]
public CharUnion Char;
[FieldOffset(2)]
public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect
{
private short Left;
private short Top;
private short Right;
private short Bottom;
public void setDrawCord(short l, short t)
{
Left = l;
Top = t;
}
public void setWindowSize(short r, short b)
{
Right = r;
Bottom = b;
}
}
/// <summary>
/// Consctructor class for the buffer. Pass in the width and height you want the buffer to be.
/// </summary>
/// <param name="Width"></param>
/// <param name="Height"></param>
public buffer(int Width, int Height, int wWidth, int wHeight) // Create and fill in a multideminsional list with blank spaces.
{
if (Width > wWidth || Height > wHeight)
{
throw new System.ArgumentException("The buffer width and height can not be greater than the window width and height.");
}
h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
width = Width;
height = Height;
windowWidth = wWidth;
windowHeight = wHeight;
buf = new CharInfo[width * height];
rect = new SmallRect();
rect.setDrawCord(0, 0);
rect.setWindowSize((short)windowWidth, (short)windowHeight);
Clear();
Console.OutputEncoding = System.Text.Encoding.Unicode;
}
/// <summary>
/// This method draws any text to the buffer with given color.
/// To chance the color, pass in a value above 0. (0 being black text, 15 being white text).
/// Put in the starting width and height you want the input string to be.
/// </summary>
/// <param name="str"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="attribute"></param>
public void Draw(String str, int x, int y, short attribute) //Draws the image to the buffer
{
if (x > windowWidth - 1 || y > windowHeight - 1)
{
throw new System.ArgumentOutOfRangeException();
}
if (str != null)
{
Char[] temp = str.ToCharArray(); //From testing I know the unicode character is still correct here
int tc = 0;
foreach (Char le in temp)
{
buf[(x + tc) + (y * width)].Char.UnicodeChar = le; //Height * width is to get to the correct spot (since this array is not two dimensions).
System.Console.Out.WriteLine(buf[(x + tc) + (y * width)].Char.UnicodeChar); //once again, a simple test to see if the unicode character is working. Enter debugging and you will see the value is correct.
if (attribute != 0)
buf[(x + tc) + (y * width)].Attributes = attribute;
tc++;
}
}
}
/// <summary>
/// Prints the buffer to the screen.
/// </summary>
public void Print() //Paint the image
{
if (!h.IsInvalid)
{
bool b = WriteConsoleOutput(h, buf, new Coord((short)width, (short)height), new Coord((short)0, (short)0), ref rect); //This is the point where I think it is messing up, but I am at a loss at to what is happening.
}
}
/// <summary>
/// Clears the buffer and resets all character values back to 32, and attribute values to 1.
/// </summary>
public void Clear()
{
for (int i = 0; i < buf.Length; i++)
{
buf[i].Attributes = 1;
buf[i].Char.UnicodeChar = '\u0020';
}
}
/// <summary>
/// Pass in a buffer to change the current buffer.
/// </summary>
/// <param name="b"></param>
public void setBuf(CharInfo[] b)
{
if (b == null)
{
throw new System.ArgumentNullException();
}
buf = b;
}
/// <summary>
/// Set the x and y cordnants where you wish to draw your buffered image.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public void setDrawCord(short x, short y)
{
rect.setDrawCord(x, y);
}
/// <summary>
/// Clear the designated row and make all attribues = 1.
/// </summary>
/// <param name="row"></param>
public void clearRow(int row)
{
for (int i = (row * width); i < ((row * width + width)); i++)
{
if (row > windowHeight - 1)
{
throw new System.ArgumentOutOfRangeException();
}
buf[i].Attributes = 0;
buf[i].Char.UnicodeChar = '\u0020';
}
}
/// <summary>
/// Clear the designated column and make all attribues = 1.
/// </summary>
/// <param name="col"></param>
public void clearColumn(int col)
{
if (col > windowWidth - 1)
{
throw new System.ArgumentOutOfRangeException();
}
for (int i = col; i < windowHeight * windowWidth; i += windowWidth)
{
buf[i].Attributes = 0;
buf[i].Char.UnicodeChar = '\u0020';
}
}
/// <summary>
/// This function return the character and attribute at given location.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>
/// byte character
/// byte attribute
/// </returns>
public KeyValuePair<byte, byte> getCharAt(int x, int y)
{
if (x > windowWidth || y > windowHeight)
{
throw new System.ArgumentOutOfRangeException();
}
return new KeyValuePair<byte, byte>((byte)buf[((y * width + x))].Char.UnicodeChar, (byte)buf[((y * width + x))].Attributes);
}
}
}
And the example to run is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DoubleBuffer
{
class ExampleClass
{
static int width = 80;
static int height = 30;
public static void Main(string[] args)
{
Console.CursorVisible = false;
Console.Title = "Double buffer example";
System.Console.SetBufferSize(width, height);
System.Console.SetWindowSize(width, height);
Console.Clear();
buffer myBuf = new buffer(width, height, width, height);
backgroundbuf.Draw("\u2580", 0, 0, 2);
myBuf.Print();
Console.ReadLine();
}
}
}
Let me know if I need to provide any more information! (Note: This code is directly from a code example I have on msdn. Was not aware of this bug until now, I want to make sure this is fixed!!!)