using System; using System.Collections.Generic; using System.Linq; using System.Management; using System.Text.RegularExpressions; namespace RobvanderWoude { public class Printing { public const string progver = "3.04"; #region Global Variables public static char switchchar = '/'; public static List actionslist = new List( ); public static List printerlist = new List( ); public static bool actionFlush = false; public static bool actionList = false; public static bool actionPause = false; public static bool actionResume = false; public static string action = String.Empty; #endregion Global Variables public static int Main( string[] args ) { try { #region Initialize Variables bool actionisset = false; bool takeaction = true; bool printerisset = false; bool useAllPrn = false; bool useDefault = false; bool useRegex = false; string pattern = ".*"; string printer = String.Empty; bool sortisset = false; string sortby = "N"; // sort by printer Name WMIPrinterStatus status = WMIPrinterStatus.Unknown; UInt32 jobs = 0; int printerscount = 0; int switchcharunix = 0; int switchchardos = 0; #endregion Initialize Variables #region Command Line Parsing if ( args.Length == 0 ) { return ErrorMessage( ); } if ( args.Length > 4 ) { return ErrorMessage( "Too many command line arguments" ); } foreach ( string arg in args ) { if ( arg.Length < 2 ) { return ErrorMessage( "Invalid command line argument \"{0}\"", arg ); } if ( "-/".Contains( arg[0] ) ) { if ( arg[0] == '-' ) { switchcharunix += 1; } else { switchchardos += 1; } switchchar = ( switchcharunix > switchchardos ? '-' : '/' ); switch ( arg[1].ToString( ).ToUpper( ) ) { case "?": case "H": case "-": return ErrorMessage( ); case "A": if ( useAllPrn ) { return ErrorMessage( "Duplicate command line switch {0}", SwitchString( arg[1] ) ); } if ( printerisset ) { return ErrorMessage( "Duplicate printer arguments \"{0}\" and \"{1}\"", printer, SwitchString( arg[1] ) ); } useAllPrn = true; useDefault = false; useRegex = false; printer = SwitchString( arg[1] ); printerisset = true; break; case "D": if ( useDefault ) { return ErrorMessage( "Duplicate command line switch {0}", SwitchString( arg[1] ) ); } if ( printerisset ) { return ErrorMessage( "Duplicate printer arguments \"{0}\" and \"{1}\"", printer, SwitchString( arg[1] ) ); } useDefault = true; useAllPrn = false; useRegex = false; printer = SwitchString( arg[1] ); printerisset = true; break; case "F": if ( actionFlush ) { return ErrorMessage( "Duplicate command line switch {0}", SwitchString( arg[1] ) ); } if ( actionisset ) { return ErrorMessage( "Duplicate action arguments \"{0}\" and \"CancelAllJobs\"", action ); } action = "CancelAllJobs"; actionisset = true; actionFlush = true; break; case "L": if ( actionList ) { return ErrorMessage( "Duplicate command line switch {0}", SwitchString( arg[1] ) ); } if ( actionisset ) { return ErrorMessage( "Duplicate action arguments \"{0}\" and \"List\"", action ); } action = "List"; actionList = true; actionisset = true; break; case "P": if ( actionPause ) { return ErrorMessage( "Duplicate command line switch {0}", SwitchString( arg[1] ) ); } if ( actionisset ) { return ErrorMessage( "Duplicate action arguments \"{0}\" and \"Pause\"", action ); } action = "Pause"; actionPause = true; actionisset = true; break; case "R": if ( actionResume ) { return ErrorMessage( "Duplicate command line switch {0}", SwitchString( arg[1] ) ); } if ( actionisset ) { return ErrorMessage( "Duplicate action arguments \"{0}\" and \"Resume\"", action ); } action = "Resume"; actionResume = true; actionisset = true; break; case "S": if ( sortisset ) { return ErrorMessage( "Duplicate command line switch {0}", SwitchString( arg[1] ) ); } if ( arg.Length == 2 ) { sortby = "N"; } else if ( arg.Length == 4 && arg[2] == ':' && "JNSjns".Contains( arg[3] ) ) { sortby = arg[3].ToString( ).ToUpper( ); } else if ( arg[2] == ':' ) { return ErrorMessage( "Invalid sort-by value \"{0}\"", arg ); } else { return ErrorMessage( "Invalid command line switch {0}", arg.ToUpper( ) ); } sortisset = true; break; case "X": if ( useRegex ) { return ErrorMessage( "Duplicate command line switch {0}", SwitchString( arg[1] ) ); } if ( printerisset ) { return ErrorMessage( "Duplicate printer arguments \"{0}\" and \"{1}\"", printer, SwitchString( arg[1] ) ); } if ( arg.Length == 2 ) { return ErrorMessage( "No regex pattern specified with {0}", SwitchString( arg[1] ) ); } else if ( arg.Length == 3 && arg[2] == ':' ) { return ErrorMessage( "No regex pattern specified with {0}", SwitchString( arg[1] ) ); } else if ( arg.Length > 3 && arg[2] == ':' ) { pattern = arg.Substring( 3 ); if ( !VerifyRegExPattern( pattern ) ) { return ErrorMessage( "Invalid regex pattern \"{0}\"", pattern ); } } else { return ErrorMessage( "Invalid command line switch {0}", arg.ToUpper( ) ); } useAllPrn = false; useDefault = false; useRegex = true; printer = arg; printerisset = true; break; default: return ErrorMessage( "Invalid command line switch {0}", arg.ToUpper( ) ); } } else { if ( printerisset ) { return ErrorMessage( "Duplicate printer arguments \"{0}\" and \"{1}\"", printer, arg ); } printer = arg; useAllPrn = false; useDefault = false; useRegex = false; printerisset = true; } } if ( !printerisset ) { if ( actionList ) { useAllPrn = true; useDefault = false; printerisset = true; } else { return ErrorMessage( "Printer not specified.\n\tSpecify a printer name, or regex pattern ({0}:\"regex\"), or use {1} (All printers) or {2} (Default printer).", SwitchString( "X" ), SwitchString( "A" ), SwitchString( "D" ) ); } } if ( !actionisset ) { return ErrorMessage( "No action specified.\n\tUse either {0} (List printers), or {1} (Pause printing),\n\tor {2} (Resume printing), or {3} (Flush print jobs).", SwitchString( "L" ), SwitchString( "P" ), SwitchString( "/R" ), SwitchString( "/F" ) ); } #endregion Command Line Parsing #region Enumerate Printers string query1; if ( useDefault ) { query1 = "SELECT * FROM Win32_Printer WHERE Default='TRUE'"; // default printer } else if ( useAllPrn || useRegex ) { query1 = "SELECT * FROM Win32_Printer"; // all printers } else { query1 = String.Format( "SELECT * FROM Win32_Printer WHERE DeviceID='{0}'", printer ); // specific printer } ManagementObjectSearcher searcher1 = new ManagementObjectSearcher( "root\\CIMV2", query1 ); #endregion Enumerate Printers foreach ( ManagementObject queryObj in searcher1.Get( ) ) { printer = queryObj["DeviceID"].ToString( ); // printer name if ( !useRegex || new Regex( pattern, RegexOptions.IgnoreCase ).IsMatch( printer ) ) { #region Investigate Current Printer Status printerscount += 1; status = (WMIPrinterStatus) Convert.ToUInt32( queryObj["ExtendedPrinterStatus"] ); // printer status string[] properties = new string[3]; properties[0] = printer; properties[1] = status.ToString( ); // number of queued print jobs string query2 = String.Format( "SELECT * FROM Win32_PrintJob WHERE Name LIKE '{0}, %'", printer ); ManagementObjectSearcher searcher2 = new ManagementObjectSearcher( "root\\CIMV2", query2 ); try { jobs = Convert.ToUInt32( searcher2.Get( ).Count ); properties[2] = jobs.ToString( ); } catch { properties[2] = "Unknown"; } printerlist.Add( properties ); #endregion Investigate Current Printer Status takeaction = ( ( actionFlush && jobs > 0 ) || ( actionPause && status != WMIPrinterStatus.Paused ) || ( actionResume && status != WMIPrinterStatus.Unknown ) ); if ( takeaction ) { #region Invoke Action WMIActionResult result = (WMIActionResult) Convert.ToUInt32( queryObj.InvokeMethod( action, null ) ); WMIPrinterStatus newstatus = WMIPrinterStatus.Unknown; #endregion Invoke Action #region Investigate New Status After Action if ( actionFlush ) { searcher2 = new ManagementObjectSearcher( "root\\CIMV2", query2 ); jobs = Convert.ToUInt32( searcher2.Get( ).Count ); } else { query2 = String.Format( "SELECT * FROM Win32_Printer WHERE DeviceID='{0}'", printer ); searcher2 = new ManagementObjectSearcher( "root\\CIMV2", query2 ); foreach ( ManagementObject queryObj2 in searcher2.Get( ) ) { newstatus = (WMIPrinterStatus) Convert.ToUInt32( queryObj2["ExtendedPrinterStatus"] ); } } string[] actions = new string[4]; actions[0] = printer; actions[1] = newstatus.ToString( ); actions[2] = jobs.ToString( ); actions[3] = result.ToString( ); actionslist.Add( actions ); #endregion Investigate New Status After Action } } } #region Warn If No Printers Found if ( printerscount == 0 ) { return ErrorMessage( "No matching printer found, use {0} for a list of valid printer names", ( switchchar == '/' ? "/L" : "-l" ) ); } #endregion Warn If No Printers Found #region Display The results return ShowResults( sortby ); #endregion Display The results } catch ( Exception e ) { #if DEBUG return ErrorMessage( "{0}\n\t{1}", e.Message, e.StackTrace ); #endif return ErrorMessage( e.Message ); } } public static int ShowResults( string sortby ) { int index = 0; switch ( sortby ) { case "J": // Sort by # jobs index = 2; break; case "N": // Sort by name index = 0; break; case "S": // Sort by status index = 1; break; } if ( sortby == "N" ) // Sort only by name, ascending { printerlist = printerlist .OrderBy( arr => arr[0] ) .ToList( ); actionslist = actionslist .OrderBy( arr => arr[0] ) .ToList( ); } else // Sort by status or # jobs, descending, then by name, ascending { printerlist = printerlist .OrderByDescending( arr => arr[index] ) .ThenBy( arr => arr[0] ) .ToList( ); actionslist = actionslist .OrderByDescending( arr => arr[index] ) .ThenBy( arr => arr[0] ) .ToList( ); } foreach ( string[] properties in printerlist ) { string printer = properties[0]; //UInt32 jobs = Convert.ToUInt32( properties[2] ); string jobs = properties[2]; string status = properties[1]; // check if the action really was invoked bool takeaction = ( (( jobs != "Unknown" && !String.IsNullOrWhiteSpace(jobs) && jobs != "0" ) && actionFlush ) || ( actionPause && status != WMIPrinterStatus.Paused.ToString( ) ) || ( actionResume && status != WMIPrinterStatus.Unknown.ToString( ) ) ); if ( takeaction ) { string newstatus = WMIPrinterStatus.Other.ToString( ); //UInt32 newjobs = 0; string newjobs = "0"; string result = WMIActionResult.Other.ToString( ); foreach ( string[] actions in actionslist ) { if ( actions[0] == properties[0] ) // read the actions list for this printer { newstatus = actions[1]; //newjobs = Convert.ToUInt32( actions[2] ); newjobs = actions[2]; result = actions[3]; } } Console.Write( "Printer : " ); if ( result != WMIActionResult.Success.ToString( ) ) { Console.ForegroundColor = ConsoleColor.Yellow; // if unsuccessful, show printer name in yellow } Console.WriteLine( printer ); Console.ResetColor( ); Console.WriteLine( "Printjobs : {0}", jobs ); Console.WriteLine( "Status : {0}", status ); if ( actionFlush ) { Console.Write( "Flush printjobs . . . " ); } else { Console.Write( "{0} printing . . . ", ( actionPause ? "Pause" : "Resume" ) ); } if ( result != WMIActionResult.Success.ToString( ) ) { Console.ForegroundColor = ConsoleColor.Red; // if unsuccessful, show result in red } Console.WriteLine( result ); Console.ResetColor( ); if ( actionFlush ) { Console.WriteLine( "Printjobs : {0}", newjobs ); } else { Console.WriteLine( "Status : {0}", newstatus ); } } else // no action was invoked, just list status { Console.WriteLine( "Printer : {0}", printer ); Console.WriteLine( "Printjobs : {0}", jobs ); Console.WriteLine( "Status : {0}", status ); } Console.WriteLine( ); } return 0; } public static string SwitchString( char sw ) { return SwitchString( sw.ToString( ) ); } public static string SwitchString( string sw ) { if ( switchchar == '-' ) { return String.Format( "{0}{1}", switchchar, sw.ToLower( ) ); } else { return String.Format( "{0}{1}", switchchar, sw.ToUpper( ) ); } } static bool VerifyRegExPattern( string testpattern ) { // Test validity of RegEx pattern // Based on http://stackoverflow.com/questions/218680/can-i-test-if-a-regex-is-valid-in-c-sharp-without-throwing-exception if ( String.IsNullOrWhiteSpace( testpattern ) ) { return false; } try { Regex.Match( "", testpattern ); return true; } catch ( ArgumentException ) { return false; } } public static int ErrorMessage( params string[] errmsg ) { /* Printing.exe, Version 3.04 Pause or resume printing, or flush all queued printjobs on the specified printer(s), or list all printers, their status and number of print jobs Usage: Printing.exe printer action [ option ] Printer: /A use All printers /D use Default printer /X:"regex" use all printers matching the regular eXpression name use the specified printer Action: /F Flush queued print jobs /L List printer name, status, and number of queued print jobs /P Pause printing /R Resume printing Option: /S:(N|S|J) Sort by printer Name (default), Status or # print Jobs Examples: PRINTING /D /P Pause printing on Default printer PRINTING PDF995 /F Flush print jobs of the printer named PDF995 PRINTING /A /R Resume printing on All printers PRINTING /L /S:J List all printers, Sort by # queued print Jobs Notes: Use doublequotes if the printer name contains spaces. With /L and no printer specified, All printers (/A) will be assumed. Credits: LINQ code to sort List of string arrays by Tim Schmelter: http://stackoverflow.com/questions/23873378#23873402 Test for validity of regex pattern based on code from: http://stackoverflow.com/questions/218680 PrinterInfo code by Bas van der Woude. 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( "Printing.exe, Version {0}", progver ); Console.Error.WriteLine( "Pause or resume printing, or flush all queued printjobs on the specified" ); Console.Error.WriteLine( "printer(s), or list all printers, their status and number of print jobs" ); Console.Error.WriteLine( ); Console.Error.Write( "Usage: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "Printing.exe printer action [ option ]" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Printer: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "{0}", SwitchString( "A" ) ); Console.ResetColor( ); Console.Error.Write( " use " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "A" ); Console.ResetColor( ); Console.Error.WriteLine( "ll printers" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " {0}", SwitchString( "D" ) ); Console.ResetColor( ); Console.Error.Write( " use " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "D" ); Console.ResetColor( ); Console.Error.WriteLine( "efault printer" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " {0}:\"regex\"", SwitchString( "X" ) ); Console.ResetColor( ); Console.Error.Write( " use all printers matching the regular e" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "X" ); Console.ResetColor( ); Console.Error.WriteLine( "pression" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " name" ); Console.ResetColor( ); Console.Error.WriteLine( " use the specified printer" ); Console.Error.Write( "Action: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "{0} F", SwitchString( "F" ) ); Console.ResetColor( ); Console.Error.WriteLine( "lush queued print jobs" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " {0} L", SwitchString( "L" ) ); Console.ResetColor( ); Console.Error.WriteLine( "ist printer name, status, and number of queued print jobs" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " {0} P", SwitchString( "P" ) ); Console.ResetColor( ); Console.Error.WriteLine( "ause printing" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " {0} R", SwitchString( "R" ) ); Console.ResetColor( ); Console.Error.WriteLine( "esume printing" ); Console.Error.Write( "Option: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "{0} S", SwitchString( "S:(N|S|J)" ) ); Console.ResetColor( ); Console.Error.Write( "ort by printer " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "N" ); Console.ResetColor( ); Console.Error.Write( "ame (default), " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "S" ); Console.ResetColor( ); Console.Error.Write( "tatus or # print " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "J" ); Console.ResetColor( ); Console.Error.WriteLine( "obs" ); Console.Error.WriteLine( ); Console.Error.Write( "Examples: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "PRINTING {0} {1} P", SwitchString( "D" ), SwitchString( "P" ) ); Console.ResetColor( ); Console.Error.Write( "ause printing on " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "D" ); Console.ResetColor( ); Console.Error.WriteLine( "efault printer" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " PRINTING PDF995 {0} F", SwitchString( "F" ) ); Console.ResetColor( ); Console.Error.Write( "lush print jobs of the printer named " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "PDF995" ); Console.ResetColor( ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " PRINTING {0} {1} R", SwitchString( "A" ), SwitchString( "R" ) ); Console.ResetColor( ); Console.Error.Write( "esume printing on " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "A" ); Console.ResetColor( ); Console.Error.WriteLine( "ll printers" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " PRINTING {0} {1} L", SwitchString( "L" ), SwitchString( "S:J" ) ); Console.ResetColor( ); Console.Error.Write( "ist all printers, " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "S" ); Console.ResetColor( ); Console.Error.Write( "ort by # queued print " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "J" ); Console.ResetColor( ); Console.Error.WriteLine( "obs" ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Notes: Use doublequotes if the printer name contains spaces." ); Console.Error.Write( " With " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( SwitchString( "L" ) ); Console.ResetColor( ); Console.Error.Write( " and no printer specified, " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "A" ); Console.ResetColor( ); Console.Error.Write( "ll printers (" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( SwitchString( "A" ) ); Console.ResetColor( ); Console.Error.WriteLine( ") will be assumed." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Credits: LINQ code to sort List of string arrays by Tim Schmelter:" ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.WriteLine( " http://stackoverflow.com/questions/23873378#23873402" ); Console.ResetColor( ); Console.Error.WriteLine( " Test for validity of regex pattern based on code from:" ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.WriteLine( " http://stackoverflow.com/questions/218680" ); Console.ResetColor( ); Console.Error.WriteLine( " PrinterInfo code by Bas van der Woude." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Written by Rob van der Woude" ); Console.Error.WriteLine( "http://www.robvanderwoude.com" ); return 1; } } #region PrinterInfo // Author : Bas van der Woude // Date : 2011-03-31 public class PrinterInfo { public string Name { get; set; } public uint Status { get; set; } public uint PrintJobCount { get; set; } public WMIActionResult ActionResult { get; set; } } public enum WMIActionResult { Success = 0, AccessDenied = 5, Other = 8 } public enum WMIPrinterStatus { Other = 1, Unknown = 2, Idle = 3, Printing = 4, Warmup = 5, StoppedPrinting = 6, Offline = 7, Paused = 8, Error = 9, Busy = 10, NotAvailable = 11, Waiting = 12, Processing = 13, Initialization = 14, PowerSave = 15, PendingDeletion = 16, IOActive = 17, ManualFeed = 18 } #endregion PrinterInfo }