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; }