不通过 stdout 写入控制台窗口

发布于 2024-09-16 09:05:00 字数 171 浏览 3 评论 0原文

如何直接写入控制台,而不用担心 c# 中的 stdout?我正在升级一些旧程序,它的标准输出重定向到文件中(因为输出很重要),我需要以某种方式直接写入控制台,并且文本不会出现在标准输出中。有可能吗(不使用WinAPI)?

编辑:我知道可以写入 stderr,但是是否可以在控制台上设置 stderr 的光标位置?

how can I write DIRECTLY into console, without bothering with stdout in c#? I'm upgrading some old program, which has it's stdout redirected into file (because the output matters), and I need somehow to write directly to console, and that the text won't appear in stdout. Is it possible (without using WinAPI)?

EDIT: I'm aware of the possibility to write to stderr, althrough, is it possible to set cursor position for stderr on console?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

那一片橙海, 2024-09-23 09:05:00

您可以写入 Console.Error,尽管它可以重定向,但它与 stdout 是分开的。

是否可以在控制台上设置 stderr 的光标位置?

编辑:假设stderr尚未重定向,请参阅Console.CursorTopConsole.CursorLeftConsole 类上还有各种其他成员< /a> 您可能会发现有用。

要直接回答您的问题,请使用 Win32 WriteConsole 功能。据我所知,.NET 框架没有直接写入控制台窗口的方法。

You could write to Console.Error, which, although it can be redirected, is separate from stdout.

is it possible to set cursor position for stderr on console?

Edit: Assuming that stderr has not been redirected, see Console.CursorTop and Console.CursorLeft. There's various other members on the Console class that you might find useful.

To answer your question directly, use the Win32 WriteConsole function. As far as I can see, the .NET framework doesn't have methods for writing directly to the console window.

我是有多爱你 2024-09-23 09:05:00

如果我运行你的程序并重定向它的 StandardOutput 和 StandardError,当没有控制台时你期望你的写入会发生什么?

出于这个原因,答案可能是“你不能”(除非使用疯狂的黑客技术,这可能涉及你说你不想使用的 Windows API)。

将控制台窗口视为一个 UI 元素,它允许用户查看程序的 stdout/stderr(并提供 stdin)。它实际上并不存在任何其他目的。

If I run your program and redirect its StandardOutput and StandardError, what do you expect to happen to your writes when there is no console?

For this reason, the answer is likely “you can’t” (except perhaps by using crazy hacks, which probably involve Windows API which you said you didn’t want to use).

Think of the console window as nothing more than a UI element that allows the user to view the stdout/stderr of your program (and to provide stdin). It doesn’t really exist for any other purpose.

痕至 2024-09-23 09:05:00

我实际上最终实现了低级别 WriteConsoleOutput 不久前,作为我编写的基于终端的俄罗斯方块实现的一部分,因为使用 Console.BackgroundColorConsole.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();            
    }
}

I actually ended up implementing the low level WriteConsoleOutput some time ago as part of a terminal based Tetris implementation I wrote, since doing coloured console output with Console.BackgroundColor and Console.Write, is far too slow for full screen refreshes. Doing the raw buffer output is much faster.

Note, this writes text to a location on the screen (eg 5,10), but doesn't update or keep track of the cursor position on it's own - writing with this method will update the text buffer and display the output, but the cursor won't move. You'll need to move and keep track of the console cursor manually with some other method, which shouldn't be too difficult.

Here's my code:

Main interop class:

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;
    }
}

Character attributes:

[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
}

Test code:

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();            
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文