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");
            }
        }
        /// <summary>
        /// This constructor expects a profile object to be provided.
        /// </summary>
        /// <param name="aoProfile">
        /// The given profile object must either contain runtime options
        /// or it will be returned filled with default runtime options.
        /// </param>
        public DoGoPcBackup(tvProfile aoProfile)
        {
            moProfile = aoProfile;

            moBackgroundThread = new Thread(new ThreadStart(moBackgroundThread_Start));
            moBackgroundThread.Start();
        }
Exemple #3
0
        private string msPreviousBackupTime = null; // This tracks changes to backup time configuration.

        #endregion Fields

        #region Constructors

        /// <summary>
        /// This constructor expects a file backup 
        /// application object to be provided.
        /// </summary>
        /// <param name="aoDoGoPcBackup">
        /// The given file backup object creates dated archives
        /// of file collections from the local file system.
        /// </param>
        public UI(DoGoPcBackup aoDoGoPcBackup)
        {
            InitializeComponent();

            // This loads window UI defaults from the given profile.
            base.Init();

            moProfile = aoDoGoPcBackup.oProfile;
            moDoGoPcBackup = aoDoGoPcBackup;
        }
 public static void ShowModelessError(
     Window aoWindow
     , string asMessageText
     , string asMessageCaption
     , tvProfile aoProfile
     )
 {
     tvMessageBox.Show(
               aoWindow
             , asMessageText
             , asMessageCaption
             , tvMessageBoxButtons.OK
             , tvMessageBoxIcons.Error
             , true
             );
 }
        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>
        /// Deletes files greater than the given age
        /// days and of the given file specifications.
        /// </summary>
        public bool CleanupFiles()
        {
            // Return if cleanup is disabled.
            if ( !moProfile.bValue("-CleanupFiles", true) )
            {
                this.LogIt("");
                this.LogIt("Cleanup files is disabled.");
                return true;
            }
            // Return if backup is enabled and it was stopped.
            if (  moProfile.bValue("-BackupFiles", true) && this.bMainLoopStopped )
                return true;
            else
                this.bMainLoopStopped = false;

            bool lbCleanupFiles = true;

            this.LogIt("");
            this.LogIt("File cleanup started ...");

            // Write the deleted file list header to disk.
            string  lsDeletedFileListOutputPathFile = moProfile.sRelativeToProfilePathFile(this.sDeletedFileListOutputPathFile);

            if ( !File.Exists(lsDeletedFileListOutputPathFile) )
            {
                // Get the column header array.
                string[] lsColumnHeaderArray = moProfile.sValue("-DeletedFileListOutputColumnHeaderArray", "Deleted File Time,File Size,Former File Location").Split(',');

                StreamWriter loStreamWriter = null;

                try
                {
                    loStreamWriter = new StreamWriter(lsDeletedFileListOutputPathFile, false);

                    // First, output the file header.
                    loStreamWriter.WriteLine(string.Format(moProfile.sValue("-DeletedFileListOutputHeader"
                            , "{0, -10:MM-dd-yyyy} File Cleanup List"), DateTime.Today));
                    loStreamWriter.WriteLine();

                    // Next output the column headers properly formatted to match the forthcoming data rows.
                    loStreamWriter.WriteLine(string.Format(
                              moProfile.sValue("-DeletedFileListOutputColumnFormat", "{0, -22:MM-dd-yyyy hh:mm:ss tt}  {1, 13:#,#}  {2}")
                            , lsColumnHeaderArray[0], lsColumnHeaderArray[1], lsColumnHeaderArray[2]));
                    loStreamWriter.WriteLine();
                }
                catch (Exception ex)
                {
                    this.ShowError(string.Format("File Write Failure: \"{0}\"\r\n"
                            , lsDeletedFileListOutputPathFile) + ex.Message
                            , "Failed Writing File"
                            );
                }
                finally
                {
                    if ( null != loStreamWriter )
                        loStreamWriter.Close();
                }
            }

            try
            {
                // This is used elsewhere to warn users if the software
                // has not been properly configured for file cleanups.
                this.mbHasNoDeletionGroups = true;

                if ( !moProfile.ContainsKey("-CleanupSet") )
                {
                    // Create the default file cleanup sets.

                    // Get the primary backup set (ie. the 1st).
                    tvProfile loBackupSet1Profile = new tvProfile(moProfile.sValue("-BackupSet", "(not set)"));
                    string lsBackupOutputPathFileBase = this.sBackupOutputPathFileBase(loBackupSet1Profile);
                    string lsBackupOutputPath = Path.GetDirectoryName(lsBackupOutputPathFileBase);
                    string lsBackupOutputFilenameNoExt = Path.GetFileNameWithoutExtension(lsBackupOutputPathFileBase);

                    // Initially set the cleanup of primary backups to "no cleanup" (ie. 1000 years).
                    // The deletion limit prevents old file removal without new files to replace them.
                    moProfile.Add("-CleanupSet", string.Format(@"
    -AgeDays=365000
    -FilesToDelete={0}*{1}

"
                            , Path.Combine(lsBackupOutputPath, lsBackupOutputFilenameNoExt)
                            , Path.GetExtension(lsBackupOutputPathFileBase)
                            ));

                    // Set the cleanup of temporary backup files to 0 days.
                    // This is necessary to cleanup after killed processes.
                    moProfile.Add("-CleanupSet", string.Format(@"
    -AgeDays=0
    -FilesToDelete={0}.tmp*

"
                            , Path.Combine(this.sArchivePath(), "*" + Path.GetExtension(lsBackupOutputPathFileBase))
                            ));

                    // Set the cleanup of file lists and backup / cleanup log files to 30 days.
                    moProfile.Add("-CleanupSet", string.Format(@"
    -AgeDays=30
    -FilesToDelete={0}
    -FilesToDelete={1}

"
                            , Path.Combine(Path.GetDirectoryName(this.sZipToolFileListPathFileBase)
                                    , "*" + Path.GetExtension(this.sZipToolFileListPathFileBase))

                            , Path.Combine(Path.GetDirectoryName(this.sLogPathFileBase)
                                    , "*" + Path.GetExtension(this.sLogPathFileBase))
                            ));
                }

                // Get all cleanup sets.
                tvProfile loCleanupSetsProfile = moProfile.oOneKeyProfile("-CleanupSet");

                foreach (DictionaryEntry loEntry in loCleanupSetsProfile)
                {
                    System.Windows.Forms.Application.DoEvents();
                    if ( this.bMainLoopStopped )
                        break;

                    // Convert the current cleanup set from a command-line string to a profile oject.
                    tvProfile loCurrentCleanupSet = new tvProfile(loEntry.Value.ToString());

                    // The default "LastWriteTime" is the last modified datetime.
                    FileDateTimeTypes leFileDateTimeType;
                            switch (loCurrentCleanupSet.sValue("-DeletedFileListDateTimeType", "LastWriteTime"))
                            {
                                case "CreationTime":
                                    leFileDateTimeType = FileDateTimeTypes.CreationTime;
                                    break;
                                case "LastAccessTime":
                                    leFileDateTimeType = FileDateTimeTypes.LastAccessTime;
                                    break;
                                default:
                                    leFileDateTimeType = FileDateTimeTypes.LastWriteTime;
                                    break;
                            }
                    // Use 1000 years as the default file age.
                    int         liAgeDays = loCurrentCleanupSet.iValue("-AgeDays", 365000);
                                liAgeDays = 0 == liAgeDays ? -1 : liAgeDays;
                                // "0" means "delete everything." -1 makes that happen.
                    DateTime    ldtOlderThan = DateTime.Now.AddDays(-liAgeDays);

                    // Get the list of path\file specifications to delete.
                    tvProfile loFilesToDeleteProfile = loCurrentCleanupSet.oOneKeyProfile("-FilesToDelete");

                    foreach (DictionaryEntry loPathFilesEntry in loFilesToDeleteProfile)
                    {
                        System.Windows.Forms.Application.DoEvents();
                        if ( this.bMainLoopStopped )
                            break;

                        // Being here means there is at least one set of files to delete.
                        this.mbHasNoDeletionGroups = false;

                        if ( lbCleanupFiles )
                            lbCleanupFiles = this.CleanupPathFileSpec(
                                      moProfile.sRelativeToProfilePathFile(loPathFilesEntry.Value.ToString())
                                    , ldtOlderThan
                                    , leFileDateTimeType
                                    , loCurrentCleanupSet
                                    );
                    }
                }

                if ( lbCleanupFiles )
                    this.DisplayDeletedFileList();
            }
            catch (Exception ex)
            {
                this.ShowError(ex.Message, "Unanticipated Error");

                lbCleanupFiles = false;
            }

            if ( this.bMainLoopStopped )
            {
                this.LogIt("Cleanup process stopped.");
                this.bMainLoopStopped = false;
                lbCleanupFiles = false;
            }
            else
            {
                if ( lbCleanupFiles )
                    this.LogIt("File cleanup finished.");
                else
                    this.LogIt("File cleanup failed.");
            }

            return lbCleanupFiles;
        }
        public int iBackupDoneScriptCopyFailuresWithBitField(bool abRerunLastArgs)
        {
            int liBackupDoneScriptCopyFailuresWithBitField = 0;

            // Before the "backup done" script can be initialized,
            // -BackupDoneScriptPathFile and -BackupDoneScriptHelp
            // must be initialized first.
            if ( moProfile.bValue("-BackupDoneScriptInit", false) )
            {
                moProfile.Remove("-BackupDoneScriptPathFile");
                moProfile.Remove("-BackupDoneScriptHelp");
            }

            string lsBackupDoneScriptPathFile = moProfile.sRelativeToProfilePathFile(
                    moProfile.sValue("-BackupDoneScriptPathFile", msBackupDoneScriptPathFileDefault));
            string lsBackupDoneScriptOutputPathFile = lsBackupDoneScriptPathFile + ".txt";

            // If the "backup done" script has not been redefined to point elsewhere,
            // prepare to create it from the current -BackupDoneScriptHelp content.
            // We do this even if the script file actually exists already. This way
            // the following default script will be written to the profile file if
            // it's not already there.
            if ( lsBackupDoneScriptPathFile == moProfile.sRelativeToProfilePathFile(msBackupDoneScriptPathFileDefault) )
            {
                bool    lbUseMainhostArchive    = moProfile.bValue("-UseVirtualMachineHostArchive", false);
                bool    lbUseConnectMainhost    = moProfile.bValue("-UseConnectVirtualMachineHost", false);
                string  lsBackupDoneScript      = moProfile.sValue("-BackupDoneScriptHelp", @"
@echo off
if %1=="""" goto :EOF
::
:: *** ""Backup Done"" script goes here. ***
::
:: This script is executed after each successful backup completes. If you 
:: prompt for input within this DOS script (eg. ""pause""), the script
:: will stay in memory. This is not recommended since such behavior would
:: be similar to a memory leak.
::
:: You can also create and edit another DOS script file and reference that
:: instead (see ""-BackupDoneScriptPathFile"" in ""{ProfileFile}""). You
:: can access several parameters from the completed backup via the DOS shell
:: command-line:
::
:: %1 = ""BackupOutputPathFile""
::
::      This is the full path\file specification of the backup file.
::      It includes the output filename as well as the embedded date.
::
:: %2 = ""BackupOutputFilename""
::
::      This is the backup filename only (ie. no path). It includes the
::      embedded date as well as the filename extension.
::
:: %3 = ""BackupBaseOutputFilename""
::
::      This is the backup filename with no path and no date. It's just
::      the base output filename name with the filename extension.
::
:: %4 = ""LocalArchivePath""
::
::      This is the local archive folder.
::
:: %5 = ""VirtualMachineHostArchive""
::
::      This is the virtual machine host archive share name.
::
:: %6 = ""LogPathFile""
::
::      This is the full path\file specification of the backup log file.
::
::
:: Note: All arguments will be passed with double quotation marks included.
::       So don't use quotes here unless you want ""double double"" quotes.
::       Also, ERRORLEVEL is not reliable enough to be heavily used below.
::
:: The following example copies the backup file to the root of drive C:
:: (if it's ""AdministratorFiles.zip""). Then it outputs a directory listing
:: of the archive folder.
::
:: Example:
::
:: if not %3.==""AdministratorFiles.zip"". goto :EOF
::
::     echo copy %1 C:\  ] ""{BackupDoneScriptOutputPathFile}"" 2>&1
::          copy %1 C:\ ]] ""{BackupDoneScriptOutputPathFile}"" 2>&1
::
:: dir %4               ]] ""{BackupDoneScriptOutputPathFile}"" 2>&1
::
::                      ^^  Replace brackets with darts.

:: The ""CopyFailures"" environment variable is used to keep count of errors to be returned.
set CopyFailures=0

:: Initialize the ""backup done"" script log file. It's for this run only.
echo.                    > ""{BackupDoneScriptOutputPathFile}"" 2>&1
"
+
(!lbUseMainhostArchive ? "" :
@"
:: This references the backup destination copy on the VM host:
set FileSpec=%5\%2

echo.                                                                       >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo This copies the backup to the virtual machine host archive:            >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo.                                                                       >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo copy %1 %5                                                             >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
     copy %1 %5                                                             >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
     if not exist %FileSpec% echo   Error: %FileSpec% is not there.         >> ""{BackupDoneScriptOutputPathFile}"" 2>&1

     if not exist %FileSpec% set /A CopyFailures += 1
"
)
+
@"
echo.                                                                       >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo The following copies the backup (base name) to each attached backup    >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo device with the file ""{BackupDriveToken}"" at its root.               >> ""{BackupDoneScriptOutputPathFile}"" 2>&1

set BackupOutputPathFile=%1
set BackupBaseOutputFilename=%3

set BackupDeviceDecimalBitField=0
set BackupDevicePositionExponent=23

:: There are 23 drive letters listed (ie. possible backup devices). A 32-bit integer
:: can handle no more when a corresponding bit field is combined with copy failures.
for %%d in (D: E: F: G: H: I: J: K: L: M: N: O: P: Q: R: S: T: U: V: W: X: Y: Z:) do call :DoCopy %%d

:: Set bit 24 (ie. add 2^23 = 8,388,608) to preserve bit field's leading zeros.
:: Combine the bit field and the copy failures into a single composite value.
:: The factor of 100 means that there can be a maximum of 99 copy failures.

set /A CompositeResult = 100 * (8388608 + %BackupDeviceDecimalBitField%) + %CopyFailures%

echo   CompositeResult=%CompositeResult%                                    >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
exit  %CompositeResult%

:DoCopy
set /A BackupDevicePositionExponent -= 1
dir %1 > nul 2> nul
if ERRORLEVEL 1 goto :EOF
if not exist %1\""{BackupDriveToken}"" goto :EOF

:: Determine the bit position (and the corresponding decimal value) from the exponent.
set BitFieldDevicePosition=1
for /L %%x in (1, 1, %BackupDevicePositionExponent%) do set /A BitFieldDevicePosition *= 2

:: Add the calculated positional value to the bit field for the current backup device.
set /A BackupDeviceDecimalBitField += %BitFieldDevicePosition%


:: This references the backup destination copy on the current backup device (%1):
set FileSpec=%1\%BackupBaseOutputFilename%

echo.                                                                       >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo This removes the previous backup (if any) from %1                      >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo.                                                                       >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo del %FileSpec%                                                         >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
     del %FileSpec%                                                         >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
     if exist %FileSpec% echo   Error: %FileSpec% is still there.           >> ""{BackupDoneScriptOutputPathFile}"" 2>&1

     if exist %FileSpec% set /A CopyFailures += 1

echo.                                                                       >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo This copies the current backup to %1                                   >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo.                                                                       >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
echo copy %BackupOutputPathFile% %FileSpec%                                 >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
     copy %BackupOutputPathFile% %FileSpec%                                 >> ""{BackupDoneScriptOutputPathFile}"" 2>&1
     if not exist %FileSpec% echo   Error: %FileSpec% is not there.         >> ""{BackupDoneScriptOutputPathFile}"" 2>&1

     if not exist %FileSpec% set /A CopyFailures += 1
"
)
                        .Replace("{ProfileFile}", Path.GetFileName(moProfile.sLoadedPathFile))
                        .Replace("{BackupDoneScriptOutputPathFile}", Path.GetFileName(lsBackupDoneScriptOutputPathFile))
                        .Replace("{BackupDriveToken}", this.sBackupDriveToken)
                        ;

                // Write the default "backup done" script if it's
                // not there or if -BackupDoneScriptInit is set.
                if (       !File.Exists(lsBackupDoneScriptPathFile)
                        || moProfile.bValue("-BackupDoneScriptInit", false) )
                {
                    StreamWriter loStreamWriter = null;

                    try
                    {
                        loStreamWriter = new StreamWriter(lsBackupDoneScriptPathFile, false);
                        loStreamWriter.Write(lsBackupDoneScript);

                        // This is used only once then reset.
                        moProfile["-BackupDoneScriptInit"] = false;
                        moProfile.Save();
                    }
                    catch (Exception ex)
                    {
                        this.ShowError(string.Format("File Write Failure: \"{0}\"\r\n"
                                , lsBackupDoneScript) + ex.Message
                                , "Failed Writing File"
                                );
                    }
                    finally
                    {
                        if ( null != loStreamWriter )
                            loStreamWriter.Close();
                    }
                }
            }

            try
            {
                this.LogIt("");
                this.LogIt("Running \"backup done\" script ...");

                // Cache the arguments to be passed to the script.
                tvProfile  loArgs = new tvProfile();
                            if ( abRerunLastArgs )
                            {
                                loArgs.LoadFromCommandLine(moProfile.sValue("-BackupDoneArgs", ""), tvProfileLoadActions.Append);
                            }
                            else
                            {
                                loArgs.Add("-BackupOutputPathFile"          , msCurrentBackupOutputPathFile                                          );
                                loArgs.Add("-BackupOutputFilename"          , Path.GetFileName(msCurrentBackupOutputPathFile)                        );
                                loArgs.Add("-BackupBaseOutputFilename"      , Path.GetFileName(this.sBackupOutputPathFileBase())                     );
                                loArgs.Add("-LocalArchivePath"              , this.sArchivePath()                                                    );
                                loArgs.Add("-VirtualMachineHostArchivePath" , moProfile.sValue("-VirtualMachineHostArchivePath", "")                 );
                                loArgs.Add("-LogPathFile"                   , moProfile.sRelativeToProfilePathFile(this.sLogPathFile)                );

                                moProfile["-BackupDoneArgs"] = loArgs.sCommandBlock();
                                moProfile.Save();
                            }

                // Run the "backup done" script.
                Process loProcess = new Process();
                        loProcess.StartInfo.FileName = lsBackupDoneScriptPathFile;
                        loProcess.StartInfo.Arguments = string.Format(
                                  " \"{0}\" \"{1}\" \"{2}\" \"{3}\" \"{4}\" \"{5}\" \"{6}\" \"{7}\" \"{8}\" \"{9}\" "
                                , loArgs.sValue("-BackupOutputPathFile"         , "")
                                , loArgs.sValue("-BackupOutputFilename"         , "")
                                , loArgs.sValue("-BackupBaseOutputFilename"     , "")
                                , loArgs.sValue("-LocalArchivePath"             , "")
                                , loArgs.sValue("-VirtualMachineHostArchivePath", "")
                                , loArgs.sValue("-LogPathFile"                  , "")
                                , ""
                                , ""
                                , ""
                                , ""
                                );
                        loProcess.StartInfo.UseShellExecute = true;
                        loProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                        loProcess.Start();

                // Wait for the "backup done" script to finish.
                while ( !this.bMainLoopStopped && !loProcess.HasExited )
                {
                    System.Windows.Forms.Application.DoEvents();
                    System.Threading.Thread.Sleep(moProfile.iValue("-MainLoopSleepMS", 100));
                }

                // If a stop request came through, kill the "backup done" script.
                if ( this.bMainLoopStopped && !this.bKillProcess(loProcess) )
                    this.ShowError("The \"backup done\" script could not be stopped."
                            , "Backup Failed");

                if ( !this.bMainLoopStopped )
                {
                    // The exit code is defined in the script as a combination of two integers:
                    // a bit field of found backup devices and a count of copy failures (99 max).
                    liBackupDoneScriptCopyFailuresWithBitField = loProcess.ExitCode;

                    double  ldCompositeResult = liBackupDoneScriptCopyFailuresWithBitField / 100.0;
                    int     liCurrentBackupDevicesBitField = (int)ldCompositeResult;   // The integer part is the bit field.

                    // The fractional part (x 100) is the number of copy failures.
                    int liBackupDoneScriptCopyFailures = (int)Math.Round(100 * (ldCompositeResult - liCurrentBackupDevicesBitField));

                    // Compare the bit field of current backup devices to the bit field of devices selected by the user.
                    List<char> loMissingBackupDevices = this.oMissingBackupDevices(liCurrentBackupDevicesBitField);

                    if (0 == liBackupDoneScriptCopyFailures && 0 == loMissingBackupDevices.Count)
                    {
                        this.LogIt("The \"backup done\" script finished successfully.");
                    }
                    else
                    {
                        if ( 0 != liBackupDoneScriptCopyFailures )
                        {
                            this.LogIt(string.Format("The \"backup done\" script had {0} copy failure{1}.\r\n"
                                    , liBackupDoneScriptCopyFailures
                                    , 1 == liBackupDoneScriptCopyFailures ? "" : "s")
                                    );

                            // Get the output from the "backup done" script.

                            string lsFileAsStream = this.sFileAsStream(lsBackupDoneScriptOutputPathFile);

                            this.LogIt("Here's output from the \"backup done\" script:\r\n\r\n" + lsFileAsStream);

                            if ( moProfile.bValue("-ShowBackupDoneScriptErrors", true) )
                                this.DisplayFileAsErrors(lsFileAsStream, "Backup Done Script Errors");
                        }

                        if ( 0 != loMissingBackupDevices.Count )
                            this.LogIt(string.Format("The \"backup done\" script noticed {0} backup device{1} missing.\r\n"
                                    , loMissingBackupDevices.Count
                                    , 1 == loMissingBackupDevices.Count ? "" : "s")
                                    );
                    }
                }

                loProcess.Close();
            }
            catch (Exception ex)
            {
                ++liBackupDoneScriptCopyFailuresWithBitField;
                this.SetBackupFailed();
                this.ShowError(ex.Message, "Failed Running \"Backup Done\" Script");
            }

            return liBackupDoneScriptCopyFailuresWithBitField;
        }
        /// <summary>
        /// Returns the total number of files to be backed up.
        /// </summary>
        /// <returns></returns>
        public int iBackupFilesCount()
        {
            int liBackupFilesCount = 0;

            // Get all backup sets.
            tvProfile loBackupSetsProfile = moProfile.oOneKeyProfile("-BackupSet");

            try
            {
                foreach (DictionaryEntry loEntry in loBackupSetsProfile)
                {
                    // Create a profile object from the current backup set.
                    tvProfile loBackupSetProfile = new tvProfile(loEntry.Value.ToString());

                    // Get all specifications of the files to backup in the current set.
                    tvProfile loFolderToBackupProfile = loBackupSetProfile.oOneKeyProfile("-FolderToBackup");

                    foreach (DictionaryEntry loFolderToBackup in loFolderToBackupProfile)
                    {
                        string  lsFolderToBackup = loFolderToBackup.Value.ToString().Trim();
                        string  lsBackupFiles = null;
                                if ( Directory.Exists(lsFolderToBackup) )
                                {
                                    lsBackupFiles = this.sBackupFileSpec(loBackupSetProfile);
                                }
                                else
                                {
                                    lsFolderToBackup = Path.GetDirectoryName(lsFolderToBackup);
                                    lsBackupFiles = Path.GetFileName(lsFolderToBackup);
                                    // If the given "lsFolderToBackup" is not actually a folder,
                                    // assume it's a single file (or a file mask).
                                }

                        // Count all files represented by the current file
                        // specifcation and add that number to the total.
                        liBackupFilesCount += Directory.GetFiles(
                                  lsFolderToBackup
                                , lsBackupFiles
                                , SearchOption.AllDirectories).Length;
                    }
                }
            }
            catch (Exception ex)
            {
                this.ShowError(ex.Message, "Backup Failed");
            }

            return liBackupFilesCount;
        }
        /// <summary>
        /// Returns a subset of the profile as a new profile using the given
        /// asKey.
        /// 
        /// The first item found with the given asKey is assumed itself to be
        /// a profile.
        /// </summary>
        /// <param name="asKey">
        /// The key string used to find the nested profile in the current
        /// profile.
        /// </param>
        /// <returns>
        /// A new object containing the nested profile found.
        /// </returns>
        public tvProfile oNestedProfile(String asKey)
        {
            tvProfile loNestedProfile = null;
            tvProfile loOneKeyProfile = this.oOneKeyProfile(asKey);

            if ( 0 == loOneKeyProfile.Count )
                loNestedProfile = new tvProfile();
            else
                loNestedProfile = new tvProfile(loOneKeyProfile[0].ToString());

            return loNestedProfile;
        }
Exemple #10
0
        /// <summary>
        /// Presents a grid UI for editing the contents of a profile object.
        /// Well known application independent tvProfile parameters are excluded.
        /// </summary>
        /// <param name="aoProfile">
        /// The given profile object to edit.
        /// </param>
        public void Edit(tvProfile aoProfile)
        {
            // This is done via late-binding so that the editor class files
            // are not required for successful compilation if they are not used.
            Type    loType = Assembly.GetExecutingAssembly().GetType("tvToolbox.tvProfileEditor", true);
            Object  loEditor = Activator.CreateInstance(loType, aoProfile);

            loType.GetMethod("ShowDialog", Type.EmptyTypes).Invoke(loEditor, null);
        }
Exemple #11
0
        /// <summary>
        /// This static factory method creates the global tvProfile object
        /// (ie. if it doesn't already exist).
        /// </summary>
        /// <returns>The global tvProfile object.</returns>
        public static tvProfile oGlobal()
        {
            if ( null == goGlobal )
            {
                goGlobal = new tvProfile(Environment.GetCommandLineArgs());
            }

            return goGlobal;
        }
        /// <summary>
        /// The value found for aiIndex.
        /// </summary>
        /// <param name="aiIndex">
        /// The integer index used to find the corresponding value in the profile.
        /// </param>
        /// <returns>
        /// The tvProfile object value found or an empty tvProfile
        /// (nothing is added to the parent profile if nothing is found).
        /// </returns>
        public tvProfile oProfile(int aiIndex)
        {
            object  loProfile = this[aiIndex];
            object  loProfileCast = loProfile as tvProfile;
                    if ( null == loProfileCast )
                        loProfileCast = new tvProfile(null == loProfile ? "" : loProfile.ToString());

            return (tvProfile)loProfileCast;
        }
        /// <summary>
        /// The "<see cref="GetAdd"/> Object" value found for asKey.
        /// </summary>
        /// <param name="asKey">
        /// The key string used to find the corresponding value in the profile.
        /// </param>
        /// <returns>
        /// The tvProfile object value found or an empty tvProfile
        /// (nothing is added to the parent profile if nothing is found).
        /// </returns>
        public tvProfile oProfile(String asKey)
        {
            object  loProfile = this[asKey];
            object  loProfileCast = loProfile as tvProfile;
                    if ( null == loProfileCast )
                        loProfileCast = new tvProfile(null == loProfile ? "" : loProfile.ToString());

            return (tvProfile)loProfileCast;
        }
 public static void ResetAllPrompts(
     Window aoWindow
     , string asMessageText
     , string asMessageCaption
     , tvProfile aoProfile
     )
 {
     if ( tvMessageBoxResults.Yes == tvMessageBox.Show(
                   aoWindow
                 , asMessageText
                 , asMessageCaption
                 , tvMessageBoxButtons.YesNo
                 , tvMessageBoxIcons.Alert
                 )
             )
     {
         aoProfile.Remove(msProfilePromptKeyPrefix + "*");
         aoProfile.Save();
     }
 }
 public static void ShowModelessError(
     Window aoWindow
     , string asMessageText
     , string asMessageCaption
     , tvProfile aoProfile
     , string asProfilePromptKey
     )
 {
     tvMessageBox.Show(
               aoWindow
             , asMessageText
             , asMessageCaption
             , tvMessageBoxButtons.OK
             , tvMessageBoxIcons.Error
             , true
             , tvMessageBoxCheckBoxTypes.SkipThis
             , aoProfile
             , asProfilePromptKey
             , tvMessageBoxResults.None
             );
 }
        /// <summary>
        /// Returns the "BackupOutputPathFile" base name from the given backup set profile.
        /// This is includes everything in the path\file specification except the embedded date.
        /// </summary>
        private string sBackupOutputPathFileBase(tvProfile aoBackupSetProfile)
        {
            string lsBackupOutputPathFileBase = "";

            if ( null != aoBackupSetProfile )
                lsBackupOutputPathFileBase = Path.Combine(this.sArchivePath(aoBackupSetProfile)
                        , aoBackupSetProfile.sValue("-OutputFilename", "Files"))
                        + moProfile.sValue("-BackupOutputExtension", ".zip");

            return lsBackupOutputPathFileBase;
        }
        /// <summary>
        /// Returns the total number of files to be evaluated for cleanup.
        /// </summary>
        /// <returns></returns>
        public int iCleanupFilesCount()
        {
            int liCleanupFilesCount = 0;

            // Get all cleanup sets.
            tvProfile loCleanupSetsProfile = moProfile.oOneKeyProfile("-CleanupSet");

            try
            {
                foreach (DictionaryEntry loEntry in loCleanupSetsProfile)
                {
                    // Create a profile object from the current cleanup set.
                    tvProfile loCleanupSetProfile = new tvProfile(loEntry.Value.ToString());

                    // Get all specifications of the files to delete in the current set.
                    tvProfile loFilesToCleanupProfile = loCleanupSetProfile.oOneKeyProfile("-FilesToDelete");

                    foreach (DictionaryEntry loFileToCleanup in loFilesToCleanupProfile)
                    {
                        try
                        {
                            // Count all files represented by the current file
                            // specifcation and add that number to the total.
                            liCleanupFilesCount += Directory.GetFiles(
                                      Path.GetDirectoryName(loFileToCleanup.Value.ToString())
                                    , Path.GetFileName(loFileToCleanup.Value.ToString())
                                    , SearchOption.AllDirectories).Length;
                        }
                        catch {}
                    }
                }
            }
            catch (Exception ex)
            {
                this.ShowError(ex.Message, "Cleanup Failed");
            }

            return liCleanupFilesCount;
        }
Exemple #18
0
        /// <summary>
        /// Returns a subset of the profile as a new profile. All items
        /// that match asKey will be included.
        /// </summary>
        /// <param name="asKey">
        /// The key string used to find items in the profile. "*" or a regular
        /// expression may be included.
        /// </param>
        /// <param name="abRemoveKeyPrefix">
        /// If true and asKey contains "*" or ".*", asKey (sans the wildcards)
        /// will be removed from each key prior to its addition to the new
        /// profile.
        /// </param>
        /// <returns>
        /// A new profile object containing the items found.
        /// </returns>
        public tvProfile oOneKeyProfile(String asKey, bool abRemoveKeyPrefix)
        {
            String  lsKeyPrefixToRemove = asKey.Replace(".*","").Replace("*","");
                    if ( asKey == lsKeyPrefixToRemove || "" == lsKeyPrefixToRemove )
                    {
                        // If the given key contains no wildcards, it's not really a prefix.
                        abRemoveKeyPrefix = false;
                    }
            tvProfile loProfile = new tvProfile();

            if ( mbUseLiteralsOnly )
            {
                foreach ( DictionaryEntry loEntry in this )
                {
                    String lsKey = loEntry.Key.ToString();

                    if ( lsKey == asKey )
                    {
                        if ( abRemoveKeyPrefix )
                        {
                            loProfile.Add("-" + lsKey.Replace(lsKeyPrefixToRemove, ""), loEntry.Value);
                        }
                        else
                        {
                            loProfile.Add(lsKey, loEntry.Value);
                        }
                    }
                }
            }
            else
            {
                foreach ( DictionaryEntry loEntry in this )
                {
                    String lsKey = loEntry.Key.ToString();

                    if ( Regex.IsMatch(lsKey, this.sExpression(asKey), RegexOptions.IgnoreCase) )
                    {
                        if ( abRemoveKeyPrefix )
                        {
                            loProfile.Add("-" + lsKey.Replace(lsKeyPrefixToRemove, ""), loEntry.Value);
                        }
                        else
                        {
                            loProfile.Add(lsKey, loEntry.Value);
                        }
                    }
                }
            }

            return loProfile;
        }
        /// <summary>
        /// Backup files of the given file specifications and run
        /// the backup scripts to copy the backup around as needed.
        /// </summary>
        public bool BackupFiles()
        {
            // Return if backup is disabled.
            if ( !moProfile.bValue("-BackupFiles", true) )
            {
                this.LogIt("Backup files is disabled.");
                return true;
            }
            // Return if cleanup is enabled and it was stopped.
            if (  moProfile.bValue("-CleanupFiles", true) && this.bMainLoopStopped )
                return true;
            else
                this.bMainLoopStopped = false;

            bool    lbBackupFiles = false;
            int     liBackupBeginScriptErrors = 0;          // The error count returned by the "backup begin" script.
            int     liBackupDoneScriptFileCopyFailures = 0; // The copy failures count returned by the "backup done" script.
            int     liCurrentBackupDevicesBitField = 0;     // The current backup devices returned by the "backup done" script.

            // Get the embedded zip compression tool from the EXE.
            tvFetchResource.ToDisk(Application.ResourceAssembly.GetName().Name, mcsZipToolExeFilename, null);
            tvFetchResource.ToDisk(Application.ResourceAssembly.GetName().Name, mcsZipToolDllFilename, null);

            // Get all backup sets.
            tvProfile loBackupSetsProfile = moProfile.oOneKeyProfile("-BackupSet");

            miBackupSets = loBackupSetsProfile.Count;
            miBackupSetsRun = 0;
            miBackupSetsGood = 0;

            // Release the lock on the profile file 
            // so that it can be backed up as well.
            moProfile.bEnableFileLock = false;

            try
            {
                lbBackupFiles = true;

                // Run the "backup begin" script and return any errors.
                if ( moProfile.bValue("-BackupBeginScriptEnabled", true) )
                    liBackupBeginScriptErrors = this.iBackupBeginScriptErrors();
                else
                    this.LogIt("The \"backup begin\" script is disabled.");

                string  lsZipToolFileListPathFile = moProfile.sRelativeToProfilePathFile(this.ZipToolFileListPathFile);
                string  lsBackupPathFiles1 = null;      // The first file specification in the set (for logging).
                int     liFileCount = 0;                // The number of file specifications in the current set.

                foreach (DictionaryEntry loEntry in loBackupSetsProfile)
                {
                    System.Windows.Forms.Application.DoEvents();
                    if ( this.bMainLoopStopped )
                        break;

                    string lsProcessPathFile = moProfile.sRelativeToProfilePathFile(moProfile.sValue("-ZipToolEXE", mcsZipToolExeFilename));

                    // Increment the backup set counter.
                    miBackupSetsRun++;

                    // Convert the current backup set from a command-line string to a profile oject.
                    moCurrentBackupSet = new tvProfile(loEntry.Value.ToString());

                    // Get the list of folders to backup within the current backup set.
                    tvProfile       loFolderToBackupProfile = moCurrentBackupSet.oOneKeyProfile(
                                            "-FolderToBackup");
                    StreamWriter    loBackupFileListStreamWriter = null;

                    liFileCount = 0;

                    try
                    {
                        // Create the backup file list file.
                        loBackupFileListStreamWriter = new StreamWriter(moProfile.sRelativeToProfilePathFile(
                                lsZipToolFileListPathFile), false);

                        // Write the list of files to compress.
                        foreach (DictionaryEntry loFolderToBackup in loFolderToBackupProfile)
                        {
                            System.Windows.Forms.Application.DoEvents();
                            if ( this.bMainLoopStopped )
                                break;

                            string  lsFolderToBackup = loFolderToBackup.Value.ToString().Trim();
                            string  lsBackupPathFiles = null;
                                    if ( Directory.Exists(lsFolderToBackup) )
                                        lsBackupPathFiles = Path.Combine(lsFolderToBackup, this.sBackupFileSpec());
                                    else
                                        lsBackupPathFiles = lsFolderToBackup;
                                        // If the given "lsFolderToBackup" is not actually a folder, assume it's a single file (or a file mask).

                            if ( 1 == ++liFileCount )
                            {
                                if ( moProfile.ContainsKey("-ActivateAlreadyRunningInstance") )
                                {
                                    // Backup just the backup profile file as well (to avoid file contention problems with the main instance).
                                    loBackupFileListStreamWriter.WriteLine(this.sExeRelativePath(lsProcessPathFile, moProfile.sLoadedPathFile));
                                }
                                else
                                {
                                    // Backup the backup as well.
                                    loBackupFileListStreamWriter.WriteLine(this.sExeRelativePath(lsProcessPathFile, Path.GetDirectoryName(moProfile.sLoadedPathFile)));

                                    // Also make an extra - easily accessible - backup of just the profile file.
                                    lsBackupPathFiles1 = moProfile.sBackupPathFile;     // Discard the pathfile name.
                                }

                                // Save the first pathfile specification (ie. folder) for later reference.
                                lsBackupPathFiles1 = lsBackupPathFiles;
                            }

                            loBackupFileListStreamWriter.WriteLine(this.sExeRelativePath(lsProcessPathFile, lsBackupPathFiles));
                        }
                    }
                    catch (Exception ex)
                    {
                        lbBackupFiles = false;
                        if ( null != this.oUI )
                        this.oUI.bBackupRunning = false;
                        this.ShowError(string.Format("File Write Failure: \"{0}\"\r\n"
                                , lsZipToolFileListPathFile) + ex.Message
                                , "Failed Writing File"
                                );
                    }
                    finally
                    {
                        if ( null != loBackupFileListStreamWriter )
                            loBackupFileListStreamWriter.Close();
                    }

                    // The backup output path file will be dated and unique.
                    msCurrentBackupOutputPathFile = this.sBackupOutputPathFile();

                    string  lsProcessArgs = moProfile.sValue("-ZipToolEXEargs"
                                    , "a -ssw \"{BackupOutputPathFile}\" @\"{BackupPathFiles}\" -w\"{BackupOutputPath}\" ")
                                    + " " + moProfile.sValue("-ZipToolEXEargsMore", "");
                            lsProcessArgs = lsProcessArgs.Replace("{BackupPathFiles}", lsZipToolFileListPathFile);
                            lsProcessArgs = lsProcessArgs.Replace("{BackupOutputPath}", Path.GetDirectoryName(msCurrentBackupOutputPathFile));
                            lsProcessArgs = lsProcessArgs.Replace("{BackupOutputPathFile}", msCurrentBackupOutputPathFile);
                    string  lsArchivePath = Path.GetDirectoryName(msCurrentBackupOutputPathFile);
                            if ( !Directory.Exists(lsArchivePath) )
                                try
                                {
                                    Directory.CreateDirectory(lsArchivePath);
                                }
                                catch (Exception ex)
                                {
                                    lbBackupFiles = false;
                                    if ( null != this.oUI )
                                    this.oUI.bBackupRunning = false;
                                    this.ShowError(
                                              string.Format("Folder: \"{0}\"\r\n", lsArchivePath) + ex.Message
                                            , "Error Creating Archive Folder"
                                            );
                                }

                    Process loProcess = new Process();
                            loProcess.ErrorDataReceived += new DataReceivedEventHandler(this.BackupProcessOutputHandler);
                            loProcess.OutputDataReceived += new DataReceivedEventHandler(this.BackupProcessOutputHandler);
                            loProcess.StartInfo.FileName = lsProcessPathFile;
                            loProcess.StartInfo.Arguments = lsProcessArgs;
                            loProcess.StartInfo.WorkingDirectory = "\\";
                            loProcess.StartInfo.UseShellExecute = false;
                            loProcess.StartInfo.RedirectStandardError = true;
                            loProcess.StartInfo.RedirectStandardInput = true;
                            loProcess.StartInfo.RedirectStandardOutput = true;
                            loProcess.StartInfo.CreateNoWindow = true;

                    string  lsLastRunCmd = lsProcessPathFile + " " + lsProcessArgs;
                    string  lsFileAsStream = string.Format(@"
cd\
@prompt $
{0}
@echo.
cd {1}
@pause
"                                           , lsLastRunCmd
                                            , Path.GetDirectoryName(moProfile.sExePathFile)
                                            );

                    moProfile.Remove("-PreviousBackupOk");
                    moProfile.Save();

                    // This lets the user see what was run (or rerun it).
                    string          lsLastRunFile = moProfile.sValue("-ZipToolLastRunCmdPathFile", "Run Last Backup.cmd");
                    StreamWriter    loLastRunFileStreamWriter = null;

                    try
                    {
                        loLastRunFileStreamWriter = new StreamWriter(moProfile.sRelativeToProfilePathFile(lsLastRunFile), false);
                        loLastRunFileStreamWriter.Write(lsFileAsStream);
                    }
                    catch (Exception ex)
                    {
                        lbBackupFiles = false;
                        if ( null != this.oUI )
                        this.oUI.bBackupRunning = false;
                        this.ShowError(string.Format("File Write Failure: \"{0}\"\r\n"
                                , lsLastRunFile) + ex.Message
                                , "Failed Writing File"
                                );
                    }
                    finally
                    {
                        if ( null != loLastRunFileStreamWriter )
                            loLastRunFileStreamWriter.Close();
                    }

                    string  lsFilesSuffix = liFileCount <= 1 ? ""
                            : string.Format(" + {0} other file specification" + (1 == (liFileCount - 1) ? "" : "s")
                                    , liFileCount - 1);
                                
                    this.LogIt("");
                    this.LogIt("Backup started ...");
                    this.LogIt("");
                    this.LogIt(string.Format("Backup: \"{0}\"{1}", lsBackupPathFiles1, lsFilesSuffix));
                    this.LogIt("");
                    this.LogIt("Backup command: " + lsLastRunCmd);

                    System.Windows.Forms.Application.DoEvents();

                    loProcess.Start();

                    // Start output to console also.
                    if ( loProcess.StartInfo.RedirectStandardError )
                        loProcess.BeginErrorReadLine();
                    if ( loProcess.StartInfo.RedirectStandardOutput )
                        loProcess.BeginOutputReadLine();

                    // Wait for the backup process to finish.
                    while ( !this.bMainLoopStopped && !loProcess.HasExited )
                    {
                        System.Windows.Forms.Application.DoEvents();
                        System.Threading.Thread.Sleep(moProfile.iValue("-MainLoopSleepMS", 100));
                    }

                    // Do final wait then call "WaitForExit()" to flush the output steams.
                    if ( !this.bMainLoopStopped && loProcess.WaitForExit(1000 * moProfile.iValue("-KillProcessOrderlyWaitSecs", 30)) )
                        loProcess.WaitForExit();

                    // Stop output to console.
                    if ( loProcess.StartInfo.RedirectStandardError )
                        loProcess.CancelErrorRead();
                    if ( loProcess.StartInfo.RedirectStandardOutput )
                        loProcess.CancelOutputRead();

                    // If a stop request came through, kill the backup process.
                    if ( this.bMainLoopStopped && !this.bKillProcess(loProcess) )
                        this.ShowError("The backup process could not be stopped."
                                , "Backup Failed");

                    // The backup process finished uninterrupted.
                    if ( !this.bMainLoopStopped )
                    {
                        if ( 0 != loProcess.ExitCode )
                        {
                            // The backup process failed.
                            this.LogIt(string.Format("The backup to \"{0}\" failed."
                                    , Path.GetFileName(msCurrentBackupOutputPathFile)));

                            // Run the "backup failed" script.
                            if ( moProfile.bValue("-BackupFailedScriptEnabled", true) )
                                this.BackupFailedScript();
                            else
                                this.LogIt("The \"backup failed\" script is disabled.");
                        }
                        else
                        {
                            // The backup process finished without error.
                            this.LogIt(string.Format("The backup to \"{0}\" was successful."
                                    , Path.GetFileName(msCurrentBackupOutputPathFile)));

                            double ldCompositeResult = 0;   // Composite result returned by the "backup done" script.

                            // Run the "backup done" script and return the failed file count with bit field.
                            // The exit code is defined in the script as a combination of two integers:
                            // a bit field of found backup devices and a count of copy failures (99 max).
                            if ( moProfile.bValue("-BackupDoneScriptEnabled", true) )
                                ldCompositeResult = this.iBackupDoneScriptCopyFailuresWithBitField() / 100.0;
                            else
                                this.LogIt("The \"backup done\" script is disabled.");

                            // The integer part of the composite number is the bit field.
                            // This will be used below after all backup sets are run. We
                            // assume that the bit field remains constant between sets.
                            liCurrentBackupDevicesBitField = (int)ldCompositeResult;

                            // The fractional part (x 100) is the actual number of copy failures.
                            int liCopyFailures = (int)Math.Round(100 * (ldCompositeResult - liCurrentBackupDevicesBitField));

                            // Add failed files from the current backup set to the total.
                            liBackupDoneScriptFileCopyFailures += liCopyFailures;

                            // Increment how many backup sets have succeeded so far.
                            if ( 0 == liCopyFailures )
                                miBackupSetsGood++;
                        }
                    }

                    loProcess.Close();
                }
            }
            catch (Exception ex)
            {
                lbBackupFiles = false;
                if ( null != this.oUI )
                this.oUI.bBackupRunning = false;
                this.ShowError(ex.Message, "Backup Failed");
            }

            if ( lbBackupFiles && !this.bMainLoopStopped )
            {
                this.LogIt("");

                // Don't bother with the system tray message 
                // if the background timer is not running.
                string  lsSysTrayMsg = null;
                        if ( moProfile.bValue("-AutoStart", true) )
                            lsSysTrayMsg = this.sSysTrayMsg;

                List<char> loMissingBackupDevices = new List<char>();

                // Compare the bit field of current backup devices to the bit field of
                // devices selected by the user to be used by the "backup done" script.
                if ( moProfile.bValue("-BackupDoneScriptEnabled", true) )
                    loMissingBackupDevices = this.oMissingBackupDevices(liCurrentBackupDevicesBitField);

                if (        miBackupSetsGood != miBackupSetsRun
                        ||  0 != liBackupBeginScriptErrors
                        ||  0 != liBackupDoneScriptFileCopyFailures
                        ||  0 != loMissingBackupDevices.Count
                        )
                {
                    if ( 0 != loMissingBackupDevices.Count )
                        moProfile["-PreviousBackupDevicesMissing"] = true;
                    else
                        moProfile["-PreviousBackupDevicesMissing"] = false;

                    this.SetBackupFailed();

                    lbBackupFiles = false;
                    if ( null != this.oUI )
                    this.oUI.bBackupRunning = false;

                    this.ShowError("The backup failed. Check the log for errors." + lsSysTrayMsg
                            , "Backup Failed");
                }
                else
                {
                    moProfile["-PreviousBackupDevicesMissing"] = false;
                    moProfile["-PreviousBackupOk"] = true;
                    moProfile["-PreviousBackupTime"] = DateTime.Now;
                    moProfile.Save();

                    this.Show("Backup finished successfully." + lsSysTrayMsg
                            , "Backup Finished"
                            , tvMessageBoxButtons.OK
                            , tvMessageBoxIcons.Done
                            , "-CurrentBackupFinished"
                            );
                }
            }

            if ( this.bMainLoopStopped )
            {
                this.LogIt("Backup process stopped.");
                this.bMainLoopStopped = false;
                lbBackupFiles = false;
            }

            // Turn on the lock that was turned off when we started.
            moProfile.bEnableFileLock = true;

            return lbBackupFiles;
        }
Exemple #20
0
        /// <summary>
        /// The "<see cref="GetAdd"/> Object" value found for asKey.
        /// </summary>
        /// <param name="asKey">
        /// The key string used to find the corresponding value in the profile.
        /// </param>
        /// <returns>
        /// The tvProfile object value found or a default empty
        /// tvProfile object will be added to the profile, see <see cref="GetAdd"/>).
        /// </returns>
        public tvProfile oProfile(String asKey)
        {
            object  loProfile = this.GetAdd(asKey, new tvProfile());
            object  loProfileCast = loProfile as tvProfile;
                    if ( null == loProfileCast )
                        loProfileCast = new tvProfile(loProfile.ToString());

            return (tvProfile)loProfileCast;
        }
        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]);
                    }
                }
        }
Exemple #22
0
        private void ReplaceDefaultProfileFromCommandLine(String[] asCommandLineArray)
        {
            this.LoadFromCommandLineArray(asCommandLineArray, tvProfileLoadActions.Overwrite);

            String[] lsIniKeys = new String[] { "-ini", "-ProfileFile" };

            int     liIniKeyIndex = - 1;
                    if (      this.ContainsKey(lsIniKeys[0]) )
                    {
                        liIniKeyIndex = 0;
                    }
                    else if ( this.ContainsKey(lsIniKeys[1]) )
                    {
                        liIniKeyIndex = 1;
                    }
            String  lsProfilePathFile = null;
                    if ( -1 != liIniKeyIndex )
                    {
                        lsProfilePathFile = this.sValue(lsIniKeys[liIniKeyIndex], "");
                    }
            bool    lbFirstArgIsFile = false;
            String  lsFirstArg = null;
                    try
                    {
                        if ( -1 != this.sInputCommandLineArray[0].IndexOf(".vshost.")
                                || this.sInputCommandLineArray[0] == this.sExePathFile )
                        {
                            lsFirstArg = this.sInputCommandLineArray[1];
                        }
                        else
                        {
                            lsFirstArg = this.sInputCommandLineArray[0];
                        }
                    }
                    catch {}

            if ( null != lsFirstArg
                    && File.Exists(this.sRelativeToProfilePathFile(lsFirstArg)) )
            {
                if ( null != lsProfilePathFile )
                {
                    // If the first argument passed on the command line is actually
                    // a file (that exists) and if an -ini key was also provided, then
                    // add the file reference to the profile using the "-File" key.
                    lbFirstArgIsFile = true;
                }
                else
                {
                    // If no -ini key was passed, then assume the referenced file is
                    // actually a profile file to be loaded.
                    lsProfilePathFile = lsFirstArg;
                }
            }

            if ( null != lsProfilePathFile )
            {
                // Load the referenced profile file.
                tvProfile   loNewProfile = new tvProfile();
                            loNewProfile.eFileCreateAction = this.eFileCreateAction;
                            loNewProfile.bUseXmlFiles = this.bUseXmlFiles;
                            loNewProfile.bAddStandardDefaults = this.bAddStandardDefaults;
                            loNewProfile.Load(lsProfilePathFile, tvProfileLoadActions.Overwrite);

                            this.sActualPathFile = loNewProfile.sActualPathFile;
                            this.sLoadedPathFile = loNewProfile.sLoadedPathFile;
                            this.bExit = loNewProfile.bExit;

                if ( !this.bExit )
                {
                    this.bFileJustCreated = loNewProfile.bFileJustCreated;

                    // We now need a slightly modified version of the given command line
                    // (ie. sans the -ini key but with a -File key, if appropriate).
                    tvProfile   loCommandLine = new tvProfile();
                                loCommandLine.LoadFromCommandLineArray(
                                        this.sInputCommandLineArray, tvProfileLoadActions.Overwrite);
                                if ( -1 != liIniKeyIndex )
                                    loCommandLine.Remove(lsIniKeys[liIniKeyIndex]);
                                if ( lbFirstArgIsFile )
                                    loCommandLine.Add("-File", lsFirstArg);

                    // Now merge in the original command line (with the above
                    // adjustments). Command line items take precedence over file items.
                    loNewProfile.LoadFromCommandLineArray(loCommandLine.sCommandLineArray(), tvProfileLoadActions.Merge);
                    this.bSaveEnabled = loNewProfile.bSaveEnabled;

                    // Reinitiallize the profile with the new combined results.
                    this.LoadFromCommandLineArray(loNewProfile.sCommandLineArray(), tvProfileLoadActions.Overwrite);
                    this.bDefaultFileReplaced = true;
                }

                loNewProfile.UnlockProfileFile();
            }
        }
        public void BackupFailedScript()
        {
            // Before the "backup failed" script can be initialized,
            // -BackupFailedScriptPathFile and -BackupFailedScriptHelp
            // must be initialized first.
            if ( moProfile.bValue("-BackupFailedScriptInit", false) )
            {
                moProfile.Remove("-BackupFailedScriptPathFile");
                moProfile.Remove("-BackupFailedScriptHelp");
            }

            string  lsBackupFailedScriptPathFile = moProfile.sRelativeToProfilePathFile(
                    moProfile.sValue("-BackupFailedScriptPathFile", msBackupFailedScriptPathFileDefault));
            string  lsBackupFailedScriptOutputPathFile = lsBackupFailedScriptPathFile + ".txt";

            // If the "backup failed" script has not been redefined to point elsewhere,
            // prepare to create it from the current -BackupFailedScriptHelp content.
            // We do this even if the script file actually exists already. This way
            // the following default script will be written to the profile file if
            // it's not already there.
            if ( lsBackupFailedScriptPathFile == moProfile.sRelativeToProfilePathFile(msBackupFailedScriptPathFileDefault) )
            {
                string  lsBackupFailedScript = moProfile.sValue("-BackupFailedScriptHelp", @"
@echo off
if %1=="""" goto :EOF
::
:: *** ""Backup Failed"" script goes here. ***
::
:: This script is executed after each backup fails to complete. If you 
:: prompt for input within this DOS script (eg. ""pause""), the script
:: will stay in memory. This is not recommended since such behavior would
:: be similar to a memory leak.
::
:: You can also create and edit another DOS script file and reference that
:: instead (see ""-BackupFailedScriptPathFile"" in ""{ProfileFile}""). You
:: can access several parameters from the completed backup via the DOS shell
:: command-line:
::
:: %1 = ""BackupOutputPathFile""
::
::      This is the full path\file specification of the backup file.
::      It includes the output filename as well as the embedded date.
::
:: %2 = ""BackupOutputFilename""
::
::      This is the backup filename only (ie. no path). It includes the
::      embedded date as well as the filename extension.
::
:: %3 = ""BackupBaseOutputFilename""
::
::      This is the backup filename with no path and no date. It's just
::      the base output filename name with the filename extension.
::
:: %4 = ""LocalArchivePath""
::
::      This is the local archive folder.
::
:: %5 = ""VirtualMachineHostArchive""
::
::      This is the virtual machine host archive share name.
::
::
:: Note: All arguments will be passed with double quotation marks included.
::       So don't use quotes here unless you want ""double double"" quotes.
::       Also, ERRORLEVEL is not reliable enough to be heavily used below.
::
:: The following example copies the backup file to the root of drive C:
:: (if it's ""AdministratorFiles.zip""). Then it outputs a directory listing
:: of the archive folder.
::
:: Example:
::
:: if not %3.==""AdministratorFiles.zip"". goto :EOF
::
::     echo copy %1 C:\  ] ""{BackupFailedScriptOutputPathFile}"" 2>&1
::          copy %1 C:\ ]] ""{BackupFailedScriptOutputPathFile}"" 2>&1
::
:: dir %4               ]] ""{BackupFailedScriptOutputPathFile}"" 2>&1
::
::                      ^^  Replace brackets with darts.

:: Initialize the ""backup failed"" script log file. It's for this run only.
echo.                    > ""{BackupFailedScriptOutputPathFile}"" 2>&1


:: Any failed backup file less than this size will be removed.
set MinFileBytes=1024

set Filesize=%~z1

:: This references the backup file by named variable (rather than positionally):
set FileSpec=%1

echo If the failed backup file is smaller than %MinFileBytes% bytes,            >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo it will be removed.                                                        >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo.                                                                           >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo %FileSpec% is %Filesize% bytes.                                            >> ""{BackupFailedScriptOutputPathFile}"" 2>&1

if %Filesize% lss %MinFileBytes% goto RemoveIt

echo.                                                                           >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo The file is not smaller than the minimmum (%MinFileBytes% bytes). Keep it. >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
goto :EOF


:RemoveIt
echo.                                                                           >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo The file is smaller than the minimmum (%MinFileBytes% bytes). Remove it.   >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo.                                                                           >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo This removes the failed backup file:                                       >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo.                                                                           >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
echo del %FileSpec%                                                             >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
     del %FileSpec%                                                             >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
     if exist %FileSpec% echo   Error: %FileSpec% is still there.               >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
     if not exist %FileSpec% echo     %FileSpec% has been removed.              >> ""{BackupFailedScriptOutputPathFile}"" 2>&1
"
)
                        .Replace("{ProfileFile}", Path.GetFileName(moProfile.sLoadedPathFile))
                        .Replace("{BackupFailedScriptOutputPathFile}", Path.GetFileName(lsBackupFailedScriptOutputPathFile))
                        ;

                // Write the default "backup failed" script if it's
                // not there or if -BackupFailedScriptInit is set.
                if (       !File.Exists(lsBackupFailedScriptPathFile)
                        || moProfile.bValue("-BackupFailedScriptInit", false) )
                {
                    StreamWriter loStreamWriter = null;

                    try
                    {
                        loStreamWriter = new StreamWriter(lsBackupFailedScriptPathFile, false);
                        loStreamWriter.Write(lsBackupFailedScript);

                        // This is used only once then reset.
                        moProfile["-BackupFailedScriptInit"] = false;
                        moProfile.Save();
                    }
                    catch (Exception ex)
                    {
                        this.ShowError(string.Format("File Write Failure: \"{0}\"\r\n"
                                , lsBackupFailedScript) + ex.Message
                                , "Failed Writing File"
                                );
                    }
                    finally
                    {
                        if ( null != loStreamWriter )
                            loStreamWriter.Close();
                    }
                }
            }

            try
            {
                this.LogIt("");
                this.LogIt("Running \"backup failed\" script ...");

                // Cache the arguments to be passed to the script.
                tvProfile  loArgs = new tvProfile();
                                loArgs.Add("-BackupOutputPathFile"          , msCurrentBackupOutputPathFile                                          );
                                loArgs.Add("-BackupOutputFilename"          , Path.GetFileName(msCurrentBackupOutputPathFile)                        );
                                loArgs.Add("-BackupBaseOutputFilename"      , Path.GetFileName(this.sBackupOutputPathFileBase())                     );
                                loArgs.Add("-LocalArchivePath"              , this.sArchivePath()                                                    );
                                loArgs.Add("-VirtualMachineHostArchivePath" , moProfile.sValue("-VirtualMachineHostArchivePath", "")                 );

                                moProfile["-BackupFailedArgs"] = loArgs.sCommandBlock();
                                moProfile.Save();

                // Run the "backup failed" script.
                Process loProcess = new Process();
                        loProcess.StartInfo.FileName = lsBackupFailedScriptPathFile;
                        loProcess.StartInfo.Arguments = string.Format(
                                  " \"{0}\" \"{1}\" \"{2}\" \"{3}\" \"{4}\" \"{5}\" \"{6}\" \"{7}\" \"{8}\" \"{9}\" "
                                , loArgs.sValue("-BackupOutputPathFile"         , "")
                                , loArgs.sValue("-BackupOutputFilename"         , "")
                                , loArgs.sValue("-BackupBaseOutputFilename"     , "")
                                , loArgs.sValue("-LocalArchivePath"             , "")
                                , loArgs.sValue("-VirtualMachineHostArchivePath", "")
                                , ""
                                , ""
                                , ""
                                , ""
                                , ""
                                );
                        loProcess.StartInfo.UseShellExecute = true;
                        loProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                        loProcess.Start();

                // Wait for the "backup failed" script to finish.
                while ( !this.bMainLoopStopped && !loProcess.HasExited )
                {
                    System.Windows.Forms.Application.DoEvents();
                    System.Threading.Thread.Sleep(moProfile.iValue("-MainLoopSleepMS", 100));
                }

                // If a stop request came through, kill the "backup failed" script.
                if ( this.bMainLoopStopped && !this.bKillProcess(loProcess) )
                    this.ShowError("The \"backup failed\" script could not be stopped."
                            , "Backup Failed");

                if ( !this.bMainLoopStopped )
                {
                    if ( 0 == loProcess.ExitCode )
                    {
                        this.LogIt("The \"backup failed\" script finished successfully.");
                    }
                    else
                    {
                        this.LogIt("The \"backup failed\" script did NOT finish successfully.");
                    }
  
                    // Get the output from the "backup failed" script.

                    this.LogIt("\r\nHere's output from the \"backup failed\" script:\r\n\r\n"
                            + this.sFileAsStream(lsBackupFailedScriptOutputPathFile));
                }

                loProcess.Close();
            }
            catch (Exception ex)
            {
                this.SetBackupFailed();
                this.ShowError(ex.Message, "Failed Running \"Backup Failed\" Script");
            }
        }
Exemple #24
0
 public tvProfileEnumerator(tvProfile aoProfile)
 {
     moProfile = aoProfile;
 }
        /// <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;
        }
        /// <summary>
        /// Returns the "ArchivePath" from the given backup set profile.
        /// </summary>
        public string sArchivePath(tvProfile aoBackupSetProfile)
        {
            string lsArchivePath = moProfile.sValue("-ArchivePath", 
                    Path.Combine(Path.GetPathRoot(Environment.GetFolderPath(
                            Environment.SpecialFolder.System)), "Archive"));

            // Don't create a default "-ArchivePath" in the backup set (that's
            // what "ContainsKey" prevents). That way the global "-ArchivePath" 
            // will stay in force unless specifically overridden.
            if ( null != aoBackupSetProfile && aoBackupSetProfile.ContainsKey("-ArchivePath") )
                lsArchivePath = aoBackupSetProfile["-ArchivePath"].ToString();

            return lsArchivePath;
        }
        public static void ActivateAlreadyRunningInstance(string[] args, tvProfile aoProfile)
        {
            if ( 0 == args.Length )
            {
                // This activates a previous instance before exiting.
                PostMessage((IntPtr)HWND_BROADCAST, WM_SHOWME, IntPtr.Zero, IntPtr.Zero);
            }
            else
            {
                tvProfile loProfile = null;

                if ( null != aoProfile.sLoadedPathFile )
                {
                    loProfile = aoProfile;
                }
                else
                {
                    aoProfile.bAppFullyLoaded = true;   // Turns off the "loading" message.

                    // The default profile was likely attempted but failed to load
                    // (due to contention). Try loading the backup profile instead.

                    string  lsPath = Path.GetDirectoryName(aoProfile.sDefaultPathFile);
                    string  lsFilename = Path.GetFileName(aoProfile.sDefaultPathFile);
                    string  lsExt = Path.GetExtension(aoProfile.sDefaultPathFile);
                    string  lsBackupPathFile = Path.Combine(lsPath, lsFilename) + ".backup" + lsExt;

                    loProfile = new tvProfile(lsBackupPathFile, tvProfileFileCreateActions.NoFileCreate);
                    loProfile.LoadFromCommandLineArray(args, tvProfileLoadActions.Merge);
                }

                // Arguments were passed, so start a new "-RunOnce" instance.
                loProfile.LoadFromCommandLine("-ActivateAlreadyRunningInstance -RunOnce", tvProfileLoadActions.Merge);
                loProfile.bAppFullyLoaded = true;   // Turns off the "loading" message.

                DoGoPcBackup    loDoDa = new DoGoPcBackup(loProfile);
                                loDoDa.CleanupFiles();
                                loDoDa.BackupFiles();
            }
        }
        /// <summary>
        /// Returns the "BackupFileSpec" from the given backup set profile.
        /// </summary>
        public string sBackupFileSpec(tvProfile aoBackupSetProfile)
        {
            string lsBackupFileSpec = moProfile.sValue("-BackupFileSpec", "*");

            // Don't create a default "-BackupFileSpec" in the backup set (that's
            // what "ContainsKey" prevents). That way the global "-BackupFileSpec"
            // will stay in force unless specifically overridden.
            if ( null != aoBackupSetProfile && aoBackupSetProfile.ContainsKey("-BackupFileSpec") )
                lsBackupFileSpec = aoBackupSetProfile["-BackupFileSpec"].ToString();

            return lsBackupFileSpec;
        }
        static void Main( string[] args )
        {
            // Generate a Profile object. When this project is near the end of its development,
            // we will need to remove the leading "..\\"'s from this file path.
            if ( File.Exists("Profile.txt"))
            {
                Profile = new tvProfile("Profile.txt", false);
            }
            // If it doesn't exist locally, use the embedded one.
            else
            {
                // Ensure that the pack scheme is known: http://stackoverflow.com/questions/6005398/uriformatexception-invalid-uri-invalid-port-specified
                string ensurePackSchemeIsKnown = PackUriHelper.UriSchemePack;

                StreamResourceInfo stream = Application.GetResourceStream(new Uri(PATH_TO_PROFILE, UriKind.Absolute));
                StreamReader reader = new StreamReader(stream.Stream);
                Profile = new tvProfile(reader.ReadToEnd());
            }

            // Create a new deck.
            Deck = new Deck(Profile);

            // Create a new list of players.
            PlayerList = new List<Player>();

            // Create a new Dicard Pile.
            DiscardPile = new List<Card>();

            // Create new instance of configs. Parameter is "application Id". It has to be same on client and server.
            Config = new NetPeerConfiguration("game");

            // Set server port
            Config.Port = ServerUtilities.PORT_NUMBER;

            // Max client amount
            Config.MaximumConnections = 200;

            // Enable New messagetype. Explained later
            Config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);

            // Create new server based on the configs just defined
            Server = new NetServer(Config);

            // Start it
            Server.Start();

            // Eh..
            Console.WriteLine("Server Started");

            // Object that can be used to store and read messages
            NetIncomingMessage inc;

            // Check time
            DateTime time = DateTime.Now;

            // Create timespan of 30ms
            TimeSpan timetopass = new TimeSpan(0, 0, 0, 0, 30);

            // Write to con..
            Console.WriteLine("Waiting for new connections");

            // Main loop
            // This kind of loop can't be made in XNA. In there, its basically same, but without while
            // Or maybe it could be while(new messages)
            while ( true )
            {
                // Server.ReadMessage() Returns new messages, that have not yet been read.
                // If "inc" is null -> ReadMessage returned null -> Its null, so dont do this :)
                if ( (inc = Server.ReadMessage()) != null )
                {
                    // Theres few different types of messages. To simplify this process, i left only 2 of em here
                    switch ( inc.MessageType )
                    {
                        // If incoming message is Request for connection approval
                        // This is the very first packet/message that is sent from client
                        // Here you can do new player initialisation stuff
                        case NetIncomingMessageType.ConnectionApproval:
                        {
                            // Read the first byte of the packet
                            // ( Enums can be casted to bytes, so it be used to make bytes human readable )
                            if ( inc.ReadByte() == (byte)PacketTypes.LOGIN )
                            {
                                Console.WriteLine("Incoming LOGIN");

                                // Approve clients connection ( Its sort of agreenment. "You can be my client and i will host you" )
                                inc.SenderConnection.Approve();

                                // Debug
                                Console.WriteLine("Approved new connection and updated the world status");
                            }

                            break;
                        }
                        // All messages manually sent from clients are considered "Data" messages.
                        // ( Approval is an automated process )
                        case NetIncomingMessageType.Data:
                        {
                            Datatype messageType = (Datatype)inc.ReadByte();

                            switch ( messageType )
                            {
                                // Receive an updated Deck from a client.
                                case Datatype.UpdateDeck:
                                {
                                    Deck = (Deck)ServerUtilities.ReceiveMessage(inc, messageType);

                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.UpdateDeck, Deck);
                                    }
                                    break;
                                }

                                // Receive an updated DiscardPile from a client.
                                case Datatype.UpdateDiscardPile:
                                {
                                    DiscardPile = (List<Card>)ServerUtilities.ReceiveMessage(inc, messageType);

                                    // Send the updated DiscardPile to all clients.
                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.UpdateDiscardPile, DiscardPile);
                                    }

                                    break;
                                }

                                // Add or modify a player in the PlayerList.
                                case Datatype.UpdatePlayer:
                                {
                                    Player updatedPlayer = (Player)ServerUtilities.ReceiveMessage(inc, messageType);
                                    bool isPlayerInList = false;

                                    // If the updated Player is already in the server's list, update that's Player's properties.
                                    // Note: This search only works if players have unique names.
                                    foreach ( Player player in PlayerList )
                                    {
                                        if ( updatedPlayer.Name == player.Name )
                                        {
                                            player.CardsInHand = updatedPlayer.CardsInHand;
                                            player.CardsInPlay = updatedPlayer.CardsInPlay;
                                            isPlayerInList = true;
                                            break;
                                        }
                                    }

                                    // If the Player is not on the list, add it.
                                    if ( !isPlayerInList )
                                    {
                                        PlayerList.Add(updatedPlayer);
                                    }

                                    // Send the updated PlayerList to all clients.
                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.UpdatePlayerList, PlayerList);
                                    }
                                    break;
                                }

                                // Update the server's Player List and send it to the clients.
                                case Datatype.UpdatePlayerList:
                                {
                                    PlayerList = (List<Player>)ServerUtilities.ReceiveMessage(inc, messageType);

                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.UpdatePlayerList, PlayerList);
                                    }

                                    break;
                                }

                                // Send the updated turn to all players.
                                case Datatype.UpdateTurn:
                                {
                                    Turn = (Turn)ServerUtilities.ReceiveMessage(inc, messageType);

                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.UpdateTurn, Turn);
                                    }

                                    break;
                                }

                                // Set up the players for the game. This case should be hit only when a client launches the game.
                                case Datatype.LaunchGame:
                                {
                                    // Deal the initial hands to the players.
                                    for ( int i = 0; i < PlayerList.Count; ++i )
                                    {
                                        PlayerList[i] = new Player(Deck, PlayerList[i].Name);
                                    }

                                    //// Send the Player List to the clients.
                                    //if ( Server.ConnectionsCount != 0 )
                                    //{
                                    //    ServerUtilities.SendMessage(Server, Datatype.UpdatePlayerList, PlayerList);
                                    //}

                                    // Generate the Turn object to keep track of the current turn.
                                    Turn = new Turn(PlayerList.Count);

                                    // Tell all clients to launch the game and send them the Turn object.
                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.LaunchGame, Turn);
                                    }

                                    break;
                                }

                                case Datatype.TimeToConnect:
                                {
                                    string playerToConnect = (String)ServerUtilities.ReceiveMessage(inc, messageType);

                                    // Broadcast a message that tells a specific client to launch the game.
                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.TimeToConnect, playerToConnect);
                                    }

                                    break;
                                }

                                // Send the rent request to all clients.
                                case Datatype.RequestRent:
                                {
                                    ActionData.RentRequest request = (ActionData.RentRequest)ServerUtilities.ReadRentRequest(inc);

                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.RequestRent, request);
                                    }

                                    break;
                                }

                                // Send the rent response to all clients.
                                case Datatype.GiveRent:
                                {
                                    ActionData.RentResponse response = (ActionData.RentResponse)ServerUtilities.ReadRentResponse(inc);

                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.GiveRent, response);
                                    }

                                    break;
                                }

                                // Send the theft request to all clients.
                                case Datatype.RequestTheft:
                                {
                                    ActionData.TheftRequest request = (ActionData.TheftRequest)ServerUtilities.ReadTheftRequest(inc);

                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.RequestTheft, request);
                                    }

                                    break;
                                }

                                // Send the theft response to all clients.
                                case Datatype.ReplyToTheft:
                                {
                                    ActionData.TheftResponse response = (ActionData.TheftResponse)ServerUtilities.ReadTheftResponse(inc);

                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.ReplyToTheft, response);
                                    }

                                    break;
                                }

                                case Datatype.EndTurn:
                                {
                                    Turn = (Turn)ServerUtilities.ReceiveMessage(inc, Datatype.EndTurn);

                                    // Send the updated Turn object to the clients.
                                    ServerUtilities.SendMessage(Server, Datatype.EndTurn, Turn);

                                    break;
                                }

                                // Send the server's Deck to all clients.
                                case Datatype.RequestDeck:
                                {
                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.UpdateDeck, Deck);
                                    }

                                    break;
                                }

                                // Send the server's PlayerList to all clients.
                                case Datatype.RequestPlayerList:
                                {
                                    if ( Server.ConnectionsCount != 0 )
                                    {
                                        ServerUtilities.SendMessage(Server, Datatype.UpdatePlayerList, PlayerList);
                                    }

                                    break;
                                }
                            }

                            break;
                        }
                        case NetIncomingMessageType.StatusChanged:
                        {
                            // In case status changed
                            // It can be one of these
                            // NetConnectionStatus.Connected;
                            // NetConnectionStatus.Connecting;
                            // NetConnectionStatus.Disconnected;
                            // NetConnectionStatus.Disconnecting;
                            // NetConnectionStatus.None;

                            // NOTE: Disconnecting and Disconnected are not instant unless client is shutdown with disconnect()
                            Console.WriteLine(inc.SenderConnection.ToString() + " status changed. " + (NetConnectionStatus)inc.SenderConnection.Status);
                            if ( inc.SenderConnection.Status == NetConnectionStatus.Disconnected || inc.SenderConnection.Status == NetConnectionStatus.Disconnecting )
                            {

                            }
                            break;
                        }
                        default:
                        {
                            // As i statet previously, theres few other kind of messages also, but i dont cover those in this example
                            // Uncommenting next line, informs you, when ever some other kind of message is received
                            //Console.WriteLine("Not Important Message");
                            break;
                        }
                    }
                } // If New messages

                // if 30ms has passed
                if ( (time + timetopass) < DateTime.Now )
                {
                    time = DateTime.Now;
                }

                // While loops run as fast as your computer lets. While(true) can lock your computer up. Even 1ms sleep, lets other programs have piece of your CPU time
                System.Threading.Thread.Sleep(100);
            }
        }
 /// <summary>
 /// Returns the "BackupOutputPathFile" name from the given backup set profile.
 /// </summary>
 private string sBackupOutputPathFile(tvProfile aoBackupSetProfile)
 {
     return this.sUniqueIntOutputPathFile(
               this.sBackupOutputPathFileBase(aoBackupSetProfile)
             , moProfile.sValue("-BackupOutputFilenameDateFormat", "-yyyy-MM-dd")
             , false
             );
 }