Question

When making calls to any one of System.Console.BufferWidth, System.Console.CurserLeft or System.Console.WindowWidth I am greeted with a System.IO.IOException when executing my [console] app from within PsExec. All works correctly when executed within a standard command window.

Any advice is greatly appreciated.

Was it helpful?

Solution

And budda-bing budda-boom, I have discovered a solution!

As it turns out, PsExec has a native function as one of it's input properties called GetConsoleScreenBufferInfo

This native function can be called via P/Invoke and can be used to get any non-standard console screen buffer properties in the event that the .NET API cannot access the console object.

The code that I have used is as follows:

static void Main(string[] args)
{
    // ... other code and stuff
    int windowWidth;
    if (!ConsoleEx.GetConsoleWindowWidth(out windowWidth))
        windowWidth = Int32.MaxValue; // if no console, no limits
    // ... other code and stuff
}

internal static class ConsoleEx
{
    /// <summary>
    /// Contains information about a console screen buffer.
    /// </summary>
    private struct ConsoleScreenBufferInfo
    {
        /// <summary> A CoOrd structure that contains the size of the console screen buffer, in character columns and rows. </summary>
        internal CoOrd dwSize;
        /// <summary> A CoOrd structure that contains the column and row coordinates of the cursor in the console screen buffer. </summary>
        internal CoOrd dwCursorPosition;
        /// <summary> The attributes of the characters written to a screen buffer by the WriteFile and WriteConsole functions, or echoed to a screen buffer by the ReadFile and ReadConsole functions. </summary>
        internal Int16 wAttributes;
        /// <summary> A SmallRect structure that contains the console screen buffer coordinates of the upper-left and lower-right corners of the display window. </summary>
        internal SmallRect srWindow;
        /// <summary> A CoOrd structure that contains the maximum size of the console window, in character columns and rows, given the current screen buffer size and font and the screen size. </summary>
        internal CoOrd dwMaximumWindowSize;
    }

    /// <summary>
    /// Defines the coordinates of a character cell in a console screen buffer. 
    /// The origin of the coordinate system (0,0) is at the top, left cell of the buffer.
    /// </summary>
    private struct CoOrd
    {
        /// <summary> The horizontal coordinate or column value. </summary>
        internal Int16 X;
        /// <summary> The vertical coordinate or row value. </summary>
        internal Int16 Y;
    }

    /// <summary>
    /// Defines file type values for use when retrieving the type of a specified file.
    /// </summary>
    private enum FileType
    {
        /// <summary> Either the type of the specified file is unknown, or the function failed. </summary>
        Unknown,
        /// <summary> The specified file is a disk file. </summary>
        Disk,
        /// <summary> The specified file is a character file, typically an LPT device or a console. </summary>
        Char,
        /// <summary> The specified file is a socket, a named pipe, or an anonymous pipe. </summary>
        Pipe
    };

    /// <summary>
    /// Gets a value that indicates whether the error output stream has been redirected from the standard error stream.
    /// </summary>
    internal static Boolean IsErrorRedirected
    {
        get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.StdErr)); }
    }

    /// <summary>
    /// Gets a value that indicates whether input has been redirected from the standard input stream.
    /// </summary>
    internal static Boolean IsInputRedirected
    {
        get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.StdIn)); }
    }

    /// <summary>
    /// Gets a value that indicates whether output has been redirected from the standard output stream.
    /// </summary>
    internal static Boolean IsOutputRedirected
    {
        get { return FileType.Char != GetFileType(GetStdHandle(StdHandle.StdOut)); }
    }

    /// <summary>
    /// Gets the column position of the cursor within the buffer area.
    /// </summary>
    /// <param name="returnLeftIfNone">
    /// In the event that there is no console, the value passed here will be the return value
    /// </param>
    /// <returns>
    /// The current position, in columns, of the cursor
    /// </returns>
    internal static Int32 GetConsoleCursorLeft(Int32 returnLeftIfNone = 0)
    {
        if (!IsOutputRedirected) // if the output is not being redirected
            return Console.CursorLeft; // return the current cursor [left] position
        else
        { // try and get the Console Buffer details
            ConsoleScreenBufferInfo csbi;
            if (GetConsoleScreenBufferInfo(GetStdHandle(StdHandle.StdOut), out csbi)) // if the console buffer exists
                return csbi.dwCursorPosition.X; // return the cursor [left] position
        }

        return returnLeftIfNone; // no console; return the desired position in this event
    }

    /// <summary>
    /// Gets the console screen buffer window height.
    /// </summary>
    /// <param name="windowHeight">
    /// A System.Int32 property that will receive the console screen buffer height
    /// </param>
    /// <returns>
    /// Returns a System.Boolean value of true if a console screen exists and the height retrieved; else false
    /// </returns>
    internal static Boolean GetConsoleWindowHeight(out Int32 windowHeight)
    {
        int discardWidth;
        return GetConsoleWindowSize(out windowHeight, out discardWidth);
    }

    /// <summary>
    /// Gets the console screen buffer window size.
    /// </summary>
    /// <param name="windowHeight">
    /// A System.Int32 property that will receive the console screen buffer height
    /// </param>
    /// <param name="windowWidth">
    /// A System.Int32 property that will receive the console screen buffer width
    /// </param>
    /// <returns>
    /// Returns a System.Boolean value of true if a console screen exists and the information retrieved; else false
    /// </returns>
    internal static Boolean GetConsoleWindowSize(out Int32 windowHeight, out Int32 windowWidth)
    {
        windowHeight = 0;
        windowWidth = 0;

        if (!IsOutputRedirected)
        { // if the output is not being redirected
            windowHeight = Console.WindowHeight; // out the current console window height
            windowWidth = Console.WindowWidth; // out the current console window width
            return true;
        }
        else
        { // try and get the Console Buffer details
            ConsoleScreenBufferInfo csbi;
            if (GetConsoleScreenBufferInfo(GetStdHandle(StdHandle.StdOut), out csbi))
            { // if the console buffer exists
                windowHeight = csbi.dwSize.Y; // out the current console window height
                windowWidth = csbi.dwSize.X; // out the current console window width
                return true;
            }
        }

        return false; // no console
    }

    /// <summary>
    /// Gets the console screen buffer window height.
    /// </summary>
    /// <param name="windowWidth">
    /// A System.Int32 property that will receive the console screen buffer width
    /// </param>
    /// <returns>
    /// Returns a System.Boolean value of true if a console screen exists and the width retrieved; else false
    /// </returns>
    internal static Boolean GetConsoleWindowWidth(out Int32 windowWidth)
    {
        int discardHeight;
        return GetConsoleWindowSize(out discardHeight, out windowWidth);
    }

    /// <summary>
    /// Retrieves information about the specified console screen buffer.
    /// </summary>
    /// <param name="hConsoleOutput">
    /// A handle to the console screen buffer
    /// </param>
    /// <param name="lpConsoleScreenBufferInfo">
    /// A pointer to a ConsoleScreenBufferInfo structure that receives the console screen buffer information
    /// </param>
    /// <returns>
    /// If the information retrieval succeeds, the return value is nonzero; else the return value is zero
    /// </returns>
    [DllImport("kernel32.dll")]
    private static extern Boolean GetConsoleScreenBufferInfo(
        IntPtr hConsoleOutput,
        out ConsoleScreenBufferInfo lpConsoleScreenBufferInfo);

    /// <summary>
    /// Retrieves the file type of the specified file.
    /// </summary>
    /// <param name="hFile">
    /// A handle to the file
    /// </param>
    /// <returns>
    /// Returns one of the FileType enum values
    /// </returns>
    [DllImport("kernel32.dll")]
    private static extern FileType GetFileType(IntPtr hFile);

    /// <summary>
    /// Retrieves a handle to the specified standard device (standard input, standard output, or standard error).
    /// </summary>
    /// <param name="nStdHandle">
    /// The standard device
    /// </param>
    /// <returns>
    /// Returns a value that is a handle to the specified device, or a redirected handle
    /// </returns>
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetStdHandle(StdHandle nStdHandle);

    /// <summary>
    /// Defines the coordinates of the upper left and lower right corners of a rectangle.
    /// </summary>
    private struct SmallRect
    {
        /// <summary> The x-coordinate of the upper left corner of the rectangle. </summary>
        internal Int16 Left;
        /// <summary> The y-coordinate of the upper left corner of the rectangle. </summary>
        internal Int16 Top;
        /// <summary> The x-coordinate of the lower right corner of the rectangle. </summary>
        internal Int16 Right;
        /// <summary> The y-coordinate of the lower right corner of the rectangle. </summary>
        internal Int16 Bottom;
    }

    /// <summary>
    /// Defines the handle type of a standard device.
    /// </summary>
    private enum StdHandle
    {
        /// <summary> The standard input device. Initially, this is the console input buffer, CONIN$. </summary>
        StdIn = -10,
        /// <summary> The standard output device. Initially, this is the active console screen buffer, CONOUT$. </summary>
        StdOut = -11,
        /// <summary> The standard error device. Initially, this is the active console screen buffer, CONOUT$. </summary>
        StdErr = -12
    };

    /// <summary>
    /// Writes the specified string value to the standard output stream.
    /// </summary>
    /// <param name="value">
    /// The value to write
    /// </param>
    /// <param name="valueLength">
    /// The length of the last line written from value
    /// </param>
    internal static void Write(String value, out Int32 valueLength)
    {
        string[] valueArray = value.Split(new char[] { '\r', '\n' }); // split the value by either carriage returns or a line feeds
        valueLength = valueArray[valueArray.Count() - 1].Length; // out the length of the last line

        Console.Write(value); // write the value to the output stream
    }
}

... and I am happy to confirm that this works beautifully with a native console window, PsExec or when redirected to a file.

I hope someone else can make use of this :D

OTHER TIPS

Yes, that's the expected behaviour. The Console.BufferWidth and similar commands actually do expect the usual cmd window (they do little more than calling the appropriate native function with the Console handle). PsExec is actually not the windows console, so you're stuck with whatever interfaces it exposes, and whatever you can do on the input/output streams (which is why Console.WriteLine works).

However, is there a reason why you can't run cmd in PsExec, and run your application from there?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top