public void DoAddTasks(bool abStartup) { // Do this at most once per minute to avoid running the same task twice in rapid succession. if ( !abStartup && (!moProfile.ContainsKey("-AddTasks") || DateTime.Now < mdtPreviousAddTasksStarted.AddMinutes(1)) ) return; mdtPreviousAddTasksStarted = DateTime.Now; try { if ( null == moAddTasksProfile ) { moAddTasksProfile = moProfile.oProfile("-AddTasks").oOneKeyProfile("-Task"); moAddTasksProcessArray = new Process[moAddTasksProfile.Count]; } for (int i=0; i < moAddTasksProfile.Count; ++i) { // Convert the current task from a command-line string to a profile oject. tvProfile loAddTask = new tvProfile(moAddTasksProfile[i].ToString()); bool lbDoTask = false; if ( abStartup ) { lbDoTask = loAddTask.bValue("-OnStartup", false); // Reset pause timer to allow other tasks to run without delay after startup. mdtPreviousAddTasksStarted = DateTime.Now.AddMinutes(-1); } else { DateTime ldtTaskStartTime = loAddTask.dtValue("-StartTime", DateTime.MinValue); string lsTaskDaysOfWeek = loAddTask.sValue("-StartDays", ""); // If -StartTime is within the current minute, start the task. // If -StartDays is specified, run the task on those days only. lbDoTask = DateTime.MinValue != ldtTaskStartTime && (int)mdtPreviousAddTasksStarted.TimeOfDay.TotalMinutes == (int)ldtTaskStartTime.TimeOfDay.TotalMinutes && ("" == lsTaskDaysOfWeek || this.bListIncludesDay(lsTaskDaysOfWeek, mdtPreviousAddTasksStarted)); } if ( lbDoTask ) { string lsCommandEXE = loAddTask.sValue("-CommandEXE", "add task -CommandEXE missing"); Process loProcess = new Process(); loProcess.ErrorDataReceived += new DataReceivedEventHandler(this.BackupProcessOutputHandler); loProcess.OutputDataReceived += new DataReceivedEventHandler(this.BackupProcessOutputHandler); loProcess.StartInfo.FileName = lsCommandEXE; loProcess.StartInfo.Arguments = loAddTask.sValue("-CommandArgs", ""); loAddTask.bValue("-UnloadOnExit", false); // The following subset of parameters are overridden when -TimeoutMinutes is set. This is // necessary to guarantee IO redirection is handled properly (ie. output goes to the log). bool lbWaitForExitOverride = (loAddTask.iValue("-TimeoutMinutes", 0) > 0); loProcess.StartInfo.CreateNoWindow = lbWaitForExitOverride | loAddTask.bValue("-CreateNoWindow", false); loProcess.StartInfo.UseShellExecute = !lbWaitForExitOverride & loAddTask.bValue("-UseShellExecute", true); loProcess.StartInfo.RedirectStandardInput = lbWaitForExitOverride | loAddTask.bValue("-RedirectStandardInput", false); loProcess.StartInfo.RedirectStandardError = lbWaitForExitOverride | loAddTask.bValue("-RedirectStandardError", false); loProcess.StartInfo.RedirectStandardOutput = lbWaitForExitOverride | loAddTask.bValue("-RedirectStandardOutput", false); moAddTasksProcessArray[i] = loProcess; try { if ( !loAddTask.bValue("-OnStartup", false) ) { this.LogIt(String.Format("Starting Task: {0}", loAddTask.sCommandLine())); loProcess.Start(); // Start output to console also. if ( loProcess.StartInfo.RedirectStandardError ) loProcess.BeginErrorReadLine(); if ( loProcess.StartInfo.RedirectStandardOutput ) loProcess.BeginOutputReadLine(); if ( lbWaitForExitOverride ) { // Wait the timeout period, then call "WaitForExit()" to flush the output steams. if ( loProcess.WaitForExit(60000 * loAddTask.iValue("-TimeoutMinutes", 0)) ) loProcess.WaitForExit(); // Stop output to console. if ( loProcess.StartInfo.RedirectStandardError ) loProcess.CancelErrorRead(); if ( loProcess.StartInfo.RedirectStandardOutput ) loProcess.CancelOutputRead(); loProcess.Close(); } } else { bool lbFound = false; string lsWindowTitle = loAddTask.sValue("-CommandWindowTitle", ""); Process[] loProcessesArray = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(loProcess.StartInfo.FileName)); // If there's exactly one matching process and no given window title to compare, we're done. lbFound = (1 == loProcessesArray.Length && "" == lsWindowTitle ); // If no window title has been given to compare, there's nothing else to do. if ( !lbFound && "" != lsWindowTitle ) { // If no matching processes have been found so far, get them all to compare. if ( 0 == loProcessesArray.Length ) loProcessesArray = Process.GetProcesses(); // Since a window title has been provided, it must be compared to the process(es) found. // Wildcards are permitted, but only at the end of titles. We stop at the first match. foreach (Process loProcessEntry in loProcessesArray) if ( loProcessEntry.MainWindowTitle.StartsWith(lsWindowTitle.Replace("*", "")) ) { lbFound = true; break; } } // Don't start -OnStartup processes that have already been started. if ( lbFound ) { // The process has "already started" if there is only one with the // same EXE or multiple EXEs with one having the same window title. this.LogIt(String.Format("Already running, task not started: {0}", loAddTask.sCommandLine())); } else { this.LogIt(String.Format("Starting Task: {0}", loAddTask.sCommandLine())); loProcess.Start(); // Start output to console also. if ( loProcess.StartInfo.RedirectStandardError ) loProcess.BeginErrorReadLine(); if ( loProcess.StartInfo.RedirectStandardOutput ) loProcess.BeginOutputReadLine(); if ( lbWaitForExitOverride ) { // Wait the timeout period, then call "WaitForExit()" to flush the output steams. if ( loProcess.WaitForExit(60000 * loAddTask.iValue("-TimeoutMinutes", 0)) ) loProcess.WaitForExit(); // Stop output to console. if ( loProcess.StartInfo.RedirectStandardError ) loProcess.CancelErrorRead(); if ( loProcess.StartInfo.RedirectStandardOutput ) loProcess.CancelOutputRead(); loProcess.Close(); } } } } catch (Exception ex) { this.ShowError(ex.Message, String.Format("Failed starting task: {0}", lsCommandEXE)); } } } } catch (Exception ex) { this.ShowError(ex.Message, "Add Tasks Failed"); } }
static void Main(string[] args) { DoGoPcBackup loMain = null; try { tvProfile loProfile = new tvProfile(args); bool lbFirstInstance; Mutex loMutex = new Mutex(false, "Global\\" + Application.ResourceAssembly.GetName().Name, out lbFirstInstance); if ( !lbFirstInstance ) DoGoPcBackup.ActivateAlreadyRunningInstance(args, loProfile); if ( lbFirstInstance && !loProfile.bExit ) { loProfile.GetAdd("-Help", @" Introduction This utility performs file backups and file cleanups in the background. It also acts as its own scheduler. First, it checks for files to be removed on a given schedule. Then it runs a backup of your files automatically. There is no need to use a job scheduler unless this software is running on a server computer that has no regular user activity (see -NoPrompts and -RunOnce below). You provide various file specifications (ie. locations of the files to backup and to cleanup) as well as file age limits for the files to cleanup. The rest is automatic. This utility will run in the background unless its timer is turned off. Its simple user interface (UI) is usually minimized to the system tray. Command-Line Usage Open this utility's profile file to see additional options available. It is usually located in the same folder as ""{EXE}"" and has the same name with "".txt"" added (see ""{INI}""). Profile file options can be overridden with command-line arguments. The keys for any ""-key=value"" pairs passed on the command-line must match those that appear in the profile (with the exception of the ""-ini"" key). For example, the following invokes the use of an alternative profile file: {EXE} -ini=NewProfile.txt This tells the software to run in automatic mode: {EXE} -AutoStart Author: George Schiro ([email protected]) Date: 7/3/2013 Options and Features The main options for this utility are listed below with their default values. A brief description of each feature follows. -AddTasks= NO DEFAULT VALUE Each added task has its own profile: -Task= NO DEFAULT VALUE -CommandEXE= NO DEFAULT VALUE This is the path\file specification of the task executable to be run. -CommandArgs="""" This is the list of arguments passed to the task executable. -CommandWindowTitle="""" This is the main window title for this instance of the task executable. This will be used to determine, during startup, if the task is already running (when multiple instances of the same executable are found). -CreateNoWindow=False Set this switch True and nothing will be displayed when the task runs. -OnStartup=False Set this switch True and the task will start each time ""{EXE}"" starts. If the task EXE is already running, it will not be started again. -StartTime= NO DEFAULT VALUE Set this to the time of day to run the task (eg. 3:00am, 9:30pm, etc). -StartDays="""" Set this to days of the week to run the task (eg. Monday, Friday, etc). This value may include a comma-separated list of days as well as ranges of days. Leave this blank and the task will run every day at -StartTime. -TimeoutMinutes=0 Set this to a number greater than zero to have the task run to completion and have all output properly logged before proceeding to the next task (ie. -CreateNoWindow will be set and IO redirection will be handled). -UnloadOnExit=False Set this switch True and the task executable will be removed from memory (if it's still running) when ""{EXE}"" exits. Here's an example: -AddTasks=[ -Task= -OnStartup -CommandEXE=http://xkcd.com -Task= -StartTime=6:00am -CommandEXE=shutdown.exe -CommandArgs=/r /t 60 -Task= -StartTime=7:00am -StartDays=""Mon-Wed,Friday,Saturday"" -CommandEXE=""C:\Program Files\Calibre2\calibre.exe"" -Note=Fetch NY Times after 6:30am -Task= -StartTime=8:00am -StartDays=""Sunday"" -CommandEXE=""C:\Program Files\Calibre2\calibre.exe"" -Note=Fetch NY Times after 7:30am Sundays -AddTasks=] -ArchivePath=C:\Archive This is the destination folder of the backup output files unless overridden in -BackupSet (see below). -AutoStart=True This tells the software to run in automatic mode. Set this switch False and the main loop in the UI will only start manually. The software will also vacate memory after the UI window closes. This is the timer switch. -BackupBeginScriptEnabled=True Set this switch False to skip running the ""backup begin"" script. -BackupBeginScriptHelp= SEE PROFILE FOR DEFAULT VALUE This is the default content of the DOS script that is initially written to -BackupBeginScriptPathFile and run before each backup starts. It contains a description of the command-line arguments passed to the script at runtime. -BackupBeginScriptInit=False Set this switch True and the ""backup begin"" script will be automatically overwritten from the content of -BackupBeginScriptHelp. Once used this switch will be reset to False. Note: the content of -BackupBeginScriptHelp will also be overwritten from the default value embedded in the executable file. -BackupBeginScriptPathFile=GoPcBackupBegin.cmd This DOS shell script is run before each backup starts. Edit the contents of the file or point this parameter to another file. If you delete the file, it will be recreated from the content found in -BackupBeginScriptHelp (see above). -BackupDoneScriptEnabled=True Set this switch False to skip running the ""backup done"" script. -BackupDoneScriptHelp= SEE PROFILE FOR DEFAULT VALUE This is the default content of the DOS script that is initially written to -BackupDoneScriptPathFile and run after each successful backup. It contains a description of the command-line arguments passed to the script at runtime. -BackupDoneScriptInit=False Set this switch True and the ""backup done"" script will be automatically overwritten from the content of -BackupDoneScriptHelp. Once used this switch will be reset to False. Note: the content of -BackupDoneScriptHelp will also be overwritten from the default value embedded in the executable file. -BackupDoneScriptPathFile=GoPcBackupDone.cmd This DOS shell script is run after each successful backup completes. You can edit the contents of the file or point this parameter to another file. If you delete the file, it will be recreated from the content found in -BackupDoneScriptHelp (see above). -BackupFailedScriptEnabled=True Set this switch False to skip running the ""backup failed"" script. -BackupFailedScriptHelp= SEE PROFILE FOR DEFAULT VALUE This is the default content of the DOS script that is initially written to -BackupFailedScriptPathFile and run after each failed backup. It contains a description of the command-line arguments passed to the script at runtime. -BackupFailedScriptInit=False Set this switch True and the ""backup failed"" script will be automatically overwritten from the content of -BackupFailedScriptHelp. Once used this switch will be reset to False. Note: the content of -BackupFailedScriptHelp will also be overwritten from the default value embedded in the executable file. -BackupFailedScriptPathFile=GoPcBackupFailed.cmd This DOS shell script is run after a backup fails to complete. You can edit the contents of the file or point this parameter to another file. If you delete the file, it will be recreated from the content found in -BackupFailedScriptHelp (see above). -BackupDriveToken=(This is my GoPC backup drive.) This is the filename looked for at the root of every storage device attached to the computer. If found, a copy of the backup will be written there. -BackupFileSpec=* This wildcard is appended to folders to backup (see -FolderToBackup). -BackupFiles=True Set this switch False to disable backups (ie. do file cleanups only). -BackupOutputExtension=.zip This is appended to all backup path\filenames (see -OutputFilename below). -BackupOutputFilenameDateFormat=-yyyy-MM-dd This format string is used to form the variable part of each backup output filename. It is inserted between the filename and the extension (see -OutputFilename below and -BackupOutputExtension above). -BackupSet=""One of many file sets to backup goes here."" Each file backup set has its own profile: -ArchivePath= INHERITED This is the destination folder of the backup output files. If provided, this value will override the parent -ArchivePath (see above). -BackupFileSpec= INHERITED This wildcard is appended to each folder to backup. If provided, it will override the parent -BackupFileSpec (see above). -FolderToBackup=""One of many folders to backup goes here."" This is the full path\file specification of a folder to backup. This parameter can appear multiple times in each backup set. Instead of an entire folder you can use a path\file pattern like ""C:\Folder\File?.*"" to backup a subset of files or a single file. -OutputFilename=Files This is the backup output filename with no path and no extension. This parameter will be combined with -BackupOutputExtension, -ArchivePath and -BackupOutputFilenameDateFormat to produce a full backup output path\file specification. -BackupTime=12:00 AM This is the time each day that the backup starts. -BackupTimeMinsPerTick=15 This determines how many minutes the backup time changes with each tick of the backup time selection slider in the UI. -CleanupFiles=True Set this switch False to disable cleanups (ie. do file backups only). -CleanupLoopSleepMS=1 This is the number of milliseconds of process thread sleep time between file deletions. The default of 1 ms should result in rapid deletions. You can increase this value if you are concerned that the UI is not responsive enough or the process is using too much CPU while deleting. -CleanupSet=""One of many file sets to cleanup goes here."" Each file cleanup set has its own profile: -AgeDays=365000 This is the maximum file age in days. It is 1000 years by default. Only files older than this will be considered for deletion. -ApplyDeletionLimit=True Set this switch False and the cleanup process won't limit deletions to files regularly replaced by newer files. Without such a limit a large collection of very old files may be wiped out in one run. With the limit in place an old file will be removed only if a newer file exists to replace it. In other words, with this switch set, there should always be as many files retained as there are days in ""-AgeDays"" multiplied by the frequency of backups (1440 divided by -MainLoopMinutes, see below). -CleanupHidden=False Set this switch True and the file cleanup process will include hidden files as well. If -Recurse is also True (see below), hidden folders will also be removed. -CleanupReadOnly=False Set this switch True and the file cleanup process will include read-only files as well. If -Recurse is also True (see below), read-only folders will also be removed. -DeletedFileListDateTimeType=LastWriteTime Each file has 3 timestamps: CreationTime, LastAccessTime and LastWriteTime. The default (""LastWriteTime"") is the file modification date. -FilesToDelete=""One of many path\file specifications goes here."" These are the files evaluated for deletion based on their age (see -AgeDays above). Wildcards are expected but not required (you can reference a single file if you like). -Recurse=False Set this switch True and the file cleanup process will recurse through all subdirectories starting from the path of the given -FilesToDelete (see above) looking for files to remove with the same file specification found in the -FilesToDelete parameter. -RecurseFolder="""" This identifies subfolders found within the folder defined by -FilesToDelete (see above). These subfolders can occur at any level of the directory hierarchy above the path provided in -FilesToDelete. If -RecurseFolder is non-empty, only files found within the -FilesToDelete path and also within any subfolder (from the given path upward) with the given subfolder name will be evaluated for removal. This parameter has the effect of greatly limiting the recursion. BE CAREFUL! If you don't provide this parameter or you specify an empty value, all files matching the file specification in the given -FilesToDelete at every level of the directory structure (starting at the path found in -FilesToDelete) will be evaluated for removal. IMPORTANT! Every empty folder found within the given recursive subfolder (at any level above the starting path) will also be removed. Here's a single line example: -CleanupSet= -AgeDays=90 -FilesToDelete=C:\WINDOWS\TEMP\*.* Here's a multi-line example: -CleanupSet=[ -AgeDays=60 -FilesToDelete=C:\WINDOWS\TEMP\*.* -FilesToDelete=C:\WINDOWS\system32\*.log -FilesToDelete=C:\WINDOWS\system32\LogFiles\W3SVC1\*.log -FilesToDelete=C:\Documents and Settings\Administrator\Local Settings\Temp\*.* -FilesToDelete=C:\Program Files\GoPcBackup\GoPcBackupList*.txt -CleanupSet=] -CleanupSet=[ -AgeDays=14 -FilesToDelete=C:\Documents and Settings\*.* -CleanupHidden -CleanupReadOnly -Recurse -RecurseFolder=Local Settings\Temp -CleanupSet=] -CleanupSet=[ -AgeDays=90 -FilesToDelete=C:\Archive\*.* -CleanupSet=] -DeletedFileListDateFormat=-yyyy-MM-dd This format string is used to form the variable part of each deleted file list output filename (see -DeletedFileListOutputPathFile below). It is inserted between the filename and the extension. -DeletedFileListOutputColumnFormat={0, -22:MM-dd-yyyy hh:mm:ss tt} {1, 13:#,#} {2} This format string specifies the layout of the three file attribute output columns (last modified date, file size and path\file name, resp.) for each file in the deleted file list (see -DeletedFileListOutputPathFile below). -DeletedFileListOutputColumnHeaderArray=Deleted File Time,File Size,Former File Location This array of names specifies the column headers of the deleted file list (see -DeletedFileListOutputPathFile below). -DeletedFileListOutputHeader={0, -10:MM-dd-yyyy} File Cleanup List This format string specifies the layout of the deleted file list output file header (see -DeletedFileListOutputPathFile below). -DeletedFileListOutputPathFile=Logs\DeletedFileList.txt This is the output path\file that will contain the list of deleted files. The profile file name will be prepended to the default and the current date (see -DeletedFileListDateFormat) will be inserted between the filename and the extension. -FetchSource=False Set this switch True to fetch the source code for this utility from the EXE. Look in the containing folder for a ZIP file with the full project sources. -Help= SEE PROFILE FOR DEFAULT VALUE This help text. -KillProcessOrderlyWaitSecs=30 This is the maximum number of seconds given to a process after a ""close"" command is given before the process is forcibly terminated. -KillProcessForcedWaitMS=1000 This is the maximum milliseconds to wait while force killing a process. -LogEntryDateTimeFormatPrefix""yyyy-MM-dd hh:mm:ss:fff tt "" This format string is used to prepend a timestamp prefix to each log entry in the process log file (see -LogPathFile below). -LogFileDateFormat=-yyyy-MM-dd This format string is used to form the variable part of each backup / cleanup log file output filename (see -LogPathFile below). It is inserted between the filename and the extension. -LogPathFile=Logs\Log.txt This is the output path\file that will contain the backup / cleanup process log. The profile file name will be prepended to the default and the current date (see -LogFileDateFormat above) will be inserted between the filename and the extension. -MainLoopMinutes=1440 This is the number of minutes until the next run. One day is the default. -MainLoopSleepMS=100 This is the number of milliseconds of process thread sleep wait time between loops. The default of 100 ms should be a happy medium between a responsive overall UI and a responsive process timer UI. You can increase this value if you are concerned that the timer UI is using too much CPU while waiting. -NoPrompts=False Set this switch True and all pop-up prompts will be suppressed. Messages are written to the log instead (see -LogPathFile above). You must use this switch whenever the software is run via a server computer batch job or job scheduler (ie. where no user interaction is permitted). -PreviousBackupDevicesMissing=False This is the True or False ""Devices Missing"" status of the previous backup run. If True, at least one external device was missing when the backup ran. -PreviousBackupOk= NO DEFAULT VALUE This is the True or False ""Ok"" status of the previous backup / cleanup run. -PreviousBackupTime= NO DEFAULT VALUE This is the completion timestamp of the previous backup / cleanup run. -RunOnce=False Set this switch True to run this utility one time only (with no UI) then shutdown automatically thereafter. This switch is useful if the utility is run in a batch process or if it is run by a server job scheduler. -SaveProfile=True Set this switch False to prevent saving to the profile file by the backup software itself. This is not recommended since backup status information is written to the profile after each backup runs. -SelectedBackupDevices= NO DEFAULT VALUE This is the list of selected backup devices as human readable text. -SelectedBackupDevicesBitField=0 (0 means not yet set) This is the list of selected backup devices as a bit field. All bit fields have a leading 1 bit to preserve leading zeros. The second bit starts the device list (ie. drive letter list). Drive C: is not available as a backup device. So the second bit identifies drive D:. -ShowBackupBeginScriptErrors=True Set this switch False to suppress the pop-up display of ""backup begin"" script errors (see -BackupBeginScriptPathFile above). -ShowBackupDoneScriptErrors=True Set this switch False to suppress the pop-up display of ""backup done"" script errors (see -BackupDoneScriptPathFile above). -ShowDeletedFileList=False Set this switch True and the list of deleted files will be displayed in a pop-up window. -ShowProfile=False Set this switch True to immediately display the entire contents of the profile file at startup in command-line format. This is sometimes helpful to diagnose problems. -UseConnectVirtualMachineHost=False Set this switch True to force a connection to the virtual machine share archive before each backup starts (ie. during the ""backup begin"" script). -UseVirtualMachineHostArchive=False Set this switch True and code will be added to the ""backup done"" script (see -BackupDoneScriptPathFile above) to copy backups to your virtual machine host computer (assuming you have one). Alternatively, any network share can be referenced here for a similar purpose. -VirtualMachineHostArchivePath= NO DEFAULT VALUE This value is used within the ""backup done"" script to copy backups to the virtual machine host share (see -UseVirtualMachineHostArchive above). You may want to reference your VM host by IP address rather than by name. Doing so is often more reliable than using net bios names on your local area network. -VirtualMachineHostPassword= NO DEFAULT VALUE This value is the password used within the ""backup begin"" script to log into the virtual machine host share (see -UseConnectVirtualMachineHost above). -VirtualMachineHostUsername= NO DEFAULT VALUE This value is the username used within the ""backup begin"" script to log into the virtual machine host share (see -UseConnectVirtualMachineHost above). -XML_Profile=False Set this switch True to change the profile file from command-line format to XML format. -ZipToolEXE=7z.exe This is the ZIP tool executable that performs the backup compression. -ZipToolEXEargs=a -ssw ""{{BackupOutputPathFile}}"" @""{{BackupPathFiles}}"" -w""{{BackupOutputPath}}"" These are command-line arguments passed to the ZIP compression tool (see -ZipToolEXE above). The tokens (in curly brackets) are self-evident. They are replaced at runtime. -ZipToolEXEargsMore= NO DEFAULT VALUE These are additional command line arguments for the ZIP tool. Using this parameter makes it easier to add functionality without changing the existing command line. A typical example would be to supply an encryption password on the command line to ""{EXE}"" itself. -ZipToolFileListFileDateFormat=-yyyy-MM-dd This format string is used to form the variable part of each file list output filename (see -ZipToolFileListPathFile below). It is inserted between the filename and the extension. -ZipToolFileListPathFile=FileLists\ZipFileList.txt This is the file used to store the list of filenames to be compressed. The profile file name will be prepended to the default and the current date (see -ZipToolFileListFileDateFormat above) will be inserted (with a GUID) between the filename and the extension. -ZipToolLastRunCmdPathFile=Run Last Backup.cmd This is a script file (text), which contains a copy of the last ZIP tool command line executed. Notes: There may be various other settings that can be adjusted also (user interface settings, etc). See the profile file (""{INI}"") for all available options. To see the options related to any particular behavior, you must run that part of the software first. Configuration options are added ""on the fly"" (in order of execution) to ""{INI}"" as the software runs. " .Replace("{EXE}", Path.GetFileName(Application.ResourceAssembly.Location)) .Replace("{INI}", Path.GetFileName(loProfile.sActualPathFile)) .Replace("{{", "{") .Replace("}}", "}") ); // Fetch simple setup. tvFetchResource.ToDisk(Application.ResourceAssembly.GetName().Name , "Setup Application Folder.exe", null); // Fetch source code. if ( loProfile.bValue("-FetchSource", false) ) tvFetchResource.ToDisk(Application.ResourceAssembly.GetName().Name , Application.ResourceAssembly.GetName().Name + ".zip", null); // Updates start here. if ( loProfile.bFileJustCreated ) { loProfile["-Updated20160416"] = true; loProfile.Save(); } else { if ( !loProfile.bValue("-Updated20160416", false) ) { //if ( MessageBoxResult.Cancel == MessageBox.Show( // "This software has been updated." // + " It requires a change to your -ZipToolEXEargs." // + "Shall we remove the old -ZipToolEXEargs now?" // , Application.ResourceAssembly.GetName().Name // , MessageBoxButton.OKCancel, MessageBoxImage.Question) ) //{ // loProfile.bExit = true; //} //else //{ loProfile.Remove("-ZipToolEXEargs"); loProfile["-Updated20160416"] = true; loProfile.Save(); //} } } // Updates end here. if ( !loProfile.bExit ) { if ( loProfile.bValue("-RunOnce", false) ) { // Run in batch mode. // Turns off the "loading" message. loProfile.bAppFullyLoaded = true; DoGoPcBackup loDoDa = new DoGoPcBackup(loProfile); loDoDa.CleanupFiles(); loDoDa.BackupFiles(); } else { // Run in interactive mode. try { loMain = new DoGoPcBackup(loProfile); // Load the UI. UI loUI = new UI(loMain); loMain.oUI = loUI; loMain.Run(loUI); } catch (ObjectDisposedException) {} } GC.KeepAlive(loMutex); } } } catch (SecurityException) { tvFetchResource.NetworkSecurityStartupErrorMessage(); } catch (Exception ex) { tvFetchResource.ErrorMessage(null, ex.Message); } finally { if ( null != loMain && null != loMain.oUI ) loMain.oUI.Close(); } }
/// <summary> /// Recursively deletes files of the given path\file /// specification older than the given age in days. /// </summary> /// <param name="asPathFiles"> /// The path\file specification of files to be deleted. /// </param> /// <param name="adtOlderThan"> /// Files with timestamps older than this will be deleted. /// </param> /// <param name="aeFileDateTimeType"> /// Each file has multiple timestamps. This specifies which one to use. /// </param> /// <param name="aoProfile"> /// This profile contains the various cleanup parameters. /// </param> public bool CleanupPathFileSpec( string asPathFiles , DateTime adtOlderThan , FileDateTimeTypes aeFileDateTimeType , tvProfile aoProfile ) { if ( this.bMainLoopStopped ) return true; bool lbCleanupPathFileSpec = true; string lsPath = Path.GetDirectoryName(asPathFiles); string lsFiles = Path.GetFileName(asPathFiles); bool lbCleanupHidden = aoProfile.bValue("-CleanupHidden", false); bool lbCleanupReadOnly = aoProfile.bValue("-CleanupReadOnly", false); bool lbRecurse = aoProfile.bValue("-Recurse", false); /* bool lbDisplayFileDeletionErrors = true; // Don't create a default value here. Let the user create the value via a prompt // below. This must be handled this way since the deletion error messages are // modeless and therefore the "skip this" checkbox will be presented only once. if ( moProfile.ContainsKey("-MsgBoxPromptFileDeletionErrors") ) lbDisplayFileDeletionErrors = moProfile.bValue("-MsgBoxPromptFileDeletionErrors", true); */ bool lbDisplayFileDeletionErrors = moProfile.bValue("-MsgBoxPromptFileDeletionErrors", false); string lsDirectorySeparatorChar = Path.DirectorySeparatorChar.ToString(); string lsRecurseFolder = aoProfile.sValue("-RecurseFolder", ""); // The recurse folder must be surrounded by path delimiters. Otherwise, // a matching path name substring may be found instead of a subfolder name. if ( !lsRecurseFolder.StartsWith(lsDirectorySeparatorChar) ) lsRecurseFolder = lsDirectorySeparatorChar + lsRecurseFolder; if ( !lsRecurseFolder.EndsWith(lsDirectorySeparatorChar) ) lsRecurseFolder += lsDirectorySeparatorChar; try { // Only check for file cleanup if either there is no recursion // or the base path contains the recursion subfolder. An empty // recursion subfolder matches everything from the base path up. if ( !lbRecurse || (lbRecurse && (lsPath + lsDirectorySeparatorChar).Contains(lsRecurseFolder)) ) { IOrderedEnumerable<FileSystemInfo> loFileSysInfoList = null; // If the given file path does not exist, do nothing. if ( Directory.Exists(lsPath) ) try { // Get a list of all files for potential deletion // sorted by file date (oldest files first). switch (aeFileDateTimeType) { case FileDateTimeTypes.CreationTime: loFileSysInfoList = new DirectoryInfo(lsPath).GetFileSystemInfos(lsFiles) .OrderBy(a => a.CreationTime); break; case FileDateTimeTypes.LastAccessTime: loFileSysInfoList = new DirectoryInfo(lsPath).GetFileSystemInfos(lsFiles) .OrderBy(a => a.LastAccessTime); break; default: loFileSysInfoList = new DirectoryInfo(lsPath).GetFileSystemInfos(lsFiles) .OrderBy(a => a.LastWriteTime); break; } } catch (Exception ex) { if ( !lbDisplayFileDeletionErrors ) this.LogIt(string.Format("Folder: \"{0}\"\r\n", lsPath) + ex.Message); else this.ShowModelessError( string.Format("Folder: \"{0}\"\r\n", lsPath) + ex.Message , "Error Deleting Files" , "-FileDeletionErrors" ); } if ( null != loFileSysInfoList ) { // This boolean prevents wiping out many old files // that are not regularly replaced with newer files. bool lbApplyDeletionLimit = aoProfile.bValue("-ApplyDeletionLimit", true); int liFileDeletionLimit = this.iFileDeletionLimit( loFileSysInfoList.Count(), adtOlderThan); int liIndex = 0; foreach (FileSystemInfo loFileSysInfo in loFileSysInfoList) { System.Windows.Forms.Application.DoEvents(); System.Threading.Thread.Sleep(moProfile.iValue("-CleanupLoopSleepMS", 1)); if ( this.bMainLoopStopped ) break; // Show UI activity for each file evaluated. this.IncrementUIProgressBar(); // Since files are deleted in file date order, // the oldest files will always be deleted first. // Once the deletion limit is reached, stop deleting. if ( lbApplyDeletionLimit && ++liIndex > liFileDeletionLimit ) break; DateTime ldtFileDate; switch (aeFileDateTimeType) { case FileDateTimeTypes.CreationTime: ldtFileDate = loFileSysInfo.CreationTime; break; case FileDateTimeTypes.LastAccessTime: ldtFileDate = loFileSysInfo.LastAccessTime; break; default: ldtFileDate = loFileSysInfo.LastWriteTime; break; } // Delete the current file only if its file date is older // than the given date. If it's also a hidden file, the // -CleanupHidden switch must be specified (see above). bool lbDoDelete = ldtFileDate < adtOlderThan && ( lbCleanupHidden || FileAttributes.Hidden != (loFileSysInfo.Attributes & FileAttributes.Hidden)); if ( lbDoDelete ) { try { // Get the file size. long llFileSize = new FileInfo(loFileSysInfo.FullName).Length; // If the -CleanupReadOnly switch is used (see above), // set the current file's attributes to "Normal". if ( lbCleanupReadOnly && FileAttributes.ReadOnly == (loFileSysInfo.Attributes & FileAttributes.ReadOnly) ) loFileSysInfo.Attributes = FileAttributes.Normal; // Hidden files can be deleted without changing attributes. // Attempt to delete the file. If its attributes still // include "readonly", let it blow an error. loFileSysInfo.Delete(); this.LogDeletedFile(loFileSysInfo.FullName, ldtFileDate, llFileSize); } catch (Exception ex) { if ( !lbDisplayFileDeletionErrors ) this.LogIt(string.Format("File: \"{0}\"\r\n", loFileSysInfo.FullName) + ex.Message); else this.ShowModelessError( string.Format("File: \"{0}\"\r\n", loFileSysInfo.FullName) + ex.Message , "Error Deleting File" , "-FileDeletionErrors" ); } } } } } // Recursion is determined by the -Recurse switch (see above). if ( lbRecurse ) { // Process the sub-folders in the base folder. // Use an empty array instead of null to // prevent the "foreach" from blowing up. string[] lsSubfoldersArray = new string[0]; if ( Directory.Exists(lsPath) ) { try { // Get subdirectories only at the next level. lsSubfoldersArray = Directory.GetDirectories(lsPath); } catch (Exception ex) { if ( !lbDisplayFileDeletionErrors ) this.LogIt(string.Format("Folder: \"{0}\"\r\n", lsPath) + ex.Message); else this.ShowModelessError( string.Format("Folder: \"{0}\"\r\n", lsPath) + ex.Message , "Error Deleting Folders" , "-FileDeletionErrors" ); } } foreach (string lsSubfolder in lsSubfoldersArray) { System.Windows.Forms.Application.DoEvents(); if ( this.bMainLoopStopped ) break; // Get the current subfolder's attributes. Using "Hidden" by default prevents // an attempt at deleting the file if its attributes can't be read for whatever // reason (unless the -CleanupHidden switch is used). In the case of unreadable // attributes the file would not likely be deletable anyway. FileAttributes loFileAttributes = FileAttributes.Hidden; try { loFileAttributes = File.GetAttributes(lsSubfolder); } catch (Exception ex) { if ( !lbDisplayFileDeletionErrors ) this.LogIt(string.Format("Folder: \"{0}\"\r\n", lsSubfolder) + ex.Message); else this.ShowModelessError( string.Format("Folder: \"{0}\"\r\n", lsSubfolder) + ex.Message , "Error Deleting Folder" , "-FileDeletionErrors" ); } if ( lbCleanupHidden || FileAttributes.Hidden != (loFileAttributes & FileAttributes.Hidden) ) { // Remove all applicable files in the current subfolder. this.CleanupPathFileSpec( Path.Combine(lsSubfolder, lsFiles) , adtOlderThan , aeFileDateTimeType , aoProfile ); // This is deliberate. Do not use "Path.Combine()" here. We need // the trailing directory separator character (eg. the backslash) // since the recurse folder will always have a trailing separator. string lsSubfolderPlus = lsSubfolder + lsDirectorySeparatorChar; // Remove empty subfolders in the recurse folder only (ie. // do not remove the recurse folder itself). In other words, // lsSubfolderPlus may contain lsRecurseFolder, but it can't // end with it (unless lsRecurseFolder is just a backslash). if ( lsSubfolderPlus.Contains(lsRecurseFolder) && ( !lsSubfolderPlus.EndsWith(lsRecurseFolder) || lsDirectorySeparatorChar == lsRecurseFolder) ) { // These are used to judge the subfolder emptiness. string[] lsPathFilesArray = new string[0]; string[] lsSubfoldersArray2 = new string[0]; if ( Directory.Exists(lsSubfolder) ) try { lsPathFilesArray = Directory.GetFiles(lsSubfolder); lsSubfoldersArray2 = Directory.GetDirectories(lsSubfolder); } catch (Exception ex) { if ( !lbDisplayFileDeletionErrors ) this.LogIt(string.Format("Folder: \"{0}\"\r\n", lsSubfolder) + ex.Message); else this.ShowModelessError( string.Format("Folder: \"{0}\"\r\n", lsSubfolder) + ex.Message , "Error Deleting Files" , "-FileDeletionErrors" ); } // Remove the folder only if it's empty. if ( 0 == lsPathFilesArray.Length && 0 == lsSubfoldersArray2.Length ) { DirectoryInfo loDirInfo = new DirectoryInfo(lsSubfolder); DateTime ldtFileDate; switch (aeFileDateTimeType) { case FileDateTimeTypes.CreationTime: ldtFileDate = loDirInfo.CreationTime; break; case FileDateTimeTypes.LastAccessTime: ldtFileDate = loDirInfo.LastAccessTime; break; default: ldtFileDate = loDirInfo.LastWriteTime; break; } try { // If the -CleanupReadOnly switch is used, // set the subfolder to "Normal" attributes. if ( lbCleanupReadOnly && FileAttributes.ReadOnly == (loFileAttributes & FileAttributes.ReadOnly) ) File.SetAttributes(lsSubfolder, FileAttributes.Normal); // Hidden folders can be deleted without changing attributes. // Attempt to delete the subfolder. If its attributes still // include "readonly", let it blow an error. Directory.Delete(lsSubfolder); // Using "0" as the file size also indicates a folder deletion. this.LogDeletedFile(lsSubfolder + " (dir)", ldtFileDate, 0); } catch (Exception ex) { if ( !lbDisplayFileDeletionErrors ) this.LogIt(string.Format("Folder: \"{0}\"\r\n", lsSubfolder) + ex.Message); else this.ShowModelessError( string.Format("Folder: \"{0}\"\r\n", lsSubfolder) + ex.Message , "Error Deleting Folder" , "-FileDeletionErrors" ); } } } } } } } catch (Exception ex) { this.ShowError(ex.Message, "Unanticipated Error"); lbCleanupPathFileSpec = false; } return lbCleanupPathFileSpec; }
public void KillAddedTasks() { if ( null != moAddTasksProfile ) for (int i=0; i < moAddTasksProfile.Count; ++i) { tvProfile loAddedTask = new tvProfile(moAddTasksProfile[i].ToString()); if ( loAddedTask.bValue("-UnloadOnExit", false) && null != moAddTasksProcessArray[i] ) { this.LogIt(String.Format("Stopping Task: {0}", loAddedTask.sCommandLine())); this.bKillProcess(moAddTasksProcessArray[i]); } } }
public static tvMessageBoxResults Show( Window aoWindow , string asMessageText , string asMessageCaption , tvMessageBoxButtons aeTvMessageBoxButtons , tvMessageBoxIcons aeTvMessageBoxIcon , bool abShowModeless , tvMessageBoxCheckBoxTypes aeTvMessageBoxCheckBoxType , tvProfile aoProfile , string asProfilePromptKey , tvMessageBoxResults aeTvMessageBoxResultsOverride ) { tvMessageBoxResults liTvMessageBoxResult = tvMessageBoxResults.None; string lsPromptAnswerKey = null; bool lbUseCheckBox = tvMessageBoxCheckBoxTypes.None != aeTvMessageBoxCheckBoxType; if ( lbUseCheckBox ) { // Insert the prompt key prefix if it's not already there. A common prefix // is necessary to allow for the removal of all prompt keys as needed. if ( !asProfilePromptKey.StartsWith(msProfilePromptKeyPrefix) ) { // Strip leading hyphen. if ( asProfilePromptKey.StartsWith("-") ) asProfilePromptKey = asProfilePromptKey.Substring(1, asProfilePromptKey.Length - 1); // Insert prefix. asProfilePromptKey = msProfilePromptKeyPrefix + asProfilePromptKey; } // Make the answer key from the prompt key and the prompt key suffix. lsPromptAnswerKey = asProfilePromptKey + msProfilePromptKeySuffix; // Only the first display of a modeless dialog can contain a checkbox. // Why? Because the first prompt is not modeless. That's the only way // to capture the checkbox value. BTW, "lbUseCheckBox" is reset here // for use outside of this block to avoid the default setting next. if ( abShowModeless ) lbUseCheckBox = !aoProfile.ContainsKey(asProfilePromptKey); if ( !aoProfile.bValue(asProfilePromptKey, false) && aoProfile.ContainsKey(lsPromptAnswerKey) ) { // Do not prompt. Return the previous stored answer instead. return (tvMessageBoxResults)aoProfile.iValue( lsPromptAnswerKey, (int)tvMessageBoxResults.None); } } if ( null == asMessageCaption ) { // No caption provided. Let's try to get one another way. if ( null != aoWindow ) // Try window title first. asMessageCaption = aoWindow.Title; else if ( null != Application.Current ) // Next try for application name. asMessageCaption = Application.Current.MainWindow.Name; } if ( null != aoWindow ) aoWindow.Cursor = null; // Turn off wait cursor in parent window. tvMessageBox loMsgBox = new tvMessageBox(); loMsgBox.MessageText.Text = asMessageText; // Use some parent window attributes, if available. if ( null != aoWindow ) { // Use the parent window's icon. loMsgBox.Icon = aoWindow.Icon; // Use the given asMessageCaption as the MsgBox title, if not null. // Otherwise use the parent window title with an added question mark. loMsgBox.Title = null != asMessageCaption ? asMessageCaption : aoWindow.Title + "?"; } // Display the MsgBox header / title (ie. the caption), if provided. if ( null != asMessageCaption ) { loMsgBox.MessageTitle.Content = asMessageCaption; loMsgBox.MessageTitle.Visibility = Visibility.Visible; } loMsgBox.SelectButtons(aeTvMessageBoxButtons); loMsgBox.SelectIcon(aeTvMessageBoxIcon); if ( lbUseCheckBox ) { switch (aeTvMessageBoxCheckBoxType) { case tvMessageBoxCheckBoxTypes.DontAsk: loMsgBox.chkDontAsk.Visibility = Visibility.Visible; break; case tvMessageBoxCheckBoxTypes.SkipThis: loMsgBox.chkSkipThis.Visibility = Visibility.Visible; break; } } if ( !abShowModeless ) { loMsgBox.ShowDialog(); } else { // It can only be modeless after the checkbox has been stored. if ( lbUseCheckBox ) loMsgBox.ShowDialog(); else loMsgBox.Show(); } if ( lbUseCheckBox ) { bool lbCheckBoxValue = false; switch (aeTvMessageBoxCheckBoxType) { case tvMessageBoxCheckBoxTypes.DontAsk: lbCheckBoxValue = (bool)loMsgBox.chkDontAsk.IsChecked; break; case tvMessageBoxCheckBoxTypes.SkipThis: lbCheckBoxValue = (bool)loMsgBox.chkSkipThis.IsChecked; break; } // Use the answer override whenever not "none". This value is // necessary for certain stored answers that don't make sense // in a given context (eg. both "skip this" and "cancel" selected). if ( tvMessageBoxResults.None == aeTvMessageBoxResultsOverride ) aeTvMessageBoxResultsOverride = loMsgBox.eTvMessageBoxResult; // Reverse the boolean. "Don't ask" or "Skip this" means "Don't prompt". aoProfile[asProfilePromptKey] = !lbCheckBoxValue; aoProfile[lsPromptAnswerKey] = (int)aeTvMessageBoxResultsOverride; aoProfile.Save(); } liTvMessageBoxResult = loMsgBox.eTvMessageBoxResult; return liTvMessageBoxResult; }