using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; namespace RobvanderWoude { class SystemTrayMessage { static string progver = "1.06"; static int rc = 0; static NotifyIcon notifyicon; static int Main( string[] args ) { #region Initialize Variables bool escapemessage = true; bool tooltip = false; bool wait = false; int timeout = 10000; int iconindex = 277; string[] searchpath = Environment.GetEnvironmentVariable( "PATH" ).Split( new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries ); string iconfile = "shell32.dll"; string iconext = ".dll"; Icon systrayicon = IconExtractor.Extract( iconfile, iconindex, true ); ToolTipIcon tooltipicon = ToolTipIcon.Info; string message = String.Empty; string title = String.Empty; #endregion Initialize Variables #region Parse Command Line if ( args.Length == 0 ) { return ShowHelp( ); } foreach ( string arg in args ) { if ( arg == "/?" ) { return ShowHelp( ); } if ( arg.ToUpper( ) == "/W" ) { if ( wait ) { return ShowHelp( "Duplicate command line switch /W" ); } wait = true; } else if ( arg.ToUpper( ) == "/L" ) { if ( !escapemessage ) { return ShowHelp( "Duplicate command line switch /NE" ); } escapemessage = false; } else if ( arg[0] == '/' ) { if ( arg.Length > 3 && arg[2] == ':' ) { switch ( arg[1].ToString( ).ToUpper( ) ) { case "I": if ( tooltipicon != ToolTipIcon.Info ) { return ShowHelp( "Duplicate command line switch /I" ); } switch ( arg.Substring( 3 ).ToUpper( ) ) { case "ERROR": tooltipicon = ToolTipIcon.Error; break; case "INFO": tooltipicon = ToolTipIcon.Info; break; case "NONE": tooltipicon = ToolTipIcon.None; break; case "WARNING": tooltipicon = ToolTipIcon.Warning; break; default: return ShowHelp( "Invalid tooltip icon type \"{0}\"", arg.Substring( 3 ) ); } break; case "P": if ( iconfile.ToUpper( ) != "SHELL32.DLL" ) { return ShowHelp( "Duplicate command line switch /P" ); } iconext = Path.GetExtension( arg.Substring( 3 ) ).ToLower( ); if ( File.Exists( arg.Substring( 3 ) ) ) { iconfile = Path.GetFullPath( arg.Substring( 3 ) ); } else if ( iconext == ".dll" || iconext == ".exe" ) { foreach ( string folder in searchpath ) { if ( File.Exists( Path.Combine( folder, arg.Substring( 3 ) ) ) ) { iconfile = Path.Combine( folder, arg.Substring( 3 ) ); break; } } if ( !File.Exists( iconfile ) ) { return ShowHelp( "Invalid path to icon file or library \"{0}\"", arg.Substring( 3 ) ); } } break; case "S": if ( iconindex != 277 ) { return ShowHelp( "Duplicate command line switch /S" ); } try { iconindex = Convert.ToInt32( arg.Substring( 3 ) ); if ( iconindex < 0 ) { return ShowHelp( "Invalid system tray icon index \"{0}\"", arg.Substring( 3 ) ); } } catch ( Exception ) { return ShowHelp( "Invalid system tray icon argument \"{0}\"", arg ); } break; case "T": if ( String.IsNullOrWhiteSpace( title ) ) { title = arg.Substring( 3 ).Trim( "\" \t".ToCharArray( ) ); } else { return ShowHelp( "Duplicate command line switch /T" ); } break; case "V": if ( timeout != 10000 ) { return ShowHelp( "Duplicate command line switch /V" ); } try { timeout = 1000 * Convert.ToInt32( arg.Substring( 3 ) ); if ( timeout < 1000 ) { return ShowHelp( "Invalid time (\"{0}\"), must be greater than zero", arg.Substring( 3 ) ); } } catch ( Exception ) { return ShowHelp( "Invalid time \"{0}\"", arg.Substring( 3 ) ); } break; default: return ShowHelp( "Invalid command line switch \"{0}\"", arg ); } } else { return ShowHelp( "Invalid command line switch \"{0}\"", arg ); } } else { if ( String.IsNullOrWhiteSpace( message ) ) { message = arg; tooltip = true; } else { return ShowHelp( "Duplicate message on command line" ); } } } if ( String.IsNullOrWhiteSpace( message ) ) { tooltip = false; } if ( tooltip && escapemessage ) { message = Regex.Replace( message, @"(? { rc = 2; }; notifyicon.BalloonTipClosed += ( object sender, EventArgs e ) => { rc = 2; }; } notifyicon.ShowBalloonTip( timeout, title, message, tooltipicon ); } if ( wait ) { rc = 3; while ( timeout > 0 && rc == 3 ) { Thread.Sleep( 1 ); Application.DoEvents( ); timeout--; } notifyicon.Visible = false; notifyicon.Dispose( ); } return rc; } static int ShowHelp( params string[] errmsg ) { #region Error Message 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( ); } #endregion Error Message #region Help Text /* SystemTrayMessage.exe, Version 1.06 Display an icon in the system tray's notification area Usage: SystemTrayMessage.exe [ message ] [ options ] Where: message is the message text in the optional tooltip balloon Options: /I:icon tooltip Icon (Error, Info, None, Warning; default: Info) /L treat message as Literal text without interpreting escaped characters, e.g. show "\n" as literal "\n" instead of interpreting it as a newline character /P:path Path to an icon file or library (default: Shell32.dll) /S:index System tray icon index in icon library (default: 277) /T:title specifies the optional Title in the tooltip balloon /V:seconds specifies the number of seconds the tooltip balloon will remain Visible (default: 10) /W Wait for the timeout to elapse or for the user to click the message, then remove the icon from the notification area (default: exit without waiting) Notes: If no tooltip message is specified, switches /I, /L and /T will be ignored and no tooltip will be shown. By default, \n is interpreted as newline and \t as tab in message; in some cases this may lead to misinterpretations, e.g. when showing a path like "c:\temp"; either escape backslashes in paths or use /L to treat all message text as literal text. Command line switch /S will be ignored if switch /P specifies anything but an icon library. Use my Shell32Icons.exe to select a Shell32 icon and get its index. Return code ("ErrorLevel") is -1 in case of errors; with /W switch, return code is 2 if message balloon is clicked, or 3 if the timeout expired without clicking; otherwise return code is 0. Credits: Code to extract icons from Shell32.dll by Thomas Levesque http://stackoverflow.com/questions/6873026 Written by Rob van der Woude https://www.robvanderwoude.com */ Console.Error.WriteLine( ); Console.Error.WriteLine( "SystemTrayMessage.exe, Version {0}", progver ); Console.Error.WriteLine( "Display an icon in the system tray's notification area" ); Console.Error.WriteLine( ); Console.Error.Write( "Usage: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "SystemTrayMessage.exe [ message ] [ options ]" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Where: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "message" ); Console.ResetColor( ); Console.Error.Write( " is the " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "message" ); Console.ResetColor( ); Console.Error.WriteLine( " text in the optional tooltip balloon" ); Console.Error.WriteLine( ); Console.Error.Write( "Options: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/I:icon" ); Console.ResetColor( ); Console.Error.Write( " tooltip " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "I" ); Console.ResetColor( ); Console.Error.WriteLine( "con (Error, Info, None, Warning; default: Info)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /L" ); Console.ResetColor( ); Console.Error.Write( " treat " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "message" ); Console.ResetColor( ); Console.Error.Write( " as " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "L" ); Console.ResetColor( ); Console.Error.WriteLine( "iteral text without interpreting" ); Console.Error.WriteLine( " escaped characters, e.g. show \"\\n\" as literal \"\\n\"" ); Console.Error.WriteLine( " instead of interpreting it as a newline character" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /P:path P" ); Console.ResetColor( ); Console.Error.WriteLine( "ath to an icon file or library (default: Shell32.dll)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /S:index S" ); Console.ResetColor( ); Console.Error.Write( "ystem tray icon " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "index" ); Console.ResetColor( ); Console.Error.WriteLine( " in icon library (default: 277)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /T:title" ); Console.ResetColor( ); Console.Error.Write( " optional " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "T" ); Console.ResetColor( ); Console.Error.WriteLine( "itle in the tooltip balloon" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /V:seconds" ); Console.ResetColor( ); Console.Error.Write( " number of " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "seconds" ); Console.ResetColor( ); Console.Error.WriteLine( " the tooltip balloon will remain" ); Console.Error.Write( "" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " V" ); Console.ResetColor( ); Console.Error.WriteLine( "isible (default: 10)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /W W" ); Console.ResetColor( ); Console.Error.WriteLine( "ait for the timeout to elapse or for the user to click" ); Console.Error.WriteLine( " the message, then remove the icon from the notification" ); Console.Error.WriteLine( " area (default: exit without waiting)" ); Console.Error.WriteLine( ); Console.Error.Write( "Notes: If no tooltip " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "message" ); Console.ResetColor( ); Console.Error.Write( " is specified, switches " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/I" ); Console.ResetColor( ); Console.Error.Write( ", " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/L" ); Console.ResetColor( ); Console.Error.Write( " and " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/T" ); Console.ResetColor( ); Console.Error.WriteLine( " will be" ); Console.Error.WriteLine( " ignored and no tooltip will be shown." ); Console.Error.Write( " By default, " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "\\n" ); Console.ResetColor( ); Console.Error.Write( " is interpreted as newline and " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "\\t" ); Console.ResetColor( ); Console.Error.Write( " as tab in " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "message" ); Console.ResetColor( ); Console.Error.WriteLine( ";" ); Console.Error.WriteLine( " in some cases this may lead to misinterpretations, e.g. when showing" ); Console.Error.Write( " a path like \"c:\\temp\"; either escape backslashes in paths or use " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "/L" ); Console.ResetColor( ); Console.Error.WriteLine( " to treat all message text as literal text." ); Console.Error.Write( " Command line switch " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/S" ); Console.ResetColor( ); Console.Error.Write( " will be ignored if switch " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/P" ); Console.ResetColor( ); Console.Error.WriteLine( " specifies" ); Console.Error.WriteLine( " anything but an icon library." ); Console.Error.WriteLine( " Use my Shell32Icons.exe to select a Shell32 icon and get its index." ); Console.Error.Write( " Return code (\"ErrorLevel\") is -1 in case of errors; with " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/W" ); Console.ResetColor( ); Console.Error.WriteLine( " switch," ); Console.Error.WriteLine( " return code is 2 if message balloon is clicked, or 3 if the timeout" ); Console.Error.WriteLine( " expired without clicking; otherwise return code is 0." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Credits: Code to extract icons from Shell32.dll by Thomas Levesque" ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.WriteLine( " http://stackoverflow.com/questions/6873026" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Written by Rob van der Woude" ); Console.Error.WriteLine( "https://www.robvanderwoude.com" ); #endregion Help Text return -1; } // Code to extract icons from Shell32.dll by Thomas Levesque // http://stackoverflow.com/questions/6873026 public class IconExtractor { public static Icon Extract( string file, int number, bool largeIcon ) { IntPtr large; IntPtr small; ExtractIconEx( file, number, out large, out small, 1 ); try { return Icon.FromHandle( largeIcon ? large : small ); } catch { return null; } } [DllImport( "Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall )] private static extern int ExtractIconEx( string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons ); } } }