Я фактически закончил реализацию низкого уровня WriteConsoleOutput некоторое время назад как часть реализации Tetris, основанной на терминалах, которую я написал, так как вывод цветной консоли с Console.BackgroundColor
и Console.Write
слишком медленный для полного обновления экрана. Вывод необработанного буфера намного быстрее.
Обратите внимание, что это записывает текст в место на экране (например, 5,10), но не обновляет и не отслеживает положение курсора самостоятельно - при записи с помощью этого метода обновляется текстовый буфер и отображается вывод , но курсор не будет двигаться. Вам нужно будет вручную перемещать и отслеживать курсор консоли с помощью другого метода, который не должен быть слишком сложным.
Вот мой код:
Основной класс взаимодействия:
public static class LowLevelConsole {
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private 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)]
private static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[StructLayout(LayoutKind.Sequential)]
public struct Coord {
public short X;
public 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 ushort Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect {
public short Left;
public short Top;
public short Right;
public short Bottom;
}
[STAThread]
public static void Write(string line, CharacterAttribute attribute, short xLoc, short yLoc) {
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
short writeHeight = 1;
short writeWidth = (short)line.Length;
if (!h.IsInvalid) {
CharInfo[] buf = new CharInfo[writeWidth * writeHeight];
SmallRect rect = new SmallRect() { Left = xLoc, Top = yLoc, Right = (short)(writeWidth + xLoc), Bottom = (short)(writeHeight + yLoc) };
for (int i = 0; i < writeWidth; i++) {
buf[i].Attributes = (ushort)attribute;
buf[i].Char.UnicodeChar = line[i];
}
bool b = WriteConsoleOutput(h, buf, new Coord() { X = writeWidth, Y = writeHeight }, new Coord() { X = 0, Y = 0 }, ref rect);
}
}
[STAThread]
public static bool WriteBuffer(CharInfo[,] buffer) { // returns true of success
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (!h.IsInvalid) {
short BufferWidth = (short)buffer.GetLength(0);
short BufferHeight = (short)buffer.GetLength(1);
CharInfo[] buf = new CharInfo[BufferWidth * BufferHeight];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = BufferWidth, Bottom = BufferHeight };
for (int y = 0; y < BufferHeight; y++) {
for (int x = 0; x < BufferWidth; x++) {
buf[y * BufferWidth + x] = buffer[x, y];
}
}
return WriteConsoleOutput(h, buf, new Coord() { X = BufferWidth, Y = BufferHeight }, new Coord() { X = 0, Y = 0 }, ref rect);
}
return false;
}
}
Атрибуты персонажа :
[Flags]
public enum CharacterAttribute : ushort {
FOREGROUND_BLUE = 0x0001,
FOREGROUND_GREEN = 0x0002,
FOREGROUND_RED = 0x0004,
FOREGROUND_INTENSITY = 0x0008,
BACKGROUND_BLUE = 0x0010,
BACKGROUND_GREEN = 0x0020,
BACKGROUND_RED = 0x0040,
BACKGROUND_INTENSITY = 0x0080,
COMMON_LVB_LEADING_BYTE = 0x0100,
COMMON_LVB_TRAILING_BYTE = 0x0200,
COMMON_LVB_GRID_HORIZONTAL = 0x0400,
COMMON_LVB_GRID_LVERTICAL = 0x0800,
COMMON_LVB_GRID_RVERTICAL = 0x1000,
COMMON_LVB_REVERSE_VIDEO = 0x4000,
COMMON_LVB_UNDERSCORE = 0x8000
}
Тестовый код:
class Program {
static void Main(string[] args) {
// write to location 0,0
LowLevelConsole.Write("Some test text", CharacterAttribute.BACKGROUND_BLUE | CharacterAttribute.FOREGROUND_RED, 0, 0);
// write to location 5,10
LowLevelConsole.Write("another test at a different location",
CharacterAttribute.FOREGROUND_GREEN | CharacterAttribute.FOREGROUND_BLUE,
5, 10);
Console.ReadLine();
}
}