开发者

Writing to console window not via stdout

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 matt开发者_运维知识库ers), 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?


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.


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.


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();            
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜