2013-12-10 23 views
7

Quando si effettuano chiamate a uno qualsiasi dei System.Console.BufferWidth, System.Console.CurserLeft o System.Console.WindowWidth vengo accolto con un System.IO.IOException durante l'esecuzione di mio [console] app dall'interno PsExec. Tutto funziona correttamente quando viene eseguito all'interno di una finestra di comando standard.System.IO.IOException quando si chiama System.Console.WindowWidth

Qualsiasi consiglio è molto apprezzato.

+0

Puoi provare la traccia dello stack dell'errore, per favore? – PCG

+0

[Questa risposta] (http://stackoverflow.com/a/3453272/17034) dovrebbe fornire sollievo. Non rovinare la finestra della console quando IsOuputRedirected restituisce true. –

+0

@P_G 'Eccezione non gestita: System.IO.IOException: l'handle non è valido. a System.IO .__ Error.WinIOError (Int32 errorCode, String maybeFullPath) a System.Console.GetBufferInfo (booleano throwOnNoConsole, booleano e riuscito) a System.Console.get_WindowWidth() a EnumInstalledApp.Program.ShowConsoleHelp (Int32 exitCode, String invalidArg) presso EnumInstalledApp.Program.Main (String [] args) ' – SmithPlatts

risposta

11

E budda-bing budda-boom, ho scoperto una soluzione!

Come si è visto, PsExec ha una funzione nativa come una delle sue proprietà di immissione chiamati GetConsoleScreenBufferInfo

Questa funzione nativa può essere chiamata tramite P/Invoke e può essere utilizzato per ottenere le proprietà tampone schermo console non standard nel caso in cui l'API .NET non possa accedere all'oggetto console.

Il codice che ho usato è il seguente:

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

... e sono felice di confermare che questo funziona perfettamente con una finestra nativa della console, PsExec o quando reindirizzato a un file.

Spero che qualcun altro possa fare uso di questo: D

1

Sì, questo è il comportamento previsto. Il Console.BufferWidth e comandi simili in realtà si aspettano la solita finestra cmd (fanno poco più che chiamare la funzione nativa appropriata con l'handle della console). PsExec non è in realtà la console di Windows, quindi sei bloccato con le interfacce che espone e qualsiasi cosa tu possa fare sui flussi di input/output (ecco perché funziona Console.WriteLine).

Tuttavia, c'è un motivo per cui non è possibile eseguire cmd in PsExec ed eseguire l'applicazione da lì?

+0

Questa è la risposta che mi aspettavo ... e temevo; grazie. Purtroppo sto eseguendo la mia app tramite un'istanza di 'cmd' eseguita in' PsExec' e sempre lo stesso risultato. Esplorerò quali metodi e proprietà native sono disponibili da 'PsExec'; se del caso ... – SmithPlatts

Problemi correlati