Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for progressbargui.cs

(view source code of progressbargui.cs as plain text)

  1. using System;
  2. using System.Drawing;
  3. using System.IO;
  4. using System.Text.RegularExpressions;
  5. using System.Threading;
  6. using System.Windows.Forms;
  7.  
  8.  
  9. namespace RobvanderWoude
  10. {
  11. 	internal static class ProgressBarGUI
  12. 	{
  13. 		static readonly string progver = "1.00";
  14.  
  15.  
  16. 		#region Global Variables
  17.  
  18. 		static System.Timers.Timer timeouttimer = new System.Timers.Timer( );
  19. 		static Form dialog = new FormDialog( ) { ShowInTaskbar = false };
  20. 		static ProgressBar progressbar = new ProgressBar( );
  21. 		static Label labeldescription = new Label( );
  22. 		static Label helptext = new Label( );
  23. 		static string tempfile = Path.Combine( Environment.CurrentDirectory, "progress.tmp" ); // file this program reads its progress from
  24. 		static float currentvalue = 0F;
  25. 		static int min = 0;   // minimum value for progress bar
  26. 		static int max = 100; // maximum value for progress bar
  27. 		static int range = 100; // difference between max and min
  28. 		static int height = 100; // height of dialog window
  29. 		static int width = 600;  // width of dialog window
  30. 		static bool debug = false;
  31.  
  32. 		#endregion Global Variables
  33.  
  34.  
  35. 		[STAThread]
  36. 		static void Main( string[] args )
  37. 		{
  38. 			#region Allow Only Single Instance
  39.  
  40. 			// Code to allow only single instance of program by michalczerwinski
  41. 			// https://stackoverflow.com/a/6486341
  42.  
  43. 			bool result = false;
  44. 			string uniqueid = string.Format( "ProgressbarGUI-{0}", Environment.GetEnvironmentVariable( "ComputerName" ) );
  45. 			Mutex mutex = new Mutex( true, uniqueid, out result );
  46.  
  47. 			if ( !result )
  48. 			{
  49. 				MessageBox.Show( "Another instance is running already.", string.Format( "ProgressBarGUI, Version {0}", progver ) );
  50. 				return;
  51. 			}
  52.  
  53. 			#endregion Allow Only Single Instance
  54.  
  55.  
  56. 			#region Initialize Variables
  57.  
  58. 			int interval = 1000; // progress refresh inteval in milliseconds
  59. 			int timeout = 3600;  // program timeout in seconds, 1 hour default
  60. 			int x = (int)( ( Screen.PrimaryScreen.WorkingArea.Width - width ) / 2 );   // X-position of top of dialog window
  61. 			int y = (int)( ( Screen.PrimaryScreen.WorkingArea.Height - height ) / 2 ); // Y-position of left side of dialog window
  62. 			string caption = string.Empty;     // text above progress bar
  63. 			string description = string.Empty; // text below progress bar
  64. 			string title = string.Format( "ProgressBarGUI,  Version {0}", progver );   // text in title bar
  65. 			bool showscale = true;
  66.  
  67. 			#endregion Initialize Variables
  68.  
  69.  
  70. 			#region Parse Command Line
  71.  
  72. 			foreach ( string arg in args )
  73. 			{
  74. 				if ( arg[0] == '/' )
  75. 				{
  76. 					if ( arg.IndexOfAny( ":=".ToCharArray( ) ) > 1 )
  77. 					{
  78. 						string key = arg.Substring( 0, arg.IndexOfAny( ":=".ToCharArray( ) ) ).ToUpper( );
  79. 						string val = arg.Substring( arg.IndexOfAny( ":=".ToCharArray( ) ) + 1 ).Trim( );
  80. 						switch ( key )
  81. 						{
  82. 							case "/CAPTION":
  83. 								caption = val.Replace( "\\n", "\n" ).Replace( "\\t", "\t" );
  84. 								break;
  85. 							case "/DESCRIPTION":
  86. 								description = val.Replace( "\\n", "\n" ).Replace( "\\t", "\t" );
  87. 								break;
  88. 							case "/FILE":
  89. 								if ( !string.IsNullOrWhiteSpace( val ) && !File.Exists( val ) )
  90. 								{
  91. 									if ( Directory.Exists( Directory.GetParent( val ).FullName ) )
  92. 									{
  93. 										File.WriteAllText( val, string.Empty );
  94. 									}
  95. 									else
  96. 									{
  97. 										ShowHelp( );
  98. 										return;
  99. 									}
  100. 								}
  101. 								tempfile = val;
  102. 								break;
  103. 							case "/HEIGHT":
  104. 								if ( !int.TryParse( val, out height ) )
  105. 								{
  106. 									ShowHelp( );
  107. 									return;
  108. 								}
  109. 								break;
  110. 							case "/INTERVAL":
  111. 								if ( !int.TryParse( val, out interval ) )
  112. 								{
  113. 									ShowHelp( );
  114. 									return;
  115. 								}
  116. 								break;
  117. 							case "/MAX":
  118. 								if ( !int.TryParse( val, out max ) )
  119. 								{
  120. 									ShowHelp( );
  121. 									return;
  122. 								}
  123. 								break;
  124. 							case "/MIN":
  125. 								if ( !int.TryParse( val, out min ) )
  126. 								{
  127. 									ShowHelp( );
  128. 									return;
  129. 								}
  130. 								break;
  131. 							case "/POS":
  132. 								Regex regex = new Regex( "^(\\d+)[x,;](\\d+)$" );
  133. 								if ( regex.IsMatch( val ) )
  134. 								{
  135. 									MatchCollection matches = regex.Matches( val );
  136. 									if ( !int.TryParse( matches[0].Groups[1].Value, out width ) )
  137. 									{
  138. 										ShowHelp( );
  139. 										return;
  140. 									}
  141. 									if ( !int.TryParse( matches[0].Groups[2].Value, out height ) )
  142. 									{
  143. 										ShowHelp( );
  144. 										return;
  145. 									}
  146. 								}
  147. 								else
  148. 								{
  149. 									ShowHelp( );
  150. 									return;
  151. 								}
  152. 								break;
  153. 							case "/TIMEOUT":
  154. 								if ( !int.TryParse( val, out timeout ) )
  155. 								{
  156. 									ShowHelp( );
  157. 									return;
  158. 								}
  159. 								if ( timeout < 0 || timeout > 86400 ) // 24h max; 0 means no timeout
  160. 								{
  161. 									ShowHelp( );
  162. 									return;
  163. 								}
  164. 								break;
  165. 							case "/TITLE":
  166. 								title = val;
  167. 								break;
  168. 							case "/WIDTH":
  169. 								if ( !int.TryParse( val, out width ) )
  170. 								{
  171. 									ShowHelp( );
  172. 									return;
  173. 								}
  174. 								break;
  175. 							default:
  176. 								ShowHelp( );
  177. 								return;
  178. 						}
  179. 					}
  180. 					else if ( arg.ToUpper( ) == "/DEBUG" )
  181. 					{
  182. 						debug = true;
  183. 					}
  184. 					else if ( arg.ToUpper( ) == "/NOSCALE" )
  185. 					{
  186. 						showscale = false;
  187. 					}
  188. 					else if ( arg.ToUpper( ) == "/SAMPLES" )
  189. 					{
  190. 						CreateSamples( );
  191. 						ShowHelp( );
  192. 						return;
  193. 					}
  194. 					else
  195. 					{
  196. 						ShowHelp( );
  197. 						return;
  198. 					}
  199. 				}
  200. 			}
  201.  
  202. 			if ( max <= min )
  203. 			{
  204. 				ShowHelp( );
  205. 				return;
  206. 			}
  207. 			range = max - min;
  208.  
  209. 			if ( debug )
  210. 			{
  211. 				description = "Debug Mode";
  212. 			}
  213.  
  214. 			#endregion Parse Command Line
  215.  
  216.  
  217. 			#region Timeout
  218.  
  219. 			if ( timeout > 0 )
  220. 			{
  221. 				timeouttimer.Enabled = true;
  222. 				timeouttimer.Interval = timeout * 1000;
  223. 				timeouttimer.Elapsed += Timeouttimer_Elapsed;
  224. 				timeouttimer.Start( );
  225. 			}
  226.  
  227.  
  228. 			#endregion Timeout
  229.  
  230.  
  231. 			#region Dialog Window
  232.  
  233. 			dialog.ClientSize = new Size( width, height );
  234. 			dialog.Left = x;
  235. 			dialog.MaximizeBox = false;
  236. 			dialog.MinimizeBox = false;
  237. 			dialog.ShowInTaskbar = false;
  238. 			dialog.SizeGripStyle = SizeGripStyle.Hide;
  239. 			dialog.Text = title;
  240. 			dialog.Top = y;
  241. 			dialog.TopMost = true;
  242. 			dialog.WindowState = FormWindowState.Normal;
  243.  
  244. 			int controlswidth = (int)( width * 0.8 );
  245. 			int linespacing = 10;
  246. 			int leftmargin = (int)( width / 10 );
  247. 			int verticaloffset = linespacing;
  248.  
  249. 			#endregion Dialog Window
  250.  
  251.  
  252. 			#region Optional Caption
  253.  
  254. 			if ( !string.IsNullOrWhiteSpace( caption ) )
  255. 			{
  256. 				Label labelcaption = new Label
  257. 				{
  258. 					AutoSize = true,
  259. 					Location = new Point( leftmargin, verticaloffset ),
  260. 					MaximumSize = new Size( controlswidth, (int)( height * 0.8 ) ),
  261. 					Text = caption
  262. 				};
  263. 				labelcaption.Font = new Font( labelcaption.Font.FontFamily, 12 );
  264. 				dialog.Controls.Add( labelcaption );
  265. 				int textheight = (int)GetTextSize( caption, labelcaption.Font ).Height;
  266. 				verticaloffset += textheight + linespacing;
  267. 			};
  268.  
  269. 			#endregion Optional Caption
  270.  
  271.  
  272. 			#region Progress Bar and Scale
  273.  
  274. 			verticaloffset = Math.Max( verticaloffset, 35 );
  275.  
  276. 			if ( showscale )
  277. 			{
  278. 				Label scalemin = new Label
  279. 				{
  280. 					Padding = new Padding( 0, 0, 5, 0 ),
  281. 					Text = min.ToString( ),
  282. 					TextAlign = ContentAlignment.MiddleRight,
  283. 					Width = leftmargin
  284. 				};
  285. 				scalemin.Font = new Font( scalemin.Font.FontFamily, 10 );
  286. 				scalemin.Location = new Point( 0, verticaloffset );
  287. 				dialog.Controls.Add( scalemin );
  288. 			}
  289.  
  290. 			progressbar.Maximum = max;
  291. 			progressbar.Minimum = min;
  292. 			progressbar.Value = (int)( 100F * ( currentvalue - min ) / range );
  293. 			progressbar.Width = controlswidth;
  294. 			progressbar.Location = new Point( leftmargin, verticaloffset );
  295. 			dialog.Controls.Add( progressbar );
  296.  
  297. 			if ( showscale )
  298. 			{
  299. 				Label scalemax = new Label
  300. 				{
  301. 					Padding = new Padding( 5, 0, 0, 0 ),
  302. 					Text = max.ToString( ),
  303. 					TextAlign = ContentAlignment.MiddleLeft,
  304. 					Width = leftmargin
  305. 				};
  306. 				scalemax.Font = new Font( scalemax.Font.FontFamily, 10 );
  307. 				scalemax.Location = new Point( width - scalemax.Width, verticaloffset );
  308. 				dialog.Controls.Add( scalemax );
  309. 			}
  310.  
  311. 			verticaloffset += progressbar.Height + linespacing;
  312. 			dialog.Refresh( );
  313.  
  314. 			#endregion Progress Bar
  315.  
  316.  
  317. 			#region Optional Description
  318.  
  319. 			if ( !string.IsNullOrWhiteSpace( description ) )
  320. 			{
  321. 				labeldescription = new Label
  322. 				{
  323. 					AutoSize = true,
  324. 					Location = new Point( leftmargin, verticaloffset ),
  325. 					MaximumSize = new Size( controlswidth, (int)( height * 0.8 ) ),
  326. 					Text = description
  327. 				};
  328. 				labeldescription.Font = new Font( labeldescription.Font.FontFamily, 10 );
  329. 				dialog.Controls.Add( labeldescription );
  330. 				int textheight = (int)GetTextSize( description, labeldescription.Font ).Height;
  331. 				verticaloffset += textheight;
  332. 			};
  333.  
  334. 			#endregion Optional Description
  335.  
  336.  
  337. 			#region Resize and Show Dialog
  338.  
  339. 			height = Math.Max( height, verticaloffset + 2 * linespacing );
  340. 			dialog.ClientSize = new Size( width, height );
  341. 			dialog.Visible = true;
  342. 			dialog.Activate( );
  343. 			dialog.Show( );
  344.  
  345. 			#endregion Resize and Show Dialog
  346.  
  347.  
  348. 			#region Update Progress Bar
  349.  
  350. 			while ( File.Exists( tempfile ) )
  351. 			{
  352. 				Thread.Sleep( interval );
  353. 				Thread thread = new Thread( UpdateValueThread );
  354. 				thread.Start( );
  355. 				Application.DoEvents( );
  356. 			}
  357.  
  358. 			#endregion Update Progress Bar
  359.  
  360.  
  361. 			Quit( );
  362. 			return;
  363. 		}
  364.  
  365.  
  366. 		static void CreateSamples( )
  367. 		{
  368. 			string demo1 = ":: Make sure the temporary file exists before starting ProgressBarGUI.exe; it may be empty but it must exist\r\n";
  369. 			demo1 += "> progress.tmp ECHO 0\r\n";
  370. 			demo1 += ":: Start the first instance of ProgressBarGUI.exe\r\n";
  371. 			demo1 += "START ProgressBarGUI.exe /Caption:\"Text above . . .\" /Description:\". . . and below the progress bar.\\nAnother line.\\nAnd one more.\" /Timeout:60\r\n";
  372. 			demo1 += ":: Short delay before starting a second instance -- which should fail by the way\r\n";
  373. 			demo1 += "TimeOut.exe /T 1 >NUL\r\n";
  374. 			demo1 += "START ProgressBarGUI.exe /Caption:\"This second instance should not be allowed to run\"\r\n";
  375. 			demo1 += ":: Feed the progress bar some numbers\r\n";
  376. 			demo1 += "FOR /L %%A IN (0,5,100) DO (\r\n";
  377. 			demo1 += "\tTimeOut.exe /T 1 >NUL\r\n";
  378. 			demo1 += "\t> progress.tmp ECHO.%%A\r\n";
  379. 			demo1 += ")\r\n";
  380. 			demo1 += ":: Wait 5 seconds\r\n";
  381. 			demo1 += " TimeOut.exe /T 5\r\n";
  382. 			demo1 += ":: Delete the temporary file, which is the \"legitimate\" way to terminate ProgressBarGUI.exe\r\n";
  383. 			demo1 += " DEL progress.tmp\r\n";
  384. 			using ( StreamWriter demo1batch = new StreamWriter( "progressbargui_demo1.bat", false ) )
  385. 			{
  386. 				demo1batch.Write( demo1 );
  387. 			}
  388.  
  389. 			string demo2 = "@ECHO OFF\r\n";
  390. 			demo2 += "SETLOCAL ENABLEDELAYEDEXPANSION\r\n";
  391. 			demo2 += ":: Preparation: count DLLs in %windir%\\system32\r\n";
  392. 			demo2 += "SET FilesCount=0\r\n";
  393. 			demo2 += "FOR %%A IN (%windir%\\system32\\*.dll) DO SET /A FilesCount += 1\r\n";
  394. 			demo2 += ":: Make sure the temporary file exists before starting ProgressBarGUI.exe; it may be empty but it must exist\r\n";
  395. 			demo2 += "> progress.tmp ECHO 0\r\n";
  396. 			demo2 += "START ProgressBarGUI.exe /TITLE:\"List %FilesCount% DLLs in %windir%\\system32\" /Max:%FilesCount% /Debug\r\n";
  397. 			demo2 += ":: Feed the progress bar some numbers\r\n";
  398. 			demo2 += "SET Count=0\r\n";
  399. 			demo2 += "FOR %%A IN (%windir%\\system32\\*.dll) DO (\r\n";
  400. 			demo2 += "\tSET /A Count += 1\r\n";
  401. 			demo2 += "\tECHO [!Count!]    %%A\r\n";
  402. 			demo2 += "\tREM Only refresh the progressbar every 25 files\r\n";
  403. 			demo2 += "\tSET /A Test = \"!Count! %% 25\"\r\n";
  404. 			demo2 += "\tIF !Test! EQU 0 (\r\n";
  405. 			demo2 += "\t\t> progress.tmp ECHO.!Count!\r\n";
  406. 			demo2 += "\t\tTimeOut.exe /T 1 >NUL\r\n";
  407. 			demo2 += "\t)\r\n";
  408. 			demo2 += ")\r\n";
  409. 			demo2 += ":: In case the total count is not a multiple of 25\r\n";
  410. 			demo2 += "> progress.tmp ECHO.!Count!\r\n";
  411. 			demo2 += ":: Wait 5 seconds\r\n";
  412. 			demo2 += "TimeOut.exe /T 5\r\n";
  413. 			demo2 += ":: Delete the temporary file, which is the \"legitimate\" way to terminate ProgressBarGUI.exe\r\n";
  414. 			demo2 += " DEL progress.tmp\r\nENDLOCAL\r\n";
  415. 			using ( StreamWriter demo1batch = new StreamWriter( "progressbargui_demo2.bat", false ) )
  416. 			{
  417. 				demo1batch.Write( demo2 );
  418. 			}
  419.  
  420. 			MessageBox.Show( string.Format( "Demo batch files \"progressbargui_demo1.bat\" and \"progressbargui_demo2\" were created in the \"{0}\" folder.", Environment.CurrentDirectory ) );
  421. 		}
  422.  
  423.  
  424. 		static SizeF GetTextSize( string text, Font font )
  425. 		{
  426. 			Bitmap bitmap = new Bitmap( width, height );
  427. 			Graphics graphic = Graphics.FromImage( bitmap );
  428. 			SizeF stringsize = new SizeF( );
  429. 			stringsize = graphic.MeasureString( text, font );
  430. 			return stringsize;
  431. 		}
  432.  
  433.  
  434. 		private static void Helptext_Click( object sender, EventArgs e )
  435. 		{
  436. 			Clipboard.SetText( helptext.Text );
  437. 			MessageBox.Show( "The help text has been copied to the clipboard", "Help Text Copied" );
  438. 		}
  439.  
  440.  
  441. 		static void Quit( )
  442. 		{
  443. 			try
  444. 			{
  445. 				timeouttimer.Stop( );
  446. 			}
  447. 			finally
  448. 			{
  449. 				System.Windows.Forms.Application.Exit( );
  450. 			}
  451. 		}
  452.  
  453.  
  454. 		static void ShowHelp( )
  455. 		{
  456. 			string message = string.Format( "ProgressBarGUI,  Version {0}\n", progver );
  457. 			message += "Batch tool to present a GUI style progress bar\n\n";
  458. 			message += "Usage:   PROGRESSBARGUI.EXE  [ options ]\n\n";
  459. 			message += "Options: /CAPTION:text            optional text above the progress bar\n";
  460. 			message += "         /DEBUG                   show actual value and progress in description\n";
  461. 			message += "         /DESCRIPTION:text        optional text underneath the progressbar\n";
  462. 			message += "         /FILE:tempfile           text file from which to read progress\n";
  463. 			message += "                                  (default: progress.tmp in current directory)\n";
  464. 			message += "         /HEIGHT:minimumheight    minimum window height (default: 100)\n";
  465. 			message += "         /INTERVAL:milliseconds   progressbar refresh interval (deafult: 1000)\n";
  466. 			message += "         /MAX:max                 maximum value for progress bar (default: 100)\n";
  467. 			message += "         /MIN:min                 minimum value for progress bar (default: 0)\n";
  468. 			message += "         /NOSCALE                 do not show min and max values next to\n";
  469. 			message += "                                  progressbar (default: show values)\n";
  470. 			message += "         /POS:\"X,Y\"               X,Y position of dialog's top left corner\n";
  471. 			message += "                                  (default: center dialog in screen)\n";
  472. 			message += "         /SAMPLES                 write 2 sample batch files, demonstrating the\n";
  473. 			message += "                                  use of this program, to the current directory.\n";
  474. 			message += "         /TIMEOUT:seconds         timeout after which program should exit,\n";
  475. 			message += "                                  ready or not (1..86400, or 0 for no timeout,\n";
  476. 			message += "                                  default: 3600 seconds = 1 hour)\n";
  477. 			message += "         /TITLE:text              dialog's window title\n";
  478. 			message += "         /WIDTH:width             dialog's window width (default: 600)\n\n";
  479. 			message += "Notes:   The program reads the current value from a temporary file, as\n";
  480. 			message += "         specified by the /FILE switch or the default \"progress.tmp\"\n";
  481. 			message += "         in the current directory.\n";
  482. 			message += "         If /DEBUG switch is used, /DESCRIPTION switch will be ignored.\n";
  483. 			message += "         The /HEIGHT switch is not required: if text is added in caption\n";
  484. 			message += "         or description, the dialog window height will automatically be\n";
  485. 			message += "         resized to make it fit.\n";
  486. 			message += "         The dialog window closes when either the temporary file no longer\n";
  487. 			message += "         exists, or the timeout has elapsed, or the window is closed manually.\n";
  488. 			message += "         Only one single instance of this program is allowed to run, an\n";
  489. 			message += "         attempt to start a second instance will return an error message.\n\n";
  490. 			message += "Credits: Code to allow only single instance of program by michalczerwinski\n";
  491. 			message += "         https://stackoverflow.com/a/6486341\n\n";
  492. 			message += "Written by Rob van der Woude\n";
  493. 			message += "https://www.robvanderwoude.com";
  494.  
  495. 			Form helpdialog = new Form( )
  496. 			{
  497. 				AutoSize = true,
  498. 				MaximizeBox = false,
  499. 				Padding = new Padding( 10 ),
  500. 				SizeGripStyle = SizeGripStyle.Hide,
  501. 				Text = string.Format( "ProgressBarGUI, Version {0}", progver )
  502. 			};
  503. 			helptext = new Label( )
  504. 			{
  505. 				AutoSize = true,
  506. 				Font = new Font( FontFamily.GenericMonospace, 12 ),
  507. 				Left = 10,
  508. 				Text = message,
  509. 				Top = 10
  510. 			};
  511. 			helptext.Click += Helptext_Click;
  512. 			helpdialog.Controls.Add( helptext );
  513. 			helpdialog.Update( );
  514. 			helpdialog.TopMost = true;
  515. 			helpdialog.ShowDialog( );
  516. 			helpdialog.Activate( );
  517. 		}
  518.  
  519.  
  520. 		private static void Timeouttimer_Elapsed( object sender, System.Timers.ElapsedEventArgs e )
  521. 		{
  522. 			Quit( );
  523. 		}
  524.  
  525.  
  526. 		static void UpdateValue( )
  527. 		{
  528. 			if ( File.Exists( tempfile ) )
  529. 			{
  530. 				try
  531. 				{
  532. 					using ( StreamReader sr = new StreamReader( tempfile ) )
  533. 					{
  534. 						float.TryParse( sr.ReadToEnd( ).Trim( ), out currentvalue );
  535. 					}
  536. 				}
  537. 				catch ( IOException )
  538. 				{
  539. 					// ignore file-in-use errors
  540. 				}
  541. 			}
  542. 			int progress = (int)( 100F * ( currentvalue - min ) / range );
  543. 			if ( debug )
  544. 			{
  545. 				labeldescription.Text = string.Format( "Value:\t{0}\nProgress:\t{1}%\n", currentvalue, progress );
  546. 			}
  547. 			progressbar.Value = (int)currentvalue;
  548. 			progressbar.Refresh( );
  549. 			dialog.Refresh( );
  550. 		}
  551.  
  552.  
  553. 		delegate void UpdateValueDelegate( );
  554.  
  555.  
  556. 		static void UpdateValueThread( )
  557. 		{
  558. 			if ( dialog.InvokeRequired )
  559. 			{
  560. 				UpdateValueDelegate uvd = new UpdateValueDelegate( UpdateValue );
  561. 				try
  562. 				{
  563. 					dialog.Invoke( uvd );
  564. 				}
  565. 				catch
  566. 				{
  567. 					// ignore error while closing
  568. 				}
  569. 			}
  570. 			else
  571. 			{
  572. 				UpdateValue( );
  573. 			}
  574. 		}
  575. 	}
  576. }

page last modified: 2024-04-16; loaded in 0.0353 seconds