(view source code of progressbargui.cs as plain text)
using System;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
namespace RobvanderWoude
{
internal static class ProgressBarGUI
{
static readonly string progver = "1.00";
#region Global Variables
static System.Timers.Timer timeouttimer = new System.Timers.Timer( );
static Form dialog = new FormDialog( ) { ShowInTaskbar = false };
static ProgressBar progressbar = new ProgressBar( );
static Label labeldescription = new Label( );
static Label helptext = new Label( );
static string tempfile = Path.Combine( Environment.CurrentDirectory, "progress.tmp" ); // file this program reads its progress from
static float currentvalue = 0F;
static int min = 0; // minimum value for progress bar
static int max = 100; // maximum value for progress bar
static int range = 100; // difference between max and min
static int height = 100; // height of dialog window
static int width = 600; // width of dialog window
static bool debug = false;
#endregion Global Variables
[STAThread]
static void Main( string[] args )
{
#region Allow Only Single Instance
// Code to allow only single instance of program by michalczerwinski
// https://stackoverflow.com/a/6486341
bool result = false;
string uniqueid = string.Format( "ProgressbarGUI-{0}", Environment.GetEnvironmentVariable( "ComputerName" ) );
Mutex mutex = new Mutex( true, uniqueid, out result );
if ( !result )
{
MessageBox.Show( "Another instance is running already.", string.Format( "ProgressBarGUI, Version {0}", progver ) );
return;
}
#endregion Allow Only Single Instance
#region Initialize Variables
int interval = 1000; // progress refresh inteval in milliseconds
int timeout = 3600; // program timeout in seconds, 1 hour default
int x = (int)( ( Screen.PrimaryScreen.WorkingArea.Width - width ) / 2 ); // X-position of top of dialog window
int y = (int)( ( Screen.PrimaryScreen.WorkingArea.Height - height ) / 2 ); // Y-position of left side of dialog window
string caption = string.Empty; // text above progress bar
string description = string.Empty; // text below progress bar
string title = string.Format( "ProgressBarGUI, Version {0}", progver ); // text in title bar
bool showscale = true;
#endregion Initialize Variables
#region Parse Command Line
foreach ( string arg in args )
{
if ( arg[0] == '/' )
{
if ( arg.IndexOfAny( ":=".ToCharArray( ) ) > 1 )
{
string key = arg.Substring( 0, arg.IndexOfAny( ":=".ToCharArray( ) ) ).ToUpper( );
string val = arg.Substring( arg.IndexOfAny( ":=".ToCharArray( ) ) + 1 ).Trim( );
switch ( key )
{
case "/CAPTION":
caption = val.Replace( "\\n", "\n" ).Replace( "\\t", "\t" );
break;
case "/DESCRIPTION":
description = val.Replace( "\\n", "\n" ).Replace( "\\t", "\t" );
break;
case "/FILE":
if ( !string.IsNullOrWhiteSpace( val ) && !File.Exists( val ) )
{
if ( Directory.Exists( Directory.GetParent( val ).FullName ) )
{
File.WriteAllText( val, string.Empty );
}
else
{
ShowHelp( );
return;
}
}
tempfile = val;
break;
case "/HEIGHT":
if ( !int.TryParse( val, out height ) )
{
ShowHelp( );
return;
}
break;
case "/INTERVAL":
if ( !int.TryParse( val, out interval ) )
{
ShowHelp( );
return;
}
break;
case "/MAX":
if ( !int.TryParse( val, out max ) )
{
ShowHelp( );
return;
}
break;
case "/MIN":
if ( !int.TryParse( val, out min ) )
{
ShowHelp( );
return;
}
break;
case "/POS":
Regex regex = new Regex( "^(\\d+)[x,;](\\d+)$" );
if ( regex.IsMatch( val ) )
{
MatchCollection matches = regex.Matches( val );
if ( !int.TryParse( matches[0].Groups[1].Value, out width ) )
{
ShowHelp( );
return;
}
if ( !int.TryParse( matches[0].Groups[2].Value, out height ) )
{
ShowHelp( );
return;
}
}
else
{
ShowHelp( );
return;
}
break;
case "/TIMEOUT":
if ( !int.TryParse( val, out timeout ) )
{
ShowHelp( );
return;
}
if ( timeout < 0 || timeout > 86400 ) // 24h max; 0 means no timeout
{
ShowHelp( );
return;
}
break;
case "/TITLE":
title = val;
break;
case "/WIDTH":
if ( !int.TryParse( val, out width ) )
{
ShowHelp( );
return;
}
break;
default:
ShowHelp( );
return;
}
}
else if ( arg.ToUpper( ) == "/DEBUG" )
{
debug = true;
}
else if ( arg.ToUpper( ) == "/NOSCALE" )
{
showscale = false;
}
else if ( arg.ToUpper( ) == "/SAMPLES" )
{
CreateSamples( );
ShowHelp( );
return;
}
else
{
ShowHelp( );
return;
}
}
}
if ( max <= min )
{
ShowHelp( );
return;
}
range = max - min;
if ( debug )
{
description = "Debug Mode";
}
#endregion Parse Command Line
#region Timeout
if ( timeout > 0 )
{
timeouttimer.Enabled = true;
timeouttimer.Interval = timeout * 1000;
timeouttimer.Elapsed += Timeouttimer_Elapsed;
timeouttimer.Start( );
}
#endregion Timeout
#region Dialog Window
dialog.ClientSize = new Size( width, height );
dialog.Left = x;
dialog.MaximizeBox = false;
dialog.MinimizeBox = false;
dialog.ShowInTaskbar = false;
dialog.SizeGripStyle = SizeGripStyle.Hide;
dialog.Text = title;
dialog.Top = y;
dialog.TopMost = true;
dialog.WindowState = FormWindowState.Normal;
int controlswidth = (int)( width * 0.8 );
int linespacing = 10;
int leftmargin = (int)( width / 10 );
int verticaloffset = linespacing;
#endregion Dialog Window
#region Optional Caption
if ( !string.IsNullOrWhiteSpace( caption ) )
{
Label labelcaption = new Label
{
AutoSize = true,
Location = new Point( leftmargin, verticaloffset ),
MaximumSize = new Size( controlswidth, (int)( height * 0.8 ) ),
Text = caption
};
labelcaption.Font = new Font( labelcaption.Font.FontFamily, 12 );
dialog.Controls.Add( labelcaption );
int textheight = (int)GetTextSize( caption, labelcaption.Font ).Height;
verticaloffset += textheight + linespacing;
};
#endregion Optional Caption
#region Progress Bar and Scale
verticaloffset = Math.Max( verticaloffset, 35 );
if ( showscale )
{
Label scalemin = new Label
{
Padding = new Padding( 0, 0, 5, 0 ),
Text = min.ToString( ),
TextAlign = ContentAlignment.MiddleRight,
Width = leftmargin
};
scalemin.Font = new Font( scalemin.Font.FontFamily, 10 );
scalemin.Location = new Point( 0, verticaloffset );
dialog.Controls.Add( scalemin );
}
progressbar.Maximum = max;
progressbar.Minimum = min;
progressbar.Value = (int)( 100F * ( currentvalue - min ) / range );
progressbar.Width = controlswidth;
progressbar.Location = new Point( leftmargin, verticaloffset );
dialog.Controls.Add( progressbar );
if ( showscale )
{
Label scalemax = new Label
{
Padding = new Padding( 5, 0, 0, 0 ),
Text = max.ToString( ),
TextAlign = ContentAlignment.MiddleLeft,
Width = leftmargin
};
scalemax.Font = new Font( scalemax.Font.FontFamily, 10 );
scalemax.Location = new Point( width - scalemax.Width, verticaloffset );
dialog.Controls.Add( scalemax );
}
verticaloffset += progressbar.Height + linespacing;
dialog.Refresh( );
#endregion Progress Bar
#region Optional Description
if ( !string.IsNullOrWhiteSpace( description ) )
{
labeldescription = new Label
{
AutoSize = true,
Location = new Point( leftmargin, verticaloffset ),
MaximumSize = new Size( controlswidth, (int)( height * 0.8 ) ),
Text = description
};
labeldescription.Font = new Font( labeldescription.Font.FontFamily, 10 );
dialog.Controls.Add( labeldescription );
int textheight = (int)GetTextSize( description, labeldescription.Font ).Height;
verticaloffset += textheight;
};
#endregion Optional Description
#region Resize and Show Dialog
height = Math.Max( height, verticaloffset + 2 * linespacing );
dialog.ClientSize = new Size( width, height );
dialog.Visible = true;
dialog.Activate( );
dialog.Show( );
#endregion Resize and Show Dialog
#region Update Progress Bar
while ( File.Exists( tempfile ) )
{
Thread.Sleep( interval );
Thread thread = new Thread( UpdateValueThread );
thread.Start( );
Application.DoEvents( );
}
#endregion Update Progress Bar
Quit( );
return;
}
static void CreateSamples( )
{
string demo1 = ":: Make sure the temporary file exists before starting ProgressBarGUI.exe; it may be empty but it must exist\r\n";
demo1 += "> progress.tmp ECHO 0\r\n";
demo1 += ":: Start the first instance of ProgressBarGUI.exe\r\n";
demo1 += "START ProgressBarGUI.exe /Caption:\"Text above . . .\" /Description:\". . . and below the progress bar.\\nAnother line.\\nAnd one more.\" /Timeout:60\r\n";
demo1 += ":: Short delay before starting a second instance -- which should fail by the way\r\n";
demo1 += "TimeOut.exe /T 1 >NUL\r\n";
demo1 += "START ProgressBarGUI.exe /Caption:\"This second instance should not be allowed to run\"\r\n";
demo1 += ":: Feed the progress bar some numbers\r\n";
demo1 += "FOR /L %%A IN (0,5,100) DO (\r\n";
demo1 += "\tTimeOut.exe /T 1 >NUL\r\n";
demo1 += "\t> progress.tmp ECHO.%%A\r\n";
demo1 += ")\r\n";
demo1 += ":: Wait 5 seconds\r\n";
demo1 += " TimeOut.exe /T 5\r\n";
demo1 += ":: Delete the temporary file, which is the \"legitimate\" way to terminate ProgressBarGUI.exe\r\n";
demo1 += " DEL progress.tmp\r\n";
using ( StreamWriter demo1batch = new StreamWriter( "progressbargui_demo1.bat", false ) )
{
demo1batch.Write( demo1 );
}
string demo2 = "@ECHO OFF\r\n";
demo2 += "SETLOCAL ENABLEDELAYEDEXPANSION\r\n";
demo2 += ":: Preparation: count DLLs in %windir%\\system32\r\n";
demo2 += "SET FilesCount=0\r\n";
demo2 += "FOR %%A IN (%windir%\\system32\\*.dll) DO SET /A FilesCount += 1\r\n";
demo2 += ":: Make sure the temporary file exists before starting ProgressBarGUI.exe; it may be empty but it must exist\r\n";
demo2 += "> progress.tmp ECHO 0\r\n";
demo2 += "START ProgressBarGUI.exe /TITLE:\"List %FilesCount% DLLs in %windir%\\system32\" /Max:%FilesCount% /Debug\r\n";
demo2 += ":: Feed the progress bar some numbers\r\n";
demo2 += "SET Count=0\r\n";
demo2 += "FOR %%A IN (%windir%\\system32\\*.dll) DO (\r\n";
demo2 += "\tSET /A Count += 1\r\n";
demo2 += "\tECHO [!Count!] %%A\r\n";
demo2 += "\tREM Only refresh the progressbar every 25 files\r\n";
demo2 += "\tSET /A Test = \"!Count! %% 25\"\r\n";
demo2 += "\tIF !Test! EQU 0 (\r\n";
demo2 += "\t\t> progress.tmp ECHO.!Count!\r\n";
demo2 += "\t\tTimeOut.exe /T 1 >NUL\r\n";
demo2 += "\t)\r\n";
demo2 += ")\r\n";
demo2 += ":: In case the total count is not a multiple of 25\r\n";
demo2 += "> progress.tmp ECHO.!Count!\r\n";
demo2 += ":: Wait 5 seconds\r\n";
demo2 += "TimeOut.exe /T 5\r\n";
demo2 += ":: Delete the temporary file, which is the \"legitimate\" way to terminate ProgressBarGUI.exe\r\n";
demo2 += " DEL progress.tmp\r\nENDLOCAL\r\n";
using ( StreamWriter demo1batch = new StreamWriter( "progressbargui_demo2.bat", false ) )
{
demo1batch.Write( demo2 );
}
MessageBox.Show( string.Format( "Demo batch files \"progressbargui_demo1.bat\" and \"progressbargui_demo2\" were created in the \"{0}\" folder.", Environment.CurrentDirectory ) );
}
static SizeF GetTextSize( string text, Font font )
{
Bitmap bitmap = new Bitmap( width, height );
Graphics graphic = Graphics.FromImage( bitmap );
SizeF stringsize = new SizeF( );
stringsize = graphic.MeasureString( text, font );
return stringsize;
}
private static void Helptext_Click( object sender, EventArgs e )
{
Clipboard.SetText( helptext.Text );
MessageBox.Show( "The help text has been copied to the clipboard", "Help Text Copied" );
}
static void Quit( )
{
try
{
timeouttimer.Stop( );
}
finally
{
System.Windows.Forms.Application.Exit( );
}
}
static void ShowHelp( )
{
string message = string.Format( "ProgressBarGUI, Version {0}\n", progver );
message += "Batch tool to present a GUI style progress bar\n\n";
message += "Usage: PROGRESSBARGUI.EXE [ options ]\n\n";
message += "Options: /CAPTION:text optional text above the progress bar\n";
message += " /DEBUG show actual value and progress in description\n";
message += " /DESCRIPTION:text optional text underneath the progressbar\n";
message += " /FILE:tempfile text file from which to read progress\n";
message += " (default: progress.tmp in current directory)\n";
message += " /HEIGHT:minimumheight minimum window height (default: 100)\n";
message += " /INTERVAL:milliseconds progressbar refresh interval (deafult: 1000)\n";
message += " /MAX:max maximum value for progress bar (default: 100)\n";
message += " /MIN:min minimum value for progress bar (default: 0)\n";
message += " /NOSCALE do not show min and max values next to\n";
message += " progressbar (default: show values)\n";
message += " /POS:\"X,Y\" X,Y position of dialog's top left corner\n";
message += " (default: center dialog in screen)\n";
message += " /SAMPLES write 2 sample batch files, demonstrating the\n";
message += " use of this program, to the current directory.\n";
message += " /TIMEOUT:seconds timeout after which program should exit,\n";
message += " ready or not (1..86400, or 0 for no timeout,\n";
message += " default: 3600 seconds = 1 hour)\n";
message += " /TITLE:text dialog's window title\n";
message += " /WIDTH:width dialog's window width (default: 600)\n\n";
message += "Notes: The program reads the current value from a temporary file, as\n";
message += " specified by the /FILE switch or the default \"progress.tmp\"\n";
message += " in the current directory.\n";
message += " If /DEBUG switch is used, /DESCRIPTION switch will be ignored.\n";
message += " The /HEIGHT switch is not required: if text is added in caption\n";
message += " or description, the dialog window height will automatically be\n";
message += " resized to make it fit.\n";
message += " The dialog window closes when either the temporary file no longer\n";
message += " exists, or the timeout has elapsed, or the window is closed manually.\n";
message += " Only one single instance of this program is allowed to run, an\n";
message += " attempt to start a second instance will return an error message.\n\n";
message += "Credits: Code to allow only single instance of program by michalczerwinski\n";
message += " https://stackoverflow.com/a/6486341\n\n";
message += "Written by Rob van der Woude\n";
message += "https://www.robvanderwoude.com";
Form helpdialog = new Form( )
{
AutoSize = true,
MaximizeBox = false,
Padding = new Padding( 10 ),
SizeGripStyle = SizeGripStyle.Hide,
Text = string.Format( "ProgressBarGUI, Version {0}", progver )
};
helptext = new Label( )
{
AutoSize = true,
Font = new Font( FontFamily.GenericMonospace, 12 ),
Left = 10,
Text = message,
Top = 10
};
helptext.Click += Helptext_Click;
helpdialog.Controls.Add( helptext );
helpdialog.Update( );
helpdialog.TopMost = true;
helpdialog.ShowDialog( );
helpdialog.Activate( );
}
private static void Timeouttimer_Elapsed( object sender, System.Timers.ElapsedEventArgs e )
{
Quit( );
}
static void UpdateValue( )
{
if ( File.Exists( tempfile ) )
{
try
{
using ( StreamReader sr = new StreamReader( tempfile ) )
{
float.TryParse( sr.ReadToEnd( ).Trim( ), out currentvalue );
}
}
catch ( IOException )
{
// ignore file-in-use errors
}
}
int progress = (int)( 100F * ( currentvalue - min ) / range );
if ( debug )
{
labeldescription.Text = string.Format( "Value:\t{0}\nProgress:\t{1}%\n", currentvalue, progress );
}
progressbar.Value = (int)currentvalue;
progressbar.Refresh( );
dialog.Refresh( );
}
delegate void UpdateValueDelegate( );
static void UpdateValueThread( )
{
if ( dialog.InvokeRequired )
{
UpdateValueDelegate uvd = new UpdateValueDelegate( UpdateValue );
try
{
dialog.Invoke( uvd );
}
catch
{
// ignore error while closing
}
}
else
{
UpdateValue( );
}
}
}
}
page last modified: 2024-04-16; loaded in 0.0274 seconds