将unicode代码传递到双缓冲区时,我遇到了问题。现在我已经尝试逐步执行代码,并且我将指出unicode字符仍然正确的位置。 (看看绘制方法)
我认为这个问题存在于最终的“印刷”方法中。
快速概述发生的情况:
- 创建缓冲区,允许绘图功能将字符插入缓冲区
- 打印功能将缓冲区发送到控制台,以便显示
从我提供的示例中,我传入了unicode字符'\ u2580',但它打印出ascii字符'80'。
使用此链接:
http://www.kreativekorp.com/charset/font.php?font=Consolas
我可以正确地打印出Basic Latin和Latin 1,但没有别的。
经过进一步研究后,我不认为问题出在控制台代码页上。除了测试切换代码页(没有效果)之外,我仍然可以使用Console.Out.WriteLine(“\ u2580”)并获得正确的unicode字符。
提供一些额外信息......下面是调用的最终函数(作为打印方法的结果)
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
这方面的文件可以在这里找到:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx
现在我可以保证当传入lpbuffer时,它有一个多维数组,其中第一个值(在本例中)由属性组成,而字符 - 此时IS仍然正确,使用调试器进行检查。
我提供了完整的代码以允许它运行。
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);
}
}
}
要运行的示例是:
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();
}
}
}
如果我需要提供更多信息,请告诉我们! (注意:这段代码直接来自我在msdn上的代码示例。直到现在才知道这个bug,我想确保这个是修复的!)
答案 0 :(得分:0)
以下是帮助我解决此问题的链接!
此外,Microsoft开发人员中心还提供了一些很好的资源来解决这类问题。
OK!我找到了解决方案!它实际上非常简单。首先,我需要摆脱“CharUnion”并把它放在“CharInfo”中(不知道为什么,但它有效 - 如果你知道如何修复CharUnion,请发帖!)。另外,我需要声明CharSet = CharSet.Auto,否则它默认为ascii。 (向那些发布回答但却删除它的人大声说出来 - 你有正确的想法!)。 - 新结构CharInfo(替换struct CharUnion):
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
public struct CharInfo
{
[FieldOffset(0)]
public char UnicodeChar;
[FieldOffset(0)]
public byte bAsciiChar;
[FieldOffset(2)]
public short Attributes;
}
接下来,不是我认为它会改变任何东西,而是创建一个新文件,我现在得到当前输出缓冲区的句柄。这消除了对SafeFileHandle CreateFile的需求。
public const Int32 STD_OUTPUT_HANDLE = -11;
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern ConsoleHandle GetStdHandle(Int32 nStdHandle);
为了便于使用,对于任何未来的读者,这是我目前使用的代码。注意,可能有一些随机片段对缓冲区的工作毫无用处,我直接从我正在工作的项目中取出它。你应该明白这一点。
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
{
/*
* Copyright [2012] [Jeff R Baker]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* v 1.2.0
*/
///<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 ConsoleHandle h;
private CharInfo[] buf;
private SmallRect rect;
public const Int32 STD_OUTPUT_HANDLE = -11;
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern ConsoleHandle GetStdHandle(Int32 nStdHandle);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool WriteConsoleOutput(
ConsoleHandle 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, CharSet = CharSet.Auto)]
public struct CharInfo
{
[FieldOffset(0)]
public char UnicodeChar;
[FieldOffset(0)]
public byte bAsciiChar;
[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 short DrawCordX()
{
return Left;
}
public short DrawCordY()
{
return Top;
}
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 = GetStdHandle(STD_OUTPUT_HANDLE);
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);
Console.OutputEncoding = System.Text.Encoding.Unicode;
Clear();
}
/// <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();
int tc = 0;
foreach (Char le in temp)
{
buf[(x + tc) + (y * width)].UnicodeChar = le; //Height * width is to get to the correct spot (since this array is not two dimensions).
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);
}
}
/// <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].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].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].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))].UnicodeChar, (byte)buf[((y * width + x))].Attributes);
}
public class ConsoleHandle : SafeHandleMinusOneIsInvalid
{
public ConsoleHandle() : base(false) { }
protected override bool ReleaseHandle()
{
return true; //releasing console handle is not our business
}
}
public int X
{
get { return width; }
}
public int Y
{
get { return height; }
}
public int dX
{
get { return rect.DrawCordX(); }
}
public int dY
{
get { return rect.DrawCordY(); }
}
}
}
对此的任何更改都可以在MSDN上的“Console Double Buffer C#”中更新。