using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; namespace RobvanderWoude { class PrintScreen { static string progver = "1.04"; static int Main( string[] args ) { try { string outputfile = String.Empty; bool capturetext = false; bool generatename = false; bool overwrite = false; bool trimoutput = false; bool typeset = false; bool usestandardout = false; ImageFormat imagetype = null; #region Command Line parsing if ( args.Length == 0 || args.Length > 3 ) { return ShowHelp( ); } foreach ( string arg in args ) { switch ( arg.ToUpper( ).Substring( 0, 2 ) ) { case "/?": return ShowHelp( ); case "/C": if ( trimoutput ) { return ShowHelp( "Duplicate command line switch /C" ); } trimoutput = true; break; case "/O": if ( overwrite ) { return ShowHelp( "Duplicate command line switch /O" ); } overwrite = true; break; case "/S": if ( usestandardout ) { return ShowHelp( "Duplicate command line switch /S" ); } usestandardout = true; break; case "/T": if ( typeset ) { return ShowHelp( "Duplicate command line switch /T" ); } switch ( arg.ToUpper( ).Substring( 3 ) ) { case "BMP": imagetype = ImageFormat.Bmp; break; case "GIF": imagetype = ImageFormat.Gif; break; case "JPG": case "JPEG": imagetype = ImageFormat.Jpeg; break; case "PNG": imagetype = ImageFormat.Png; break; case "TIF": case "TIFF": imagetype = ImageFormat.Tiff; break; case "TXT": case "TEXT": capturetext = true; break; default: return ShowHelp( "Invalid file format \"{0}\"", arg.Substring( 3 ) ); } typeset = true; break; case "/U": if ( generatename ) { return ShowHelp( "Duplicate command line switch /U" ); } if ( !String.IsNullOrEmpty( outputfile ) ) { return ShowHelp( "Do not specify a file name when using /U to generate one" ); } generatename = true; break; default: if ( arg[0] == '/' ) { return ShowHelp( "Invalid command line switch \"{0}\"", arg ); } else { if ( !String.IsNullOrEmpty( outputfile ) ) { return ShowHelp( "Duplicate output file name arguments:\n\t\"{0}\"\n\t\"{1}\"", outputfile, Path.GetFullPath( arg ) ); } if ( generatename ) { return ShowHelp( "Do not specify a file name when using /U to generate one" ); } try { outputfile = Path.GetFullPath( arg ); } catch ( Exception ) { return ShowHelp( "Invalid output file \"{0}\"", arg ); } } break; } } if ( usestandardout ) { if ( imagetype != null || overwrite || !String.IsNullOrEmpty( outputfile ) ) { return ShowHelp( "Except /C no other command line switch can be used with /S" ); } capturetext = true; } else if ( generatename ) { // We are assuming that multiple instances, each using /U, will never "collide" } else { if ( String.IsNullOrEmpty( outputfile ) ) { return ShowHelp( "No output file specified." ); } // Check if directory exists if ( !Directory.Exists( Directory.GetParent( outputfile ).FullName ) ) { return ShowHelp( "Invalid path for output file \"{0}\"", outputfile ); } // Check if file exists, and if so, if it can be overwritten if ( File.Exists( outputfile ) ) { if ( overwrite ) { File.Delete( outputfile ); } else { return ShowHelp( "Output file exists.\n\tUse /O to overwrite existing files." ); } } } if ( !generatename && !usestandardout && imagetype == null && capturetext == false ) { string ext = Path.GetExtension( outputfile ).ToUpper( ); switch ( ext ) { case "": return ShowHelp( "No extension for output file.\n\tUse /T to specify the output file type.", outputfile ); case ".BMP": imagetype = ImageFormat.Bmp; break; case ".GIF": imagetype = ImageFormat.Gif; break; case ".JPG": case ".JPEG": imagetype = ImageFormat.Jpeg; break; case ".PNG": imagetype = ImageFormat.Png; break; case ".TIF": case ".TIFF": imagetype = ImageFormat.Tiff; break; case ".TXT": capturetext = true; break; default: return ShowHelp( "Unrecognized extension \"{0}\" for output file.\n\tUse /T to specify the output file type.", ext ); } } if ( generatename ) { if ( capturetext ) { outputfile = GenerateUniqueFileName( "txt" ); } else { if ( imagetype == null ) { return ShowHelp( "/U switch requires /T switch as well" ); } outputfile = GenerateUniqueFileName( imagetype.ToString( ) ); } } #endregion Command Line parsing if ( capturetext ) { // Based on code by Simon Mourier (http://www.softfluent.com/) // http://stackoverflow.com/questions/12355378/read-from-location-on-console-c-sharp string readtext = string.Empty; for ( short i = 0; i < (short) Console.BufferHeight; i++ ) { foreach ( string line in ConsoleReader.ReadFromBuffer( 0, i, (short) Console.BufferWidth, 1 ) ) { if ( trimoutput ) { // Trim whitespace from the end of the line, based on a suggestion by Daniel Saul Dominguez readtext += line.TrimEnd( "\t ".ToCharArray( ) ) + "\n"; } else { readtext += line + "\n"; } } } readtext = readtext.TrimEnd( " \n\r\t".ToCharArray( ) ); if ( usestandardout ) { Console.Write( readtext ); } else { StreamWriter file = new StreamWriter( outputfile ); file.Write( readtext ); file.Close( ); } } else { // Based on code by Ali Hamdar (http://alihamdar.com/) // http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/79efecc4-fa6d-4078-afe4-bb1379bb968b // Default values for full screen int width = Screen.PrimaryScreen.Bounds.Width; int height = Screen.PrimaryScreen.Bounds.Height; int top = 0; int left = 0; Bitmap printscreen = new Bitmap( width, height ); Graphics graphics = Graphics.FromImage( printscreen as Image ); graphics.CopyFromScreen( top, left, 0, 0, printscreen.Size ); printscreen.Save( outputfile, imagetype ); } return 0; } catch ( Exception e ) { return ShowHelp( e.Message ); } } static string GenerateUniqueFileName( string type ) { string ext = type.Substring( 0, 3 ).ToLower( ); if ( type.ToLower( ) == "jpeg" ) { ext = "jpg"; } return Path.Combine( Directory.GetCurrentDirectory( ), String.Format( "PrintScreen{0}.{1}", DateTime.Now.ToString( "yyyyMMddHHmmssffffff" ), ext ) ); } #region Error Handling public static int ShowHelp( params string[] errmsg ) { /* PrintScreen, Version 1.04 Save screenshot as image, or console buffer to text file or standard output Usage: PRINTSCREEN imagefile [ /T:type ] [ /O ] or: PRINTSCREEN textfile [ /T:TXT ] [ /O ] [ /C ] or: PRINTSCREEN /U /T:type or: PRINTSCREEN /S [ /C ] Where: imagefile is the image file to save the screenshot to textfile is the text file to save the console buffer text to Options: /C trim lines (text only) /O Overwrites an existing file /S send captured text to Standard output (assuming /T:TXT; do not specify a file name, nor type, nor overwrite) /T:type specifies the file Type: BMP, GIF, JPG, PNG, TIF or TXT (required only if outputfile extension is different) /U automatically generate a Unique output file name in PrintScreenyyyyMMddHHmmssffffff.ext format in current directory (requires /T:type, no other arguments allowed) Note: Return code ("errorlevel") is 1 in case of errors, or 0 otherwise. Credits: Code to read console buffer by Simon Mourier http://www.softfluent.com Code for graphic screenshot by Ali Hamdar http://alihamdar.com Written by Rob van der Woude http://www.robvanderwoude.com */ if ( errmsg.Length > 0 ) { List errargs = new List( errmsg ); errargs.RemoveAt( 0 ); Console.Error.WriteLine( ); Console.ForegroundColor = ConsoleColor.Red; Console.Error.Write( "ERROR:\t" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) ); Console.ResetColor( ); } Console.Error.WriteLine( ); Console.Error.WriteLine( "PrintScreen, Version {0}", progver ); Console.Error.WriteLine( "Save screenshot as image, or console buffer to text file or standard output" ); Console.Error.WriteLine( ); Console.Error.Write( "Usage: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "PRINTSCREEN imagefile [ /T:type ] [ /O ]" ); Console.ResetColor( ); Console.Error.Write( " or: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "PRINTSCREEN textfile [ /T:TXT ] [ /O ] [ /C ]" ); Console.ResetColor( ); Console.Error.Write( " or: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "PRINTSCREEN /U /T:type" ); Console.ResetColor( ); Console.Error.Write( " or: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "PRINTSCREEN /S [ /C ]" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Where: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "imagefile" ); Console.ResetColor( ); Console.Error.WriteLine( " is the image file to save the screenshot to" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " textfile" ); Console.ResetColor( ); Console.Error.WriteLine( " is the text file to save the console buffer text to" ); Console.Error.WriteLine( ); Console.Error.Write( "Options: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/C" ); Console.ResetColor( ); Console.Error.WriteLine( " trim lines (text only)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /O O" ); Console.ResetColor( ); Console.Error.WriteLine( "verwrites an existing file" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /S" ); Console.ResetColor( ); Console.Error.Write( " send captured text to " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "S" ); Console.ResetColor( ); Console.Error.Write( "tandard output (assuming " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/T:TXT" ); Console.ResetColor( ); Console.Error.WriteLine( ";" ); Console.Error.WriteLine( " do not specify a file name, nor type, nor overwrite)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /T:type" ); Console.ResetColor( ); Console.Error.Write( " specifies the file " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "T" ); Console.ResetColor( ); Console.Error.Write( "ype: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "BMP" ); Console.ResetColor( ); Console.Error.Write( ", " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "GIF" ); Console.ResetColor( ); Console.Error.Write( ", " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "JPG" ); Console.ResetColor( ); Console.Error.Write( ", " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "PNG" ); Console.ResetColor( ); Console.Error.Write( ", " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "TIF" ); Console.ResetColor( ); Console.Error.Write( " or " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "TXT" ); Console.ResetColor( ); Console.Error.Write( " (required only if " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "outputfile" ); Console.ResetColor( ); Console.Error.WriteLine( " extension is different)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /U" ); Console.ResetColor( ); Console.Error.Write( " automatically generate a " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "U" ); Console.ResetColor( ); Console.Error.WriteLine( "nique output file name in" ); Console.Error.Write( " PrintScreen" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "YYYYMMDDHHmmssffffff" ); Console.ResetColor( ); Console.Error.WriteLine( ".ext format in current" ); Console.Error.Write( " directory (requires " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/T:type" ); Console.ResetColor( ); Console.Error.WriteLine( ", no other arguments allowed)" ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Note: Return code (\"errorlevel\") is 1 in case of errors, or 0 otherwise." ); Console.Error.WriteLine( ); Console.Error.Write( "Credits: Code to read console buffer by Simon Mourier " ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.WriteLine( "http://www.softfluent.com" ); Console.ResetColor( ); Console.Error.Write( " Code for graphic screenshot by Ali Hamdar " ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.WriteLine( "http://alihamdar.com" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Written by Rob van der Woude" ); Console.Error.WriteLine( "http://www.robvanderwoude.com" ); return 1; } #endregion Error Handling } #region Read From Console Buffer // ConsoleReader by Simon Mourier (http://www.softfluent.com/) // http://stackoverflow.com/questions/12355378/read-from-location-on-console-c-sharp public class ConsoleReader { public static IEnumerable ReadFromBuffer( short x, short y, short width, short height ) { IntPtr buffer = Marshal.AllocHGlobal( width * height * Marshal.SizeOf( typeof( CHAR_INFO ) ) ); if ( buffer == null ) { throw new OutOfMemoryException( ); } try { COORD coord = new COORD( ); SMALL_RECT rc = new SMALL_RECT( ); rc.Left = x; rc.Top = y; rc.Right = (short) ( x + width - 1 ); rc.Bottom = (short) ( y + height - 1 ); COORD size = new COORD( ); size.X = width; size.Y = height; const int STD_OUTPUT_HANDLE = -11; if ( !ReadConsoleOutput( GetStdHandle( STD_OUTPUT_HANDLE ), buffer, size, coord, ref rc ) ) { // Not enough storage is available to process this command // may be raised for buffer size > 64K (see ReadConsoleOutput doc.) throw new Win32Exception( Marshal.GetLastWin32Error( ) ); } IntPtr ptr = buffer; for ( int h = 0; h < height; h++ ) { StringBuilder sb = new StringBuilder( ); for ( int w = 0; w < width; w++ ) { CHAR_INFO ci = (CHAR_INFO) Marshal.PtrToStructure( ptr, typeof( CHAR_INFO ) ); char[] chars = Console.OutputEncoding.GetChars( ci.charData ); sb.Append( chars[0] ); ptr += Marshal.SizeOf( typeof( CHAR_INFO ) ); } yield return sb.ToString( ); } } finally { Marshal.FreeHGlobal( buffer ); } } [StructLayout( LayoutKind.Sequential )] private struct CHAR_INFO { [MarshalAs( UnmanagedType.ByValArray, SizeConst = 2 )] public byte[] charData; public short attributes; } [StructLayout( LayoutKind.Sequential )] private struct COORD { public short X; public short Y; } [StructLayout( LayoutKind.Sequential )] private struct SMALL_RECT { public short Left; public short Top; public short Right; public short Bottom; } [StructLayout( LayoutKind.Sequential )] private struct CONSOLE_SCREEN_BUFFER_INFO { public COORD dwSize; public COORD dwCursorPosition; public short wAttributes; public SMALL_RECT srWindow; public COORD dwMaximumWindowSize; } [DllImport( "kernel32.dll", SetLastError = true )] private static extern bool ReadConsoleOutput( IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion ); [DllImport( "kernel32.dll", SetLastError = true )] private static extern IntPtr GetStdHandle( int nStdHandle ); } #endregion Read From Console Buffer }