' Windows Image Manager
' This program is intended to be run on Windows 10 64-Bit and compiled with 64-Bit QB64 1.3.
' We are running a lot of Windows command line commands so we are setting this program to run within a command console.
' **************************
' * Declare variables here *
' **************************
DIM AdditionalPartitions
AS INTEGER ' The number of partitions that a user wants to add to a bootable thumb drive DIM AddPart
AS STRING ' Set to either "Y" or "N" depending upon whether user wishes to add additional partitions to a bootable thumb drive DIM AutoCleanup
AS INTEGER ' A flag to indicate whether an automatic cleanup of folders has been attempted. This is set to 1 if the program has already attempted automatic cleanup of folders that could not be deleted. DIM BitlockerCount
AS INTEGER ' Stores the number of partitions that need to be encrypted DIM CDROM
AS STRING ' The drive letter assigned to the mounted ISO image DIM ClosedParenPosition
AS INTEGER ' Used in the StripTimeAndDateStamp routine to hold the position within the file name where the last Closed Parenthesis is located DIM Cmd
AS STRING ' When converting expersions into strings that will be executed at a command prompt or written to a batch file to be executed, the string is stored in this variable DIM CurrentFileNum
AS INTEGER ' Keeps track of how many files we have updated in the loop to inject updates or drivers DIM DestinationFileName
AS STRING ' The file name of the ISO image to be created without a path DIM DestinationFolder
AS STRING ' The destination folder where all the folders created by the project will be located as well as the final updated ISO images DIM DestinationPath
AS STRING ' The destination path for the ISO image without a file name DIM DestinationPathAndFile
AS STRING ' The full path including the file name of the ISO image to be created DIM Detail
AS STRING ' Will be set to "Y" or "N" to indicate whether user wants to see detailed status as updates are being injected into images DIM DiskID
AS INTEGER ' Used in 2 places to ask the user for a DiskID as presented by the Microsoft DiskPart utility DIM ExportFolder
AS STRING ' Used by the routine for exporting drivers from a system. This string is the path to which drivers are exported DIM file
AS STRING ' used to hold a file name read by a line input statement from a file in the routine to strip off time and date stamp DIM Index
AS STRING ' We need the index number for the image being processed with a leading space so we convert the numeric value to a string to accomplish this and save it in this variable DIM IndexLoop
AS INTEGER ' Used as a counter to keep track of file we are asking user for an index number DIM LineCount
AS INTEGER ' General purpose counter that holds how many lines we have read from a file DIM Location
AS STRING ' Used in the StripTimeAndDateStamp routine to hold the path of files to be processed DIM MainLoopCount
AS INTEGER ' This is the counter to indicate which loop we are in. For example, if there are 5 ISO images to be updated, this counter will increment from 1 to 5 DIM MakeBootableSourceISO
AS STRING ' The full path and file name of the ISO image that the user want to make a bootable thumb drive from DIM MenuSelection
AS INTEGER ' Will hold the number of the menu option selected by the user DIM NewFile
AS STRING ' Used in the StripTimeAndDateStamp routine to hold the file name with the time and date stamp stripped off DIM NumberOfFilesToUpdate
AS INTEGER ' Holds the number of files in the folder that the user wants to update DIM NumberOfLines
AS INTEGER ' Tell the program how many lines to read in a file DIM OpenParenPosition
AS INTEGER ' Used in the StripTimeAndDateStamp routine to hold the position within the file name where the last Open Parenthesis is located DIM ProgramVersion
AS STRING ' Holds the current version number of the program DIM ProgramReleaseDate
AS STRING ' Holds the release date of the program DIM StripThisFile
AS STRING ' Holds a Yes / No response from user in the routine to strip time and date stamp from file names DIM TempPath
AS STRING ' A temporary variable used while manipulating strings DIM TimeFixed
AS STRING ' TIME$ returns the time with colons in the string. Since this cannot be used in a filename we are converting the time to replace colons with dashes DIM TotalPartitions
AS INTEGER ' The total number of partitions that need to be created on a bootable thumb drive DIM SourcePath
AS STRING ' Holds the path containing the files to be injected into an ISO image file DIM UpdateAll
AS STRING ' User is asked by routine to inject updates or drivers if all images should be updated. This string hold their response DIM Updates
AS STRING ' The path to the Windows updates that are to be injected into the ISO images DIM VHDXPath
AS STRING ' The path to where a virtual disk drive is to be created DIM VHDXFileName
AS STRING ' The file name to give a virtual disk drive that is to be created DIM VHDXSize
AS INTEGER ' The size in MB to create the virtual disk drive DIM VHDXLetter
AS STRING ' The drive letter to assign to the virtual disk drive after it is created and mounted DIM VolumeName
AS STRING ' Used by the routine to create a generic ISO image to store the volume name that the user would like to assign to the image DIM x
AS INTEGER ' Generic variable reused throughout program, mainly as a counter in FOR ... NEXT loops
' Variables dimensioned as SHARED (Globally accessible to the main program and SUB procedures)
DIM SHARED Temp
AS STRING ' Temporary string value that can be shared with subroutines and also used elsewhere as temporary storage DIM SHARED NumberOfFiles
AS INTEGER ' Used by the FileTypeSearch subroutine to keep count of the number of files found in a folder of the type specified by a user DIM SHARED CleanupSuccess
AS INTEGER ' Set to 0 if we do not successfully clean contents of folder in the cleanup routine, otherwise set to 1 if we succeed DIM SHARED YN
AS STRING ' This variable is returned by the "YesOrNo" procedure to parse user response to a yes or no prompt. See the SUB procedure for details
' Arrays
DIM SHARED TempArray
(25) AS STRING ' Used by the FileTypeSearch subroutine to keep the name of each file of the type specified by the user. We cannot DIM an array within a subroutine so we have to DIMension it outside of the SUB before we really know how many elements to DIMension it for. We are simply assuming that we will need less than 25.
' The following arrays are dimensioned dynamically within the program.
' AutoUnlock$() - Flag is set to either "Y" or "N" to indicate whether this partition should be autounlocked on current system
' BitLockerFlag$() - Flag is set to either "Y" or "N" to indicate whether this partition should be encrypted
' FileArray$() - Will hold the name of each ISO image file to be updated.
' FinalFileNameOnly$() - This is the destination file name without a path
' FinalFilePathAndName$() - This is the full path and file name of the destination file to be created
' IndexArray$() - Holds the selected the index number for the Windows edition chosen for each ISO image.
' PartitionSize$() - The size to create a partition converted to a string so that leading space is stripped out.
' SourceFileNameOnly$() - The name of the source ISO image file without a path
' ********************************
' * End of variable declarations *
' ********************************
' ***********************************************************
' ***********************************************************
' ** The following strings hold the program version number **
' ** and program release date. **
' ** Make sure to keep this updated. **
' ***********************************************************
' ***********************************************************
ProgramVersion$ = "1.5.0"
ProgramReleaseDate$ = "May 2, 2019"
' This application needs to be run with elevated permissions. Perform a test to see if user started the program with elevated permissions.
' If not running with elevated permissions, then relaunch the program with elevated permissions and close the original instance of the program.
Cmd$
= ">nul 2>&1 " + CHR$(34) + "%SYSTEMROOT%\system32\cacls.exe" + CHR$(34) + " " + CHR$(34) + "%SYSTEMROOT%\system32\config\system" + CHR$(34)
' If we reach this point then the program was run elevated.
' Display the main menu
MainMenu:
_CONSOLETITLE "WIM Version" + ProgramVersion$
+ " by Hannes Sehestedt"
ClearScreen
PRINT " Windows Image Manager (WIM) Tools" PRINT " Version "; ProgramVersion$
PRINT " Released "; ProgramReleaseDate$
PRINT " 1) Inject Windows updates into one or more Windows ISO images" PRINT " 2) Inject drivers into one or more Windows images" PRINT " 3) Make a bootable thumb drive from a Windows ISO image" PRINT " 4) Create a bootable ISO image from Windows files in a folder" PRINT " 5) Export drivers from this system" PRINT " 6) Expand drivers supplied in a .CAB file" PRINT " 7) Create a Virtual Disk (VHDX)" PRINT " 8) Create a generic ISO image and inject files and folders into it" PRINT " 9) Strip the time and date stamp from filenames" PRINT " 10) Cleanup files and folders" PRINT " 11) Display help and information for this program" INPUT " Please make a selection by number: "; MenuSelection
IF MenuSelection
= 9 THEN GOTO StripTimeAndDateStamp
' We arrive here if the user makes an invalid selection from the main menu
ClearScreen
PRINT "You have made an invalid selection. You need to make a selection by entering a number from 1 to 12."
' ***************************************************************************
' * Menu item #1: Inject Windows updates into one or more Windows ISO images *
' ***************************************************************************
InjectUpdates:
' This routine will inject Windows updates into one or more Windows ISO images. The ISO images can be
' clean, plain Windows ISO images, or they can be ISO images with an answer file for unattended setup,
' or even ISO images with a sysprep installation.
' Make sure that SourceFolder$ is initially empty.
SourceFolder$ = ""
ClearScreen
PRINT "This routine will allow you to update your Windows 10 ISO images by injecting Windows updates into the images." PRINT "You can update update all images in the location that you specify, or just some of them." INPUT "Enter the path to the source files: "; SourceFolder$
CleanPath SourceFolder$
SourceFolder$ = Temp$ + "\"
' Verify that the path specified exists.
ClearScreen
PRINT "The location that you specified does not exist or is not valid."
' Perform a check to see if files with a .ISO extension exist in specified folder.
' We are going to call the FileTypeSearch subroutine for this. We will pass to it
' the path to search and the extension to search for. It will return to us the number
' of files with that extension in the variable called filecount and the name of
' each file in an array called FileArray$(x).
FileTypeSearch SourceFolder$, ".ISO"
' NumberOfFiles is only a temporary value returned by the FileTypeSearch subroutime. Save this to FileCount now so that NumberOfFiles is available to be changed by the subroutine again
FileCount = NumberOfFiles
ClearScreen
PRINT "No files with the .ISO extension were found." PRINT "Please specify another folder."
' Initialize arrays and other variables
NumberOfFilesToUpdate = 0
UpdateAll$ = ""
' Take the temporary array called TempArray$() and save the values in FileArray$()
' We also want to have the name of the files with the path stripped out. We are going to store the file
' names without a path in the array called SourceFileNameOnly$().
FileArray$(x) = TempArray$(x)
SourceFileNameOnly$
(x
) = MID$(FileArray$
(x
), (_INSTRREV(FileArray$
(x
), "\") + 1))
UpdateAll:
ClearScreen
INPUT "Do you want to update all ISO images in this folder "; UpdateAll$
UpdateAll$ = "Y"
YesOrNo UpdateAll$
UpdateAll$ = YN$
UpdateFlag$(x) = "Y"
NumberOfFilesToUpdate = FileCount
Marker1:
ClearScreen
PRINT "Do you want to add updates to the file named: ";
CHR$(34); SourceFileNameOnly$
(x
);
CHR$(34);
YesOrNo UpdateFlag$(x)
PRINT "Please provide a valid response." UpdateFlag(x) = "Y"
NumberOfFilesToUpdate = NumberOfFilesToUpdate + 1
UpdateFlag$(x) = "N"
IF NumberOfFilesToUpdate
= 0 THEN ClearScreen
PRINT "You have not selected any files to update. We will now return to the main menu."
GetDestinationPath:
' Now that we have a valid source directory and we know that there are ISO images
' located there, ask the user for the location where we should save the updated
' files with the Windows updates applied.
DestinationFolder$ = "" ' Set initial value
ClearScreen
INPUT "Enter the path to which we should save the updated files: "; DestinationFolder$
CleanPath DestinationFolder$
DestinationFolder$ = Temp$ + "\"
' Verify that the path specified exists.
' The destination path does not exist. We will now attempt to create it.
ClearScreen
PRINT "Destination folder does not exist. Attempting to create it..." Cmd$
= "md " + CHR$(34) + DestinationFolder$
+ CHR$(34)
' Checking for existance of folder again again to see if we were able to create it.
PRINT "We were not able to create the destination folder." PRINT "Please recheck the path you have specified and try again."
' If we have arrived here it means that the destination path already exists
' or we were able to create it successfully.
' Now, ask for the location of the Windows updates to inject
GetUpdatesLocation:
Updates$ = "" 'Set initial value
ClearScreen
INPUT "Enter the path to the Windows update files: "; Updates$
CleanPath Updates$
Updates$ = Temp$
' Verify that the path specified exists.
' The path does not exist. Inform user and allow them to try again.
ClearScreen
PRINT "The updates folder does not exist. Please try again."
' If we have arrived here it means that the updates path is valid.
' Now, verify that update files actually exist in this location.
FileTypeSearch Updates$ + "\", ".MSU"
NumberOfUpdates = NumberOfFiles
PRINT "No update files were found in this location." PRINT "Please specify another location."
' Go through the list of ISO images and ask what index is associated with the edition that the
' user wants to update. If user does not know the index, display the indexs available.
ClearScreen
PRINT "Each Windows ISO image can contain multiple editions of Windows. For example," PRINT "Windows Professional, Home, Education edition, etc." PRINT "Each edition has an index number associated with it. We need to know the index" PRINT "number for the edition that you want to update." PRINT "We will now ask you for the index number associated with the edition that you" PRINT "want to update. If you do not know the index number, simply press ENTER and the" PRINT "program will display a list of Windows editions and the associated index numbers."
FOR IndexLoop
= 1 TO FileCount
IF UpdateFlag$
(IndexLoop
) = "Y" THEN ClearScreen
PRINT "Enter the index number for this file:" PRINT SourceFileNameOnly$
(IndexLoop
) INPUT "Enter index number: "; IndexArray
(IndexLoop
) IF IndexArray
(IndexLoop
) = 0 THEN ClearScreen
PRINT "Preparing to display a list of Windows Editions and the associated index numbers." PRINT "This may take a little while. Please standby..."
' If we arrive here, then we have retrieved all indices.
' Ask user if they want to see detailed status while we update their Windows images.
Detail:
Detail$ = "" ' Set initial value
ClearScreen
PRINT "Do you want to display detailed status information?" PRINT "If you choose not to display detailed status, then we display a clean," INPUT "Display detailed status (YES / NO) "; Detail$
' Parse the users response for a valid yes / no response
YesOrNo Detail$
Detail$ = YN$
PRINT "Please provide a valid response."
' The user entered a valid response
DetailIsValid:
' If there is an old logs folder made before this project, delete it, then create a new, empty logs folder
' so we start clean with this project.
' The logs folder is different than the other folders we will use in the project because we want to keep
' it through each loop for each file that we process. As a result, we create it before the main loop
' through each file to process is started.
ClearScreen
PRINT "Please standby while we do a little housekeeping...." TempPath$ = DestinationFolder$ + "logs\"
Cmd$
= "rmdir " + CHR$(34) + TempPath$
+ CHR$(34) + " /s /q"
MKDIR DestinationFolder$
+ "logs"
' The first time into the update process, we want the MainLoopCount to be equal to 1. Then, with each loop through
' the process we will increment the counter. See the comments below for the purpose of the AutoCleanup variable.
' Initial variables
CurrentFileNum = 0
MainLoopCount = 1
AutoCleanup = 0
BeginUpdateProcess:
IF UpdateFlag$
(MainLoopCount
) = "Y" THEN GOTO UpdateThisImage
IF MainLoopCount
= FileCount
THEN GOTO FinishedWithUpdates
MainLoopCount = MainLoopCount + 1
UpdateThisImage:
CurrentFileNum = CurrentFileNum + 1
' Before starting the update process, verify that there are no leftover files sitting in the
' destination.
Cleanup DestinationFolder$
' Create the folders we need for the project.
MKDIR DestinationFolder$
+ "Mount" MKDIR DestinationFolder$
+ "ISO_Files" MKDIR DestinationFolder$
+ "Scratch"
' Set a couple of variables to the current image to be processed and the index number to be processed.
CurrentSourceFile$ = FileArray$(MainLoopCount)
CurrentIndex = IndexArray(MainLoopCount)
' We need the index number with the leading space stripped off in a command so we are converting it to a string and storing in Index$
Index$
= STR$(IndexArray
(MainLoopCount
))
' Time to finally start the real work
ClearScreen
PRINT " Currently working on image #"; CurrentFileNum;
"of"; NumberOfFilesToUpdate
PRINT " "; SourceFileNameOnly$
(MainLoopCount
) _CONSOLETITLE "WIM Version " + ProgramVersion$
+ " - Working on Image" + STR$(CurrentFileNum
) + " of" + STR$(NumberOfFilesToUpdate
) + " - " + CHR$(34) + SourceFileNameOnly$
(MainLoopCount
) + CHR$(34)
' Here we begin the main work to inject updates into the ISO images. When there is a fairly long command to be run, we build
' that command into a string named Cmd$. If the user does not want detailed status, then we run the command with a SHELL _HIDE
' which will hide the detailed status information.
' > Phase 1 of 8 <
PRINT "Phase 1 of 8 - Copying files from the original ISO image to a temporary working folder."
' Mount the original ISO image and copy the files to a temporary working directory.
MountISO FileArray$(MainLoopCount)
Cmd$
= "robocopy " + MountedImageDriveLetter$
+ "\ " + CHR$(34) + DestinationFolder$
+ "ISO_Files" + CHR$(34) + " /mir /zb /256 /R:5 /W:5 /njh /njs"
' The next command dismounts the ISO image since we are now done with it. The messages displayed by the process are
' not really helpful so we are going to hide those messages even if detailed status is selected by the user.
Cmd$
= "powershell.exe -command " + CHR$(34) + "Dismount-DiskImage " + CHR$(34) + "'" + CurrentSourceFile$
+ "'" + CHR$(34) + CHR$(34)
' The next command removes the Read-Only attribute from all of the files that we just copied.
' There is no need to display this so we are hiding the output.
Cmd$
= "attrib -r " + CHR$(34) + DestinationFolder$
+ "ISO_Files\*.*" + CHR$(34) + " /s /d"
' > Phase 2 of 8 <
PRINT "Phase 2 of 8 - Mounting the install.wim image in preparation for the update process." Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /mount-image /imagefile:" + CHR$(34) + DestinationFolder$
+ "ISO_Files\sources\install.wim" + CHR$(34) + " /index:" + Index$
+ " /mountdir:" + CHR$(34) + DestinationFolder$
+ "Mount" + CHR$(34)
' > Phase 3 of 8 <
PRINT "Phase 3 of 8 - Adding Windows update packages." Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /Add-Package /Image:" + CHR$(34) + DestinationFolder$
+ "Mount" + CHR$(34) + " /PackagePath=" + CHR$(34) + Updates$
+ CHR$(34) + " /LogPath=" + CHR$(34) + DestinationFolder$
+ "Logs\DISM - " + SourceFileNameOnly$
(MainLoopCount
) + ".log" + CHR$(34)
PRINT "Addition of update packages completed."
' > Phase 4 of 8 <
PRINT "Phase 4 of 8 - Locking in the updates." Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /Cleanup-Image /Image:" + CHR$(34) + DestinationFolder$
+ "Mount" + CHR$(34) + " /StartComponentCleanup /ResetBase /ScratchDir:" + CHR$(34) + DestinationFolder$
+ "Scratch" + CHR$(34)
PRINT "Locking in of the updates completed."
' > Phase 5 of 8 <
PRINT "Phase 5 of 8 - Creating log files." Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /Get-Packages /Format:Table /image:" + CHR$(34) + DestinationFolder$
+ "mount" + CHR$(34) + " > " + CHR$(34) + DestinationFolder$
+ "logs\Packages - " + SourceFileNameOnly$
(MainLoopCount
) + ".txt" + CHR$(34)PRINT "Log creation completed."
' > Phase 6 of 8 <
PRINT "Phase 6 of 8 - Unmounting the image and commiting changes." Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /Unmount-Image /MountDir:" + CHR$(34) + DestinationFolder$
+ "mount" + CHR$(34) + " /Commit"
PRINT "Unmount and commit of the image completed."
' Create the final destination file name. We have waited until this point in the process to create
' these names because we want to assign the timestamp to the file name just before writting the file.
' The TIME$ function returs the time with colons (:) in the string. These are not valid in file names
' so we need to convert those to dashes.
' Store the full name and path of the DESTINATION file in the FinalFilePathAndName$() array.
' Store the name only of the DESTINATION file in the FinalFileNameOnly() array.
' Note that we are not filling the entire array. We are only processing the current file.
' This ensures that the timestamp appended to the file name is obtained only once we
' actually start processing that file.
TimeFixed$ = ""
TimeFixed$ = TimeFixed$ + "-"
TimeFixed$
= TimeFixed$
+ MID$(Temp$
, x
, 1)
FinalFilePathAndName$
(MainLoopCount
) = DestinationFolder$
+ (LEFT$(SourceFileNameOnly$
(MainLoopCount
), (LEN(SourceFileNameOnly$
(MainLoopCount
)) - 4)) + " (UPDATED " + DATE$ + " " + TimeFixed$
) + ").ISO"
FOR x
= LEN(FinalFilePathAndName$
(MainLoopCount
)) TO 1 STEP -1 IF MID$(FinalFilePathAndName$
(MainLoopCount
), x
, 1) = "\" THEN FinalFileNameOnly$
(MainLoopCount
) = RIGHT$(FinalFilePathAndName$
(MainLoopCount
), (LEN(FinalFilePathAndName$
(MainLoopCount
)) - x
)) FinalFileNameOnly$(MainLoopCount) = FinalFilePathAndName$(MainLoopCount)
' > Phase 7 of 8 <
PRINT "Phase 7 of 8 - Creating the final ISO image" PRINT " >>> "; FinalFileNameOnly$
(MainLoopCount
);
" <<<" Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe" + CHR$(34) + " -m -o -u2 -udfver102 -bootdata:2#p0,e,b" + CHR$(34) + DestinationFolder$
+ "ISO_Files\boot\etfsboot.com" + CHR$(34) + "#pEF,e,b" + CHR$(34) + DestinationFolder$
+ "ISO_Files\efi\microsoft\boot\efisys.bin" + CHR$(34) + " " + CHR$(34) + DestinationFolder$
+ "ISO_Files" + CHR$(34) + " " + CHR$(34) + FinalFilePathAndName$
(MainLoopCount
) + CHR$(34) PRINT "----------------------------------------------------" PRINT "----------------------------------------------------" PRINT "Creation of the updated ISO image completed."
' Now we will delete the temporary files and folders that we no longer need and update the user with
' how many images we have updated so far.
' > Phase 8 of 8 <
PRINT "Phase 8 of 8 - Cleaning up temporary files and folders." Cmd$
= "rmdir " + CHR$(34) + DestinationFolder$
+ "ISO_Files" + CHR$(34) + " /s /q"Cmd$
= "rmdir " + CHR$(34) + DestinationFolder$
+ "Mount" + CHR$(34) + " /s /q"Cmd$
= "rmdir " + CHR$(34) + DestinationFolder$
+ "Scratch" + CHR$(34) + " /s /q"PRINT "Cleanup completed." PRINT "**********************************************" PRINT MainLoopCount;
"ISO image(s) out of"; FileCount;
"have been updated." PRINT "**********************************************"
IF CurrentFileNum
= NumberOfFilesToUpdate
THEN GOTO FinishedWithUpdates
IF MainLoopCount
<> FileCount
THEN MainLoopCount = MainLoopCount + 1
PRINT "The program will begin processing the next image in a few seconds."
' If we reach point, then we have finished updating all images that need to be updated.
' Inform the user that we are done, then return to the main menu.
FinishedWithUpdates:
ClearScreen
_CONSOLETITLE "WIM Version" + ProgramVersion$
+ " by Hannes Sehestedt" PRINT "All images have been processed."
' ****************************************************************
' * Menu Item #2: Inject drivers into one or more Windows images *
' ****************************************************************
InjectDrivers:
' This routine will inject drivers into one or more Windows ISO images. The ISO images can be
' clean, plain Windows ISO images, or they can be ISO images with an answer file for unattended setup,
' or even ISO images with a sysprep installation.
'
' Note that this section of code is mostly a copy / paste of the code section used to install Windows
' updates. To avoid duplicate label names I have simply renamed the labels in this section by adding
' a "2" to the end of the label name. For example, the label GetDestinationPath becomes GetDestinationPath2.
SourceFolder$ = "" ' Set initial value
ClearScreen
PRINT "This routine will inject drivers into Windows ISO images. You can update all images in" PRINT "the location you specify or just some of them." INPUT "Enter the path to the ISO images to be updated: "; SourceFolder$
CleanPath SourceFolder$
SourceFolder$ = Temp$ + "\"
' Verify that the path specified exists.
ClearScreen
PRINT "The location that you specified does not exist or is not valid."
' Perform a check to see if files with a .ISO extension exist in specified folder.
' We are going to call the FileTypeSearch subroutine for this. We will pass to it
' the path to search and the extension to search for. It will return to us the number
' of files with that extension in the variable called filecount and the name of
' each file in an array called FileArray$(x).
FileTypeSearch SourceFolder$, ".ISO"
' NumberOfFiles is only a temporary value returned by the FileTypeSearch subroutime. Save this to FileCount now so that NumberOfFiles is available to be changed by the subroutine again
FileCount = NumberOfFiles
ClearScreen
PRINT "No files with the .ISO extension were found." PRINT "Please specify another folder."
' Initialize arrays and other variables
NumberOfFilesToUpdate = 0
UpdateAll$ = ""
' Take the temporary array called TempArray$() and save the values in FileArray$()
' We also want to have the name of the files with the path stripped out. We are going to store the file
' names without a path in the array called SourceFileNameOnly$().
FileArray$(x) = TempArray$(x)
SourceFileNameOnly$
(x
) = MID$(FileArray$
(x
), (_INSTRREV(FileArray$
(x
), "\") + 1))
UpdateAll2:
ClearScreen
INPUT "Do you want to update all ISO images in this folder "; UpdateAll$
UpdateAll$ = "Y"
YesOrNo UpdateAll$
UpdateAll$ = YN$
UpdateFlag$(x) = "Y"
NumberOfFilesToUpdate = FileCount
Marker2:
ClearScreen
PRINT "Do you want to add updates to the file named: ";
CHR$(34); SourceFileNameOnly$
(x
);
CHR$(34);
YesOrNo UpdateFlag$(x)
PRINT "Please provide a valid response." UpdateFlag(x) = "Y"
NumberOfFilesToUpdate = NumberOfFilesToUpdate + 1
UpdateFlag$(x) = "N"
IF NumberOfFilesToUpdate
= 0 THEN ClearScreen
PRINT "You have not selected any files to update. We will now return to the main menu."
GetDestinationPath2:
' Now that we have a valid source directory and we know that there are ISO images
' located there, ask the user for the location where we should save the updated
' files with the added drivers applied.
DestinationFolder$ = "" ' Set initial value
ClearScreen
INPUT "Enter the destination path: "; DestinationFolder$
CleanPath DestinationFolder$
DestinationFolder$ = Temp$ + "\"
' Verify that the path specified exists.
' The destination path does not exist. We will now attempt to create it.
ClearScreen
PRINT "Destination folder does not exist. Attempting to create it..." Cmd$
= "md " + CHR$(34) + DestinationFolder$
+ CHR$(34)
' Checking for existance of folder again again to see if we were able to create it.
PRINT "We were not able to create the destination folder." PRINT "Please recheck the path you have specified and try again."
' If we have arrived here it means that the destination path already exists
' or we were able to create it successfully.
' Now, ask for the location of the Windows updates to inject
GetUpdatesLocation2:
Updates$ = "" ' Set initial value
ClearScreen
PRINT "Enter the path to the Windows drivers to inject. Note: All subfolders will be included." INPUT "Enter the path: "; Updates$
CleanPath Updates$
Updates$ = Temp$
' Verify that the path specified exists.
' The path does not exist. Inform user and allow them to try again.
ClearScreen
PRINT "The updates folder does not exist. Please try again."
' If we have arrived here it means that the drivers path is valid.
' Go through the list of ISO images and ask what index is associated with the edition that the
' user wants to update. If user does not know the index, display the indexs available.
ClearScreen
PRINT "Each Windows ISO image can contain multiple editions of Windows. For example," PRINT "Windows Professional, Home, Education edition, etc." PRINT "Each edition has an index number associated with it. We need to know the index" PRINT "number for the edition that you want to add drivers to." PRINT "We will now ask you for the index number associated with the edition that you" PRINT "want to update. If you do not know the index number, simply press ENTER and the" PRINT "program will display a list of Windows editions and the associated index numbers."
FOR IndexLoop
= 1 TO FileCount
IF UpdateFlag$
(IndexLoop
) = "Y" THEN ClearScreen
PRINT "Enter the index number for this file:" PRINT SourceFileNameOnly$
(IndexLoop
) INPUT "Enter index number: "; IndexArray
(IndexLoop
) IF IndexArray
(IndexLoop
) = 0 THEN ClearScreen
PRINT "Preparing to display a list of Windows Editions and the associated index numbers." PRINT "This may take a little while. Please standby..."
' If we arrive here, then we have retrieved all indices.
Detail2:
' Ask user if they want to see detailed status while we update their Windows images.
Detail$ = "" ' Set initial value
ClearScreen
PRINT "Do you want to display a detailed status as the update process progresses?" PRINT "If you choose not to display detailed status, then we display a clean," PRINT "streamlined status but without details for each phase." INPUT "Display detailed status (YES / NO) "; Detail$
' Parse the users response to see determine if it is a valid yes / no response.
YesOrNo Detail$
Detail$ = YN$
PRINT "Please provide a valid response."
' The user entered a valid response
DetailIsValid2:
' If there is an old logs folder made before this project, delete it, then create a new, empty logs folder
' so we start clean with this project.
' The logs folder is different than the other folders we will use in the project because we want to keep
' it through each loop for each file that we process. As a result, we create it before the main loop
' through each file to process is started.
ClearScreen
PRINT "Please standby while we do a little housekeeping...." TempPath$ = DestinationFolder$ + "logs\"
Cmd$
= "rmdir " + CHR$(34) + TempPath$
+ CHR$(34) + " /s /q"
MKDIR DestinationFolder$
+ "logs"
' The first time into the update process, we want the MainLoopCouter to be equal to 1. Then, with each loop through
' the process we will increment the counter. See the comments below for the purpose of the AutoCleanup variable.
MainLoopCount = 1 ' Set initial value
AutoCleanup = 0 ' Set initial value
BeginUpdateProcess2:
IF UpdateFlag$
(MainLoopCount
) = "Y" THEN GOTO UpdateThisImage2
IF MainLoopCount
= FileCount
THEN GOTO FinishedWithUpdates2
MainLoopCount = MainLoopCount + 1
UpdateThisImage2:
CurrentFileNum = CurrentFileNum + 1
' Before starting the update process, verify that there are no leftover files sitting in the
' destination.
Cleanup DestinationFolder$
' Create the folders we need for the project.
MKDIR DestinationFolder$
+ "Mount" MKDIR DestinationFolder$
+ "ISO_Files"
' Set a couple of variables to the current image to be processed and the index number to be processed.
CurrentSourceFile$ = FileArray$(MainLoopCount)
CurrentIndex = IndexArray(MainLoopCount)
' We need the index number with the leading space stripped off in a command so we are converting it to a string and storing in Index$
Index$
= STR$(IndexArray
(MainLoopCount
))
' Time to finally start the real work
ClearScreen
PRINT " Currently working on image #"; CurrentFileNum;
"of"; NumberOfFilesToUpdate
PRINT " "; SourceFileNameOnly$
(MainLoopCount
) _CONSOLETITLE "WIM Version " + ProgramVersion$
+ " - Working on Image" + STR$(CurrentFileNum
) + " of" + STR$(NumberOfFilesToUpdate
) + " - " + CHR$(34) + SourceFileNameOnly$
(MainLoopCount
) + CHR$(34)
' Here we begin the main work to inject drivers into the ISO images. When there is a fairly long command to be run, we build
' that command into a string named Cmd$. If the user does not want detailed status, then we run the command with a SHELL _HIDE
' which will hide the detailed status information.
' Mount the original ISO image and copy the files to a temporary working directory.
' > Phase 1 of 6 <
PRINT "Phase 1 of 6 - Copying files from the original ISO image to a temporary working folder." MountISO FileArray$(MainLoopCount)
Cmd$
= "robocopy " + MountedImageDriveLetter$
+ "\ " + CHR$(34) + DestinationFolder$
+ "ISO_Files" + CHR$(34) + " /mir /zb /256 /R:5 /W:5 /njh /njs"
' The next command dismounts the ISO image since we are now done with it. The messages displayed by the process are
' not really helpful so we are going to hide those messages even if detailed status is selected by the user.
Cmd$
= "powershell.exe -command " + CHR$(34) + "Dismount-DiskImage " + CHR$(34) + "'" + CurrentSourceFile$
+ "'" + CHR$(34) + CHR$(34)
' The next command removes the Read-Only attribute from all of the files that we just copied.
' There is no need to display this so we are hiding the output.
Cmd$
= "attrib -r " + CHR$(34) + DestinationFolder$
+ "ISO_Files\*.*" + CHR$(34) + " /s /d"
' > Phase 2 of 6 <
PRINT "Phase 2 of 6 - Mounting the install.wim image in preparation for the update process." Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /mount-image /imagefile:" + CHR$(34) + DestinationFolder$
+ "ISO_Files\sources\install.wim" + CHR$(34) + " /index:" + Index$
+ " /mountdir:" + CHR$(34) + DestinationFolder$
+ "Mount" + CHR$(34)
' > Phase 3 of 6 <
PRINT "Phase 3 of 6 - Adding Drivers." Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /Image:" + CHR$(34) + DestinationFolder$
+ "Mount" + CHR$(34) + " /Add-Driver:" + CHR$(34) + Updates$
+ CHR$(34) + " /Recurse /LogPath=" + CHR$(34) + DestinationFolder$
+ "Logs\DISM - " + SourceFileNameOnly$
(MainLoopCount
) + ".log" + CHR$(34)
PRINT "Addition of drivers completed."
' > Phase 4 of 6 <
PRINT "Phase 4 of 6 - Unmounting the image and commiting changes." Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /Unmount-Image /MountDir:" + CHR$(34) + DestinationFolder$
+ "mount" + CHR$(34) + " /Commit"
PRINT "Unmount and commit of the image completed."
' Create the final destination file name. We have waited until this point in the process to create
' these names because we want to assign the timestamp to the file name just before writting the file.
' The TIME$ function returs the time with colons (:) in the string. These are not valid in file names
' so we need to convert those to dashes.
' Store the full name and path of the DESTINATION file in the FinalFilePathAndName$() array.
' Store the name only of the DESTINATION file in the FinalFileNameOnly() array.
' Note that we are not filling the entire array. We are only processing the current file.
' This ensures that the timestamp appended to the file name is obtained only once we
' actually start processing that file.
TimeFixed$ = "" ' Set initial value
TimeFixed$ = TimeFixed$ + "-"
TimeFixed$
= TimeFixed$
+ MID$(Temp$
, x
, 1)
FinalFilePathAndName$
(MainLoopCount
) = DestinationFolder$
+ (LEFT$(SourceFileNameOnly$
(MainLoopCount
), (LEN(SourceFileNameOnly$
(MainLoopCount
)) - 4)) + " (UPDATED " + DATE$ + " " + TimeFixed$
) + ").ISO"
FOR x
= LEN(FinalFilePathAndName$
(MainLoopCount
)) TO 1 STEP -1 IF MID$(FinalFilePathAndName$
(MainLoopCount
), x
, 1) = "\" THEN FinalFileNameOnly$
(MainLoopCount
) = RIGHT$(FinalFilePathAndName$
(MainLoopCount
), (LEN(FinalFilePathAndName$
(MainLoopCount
)) - x
)) FinalFileNameOnly$(MainLoopCount) = FinalFilePathAndName$(MainLoopCount)
' > Phase 5 of 6 <
PRINT "Phase 5 of 6 - Creating the final ISO image" PRINT " >>> "; FinalFileNameOnly$
(MainLoopCount
);
" <<<" Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe" + CHR$(34) + " -m -o -u2 -udfver102 -bootdata:2#p0,e,b" + CHR$(34) + DestinationFolder$
+ "ISO_Files\boot\etfsboot.com" + CHR$(34) + "#pEF,e,b" + CHR$(34) + DestinationFolder$
+ "ISO_Files\efi\microsoft\boot\efisys.bin" + CHR$(34) + " " + CHR$(34) + DestinationFolder$
+ "ISO_Files" + CHR$(34) + " " + CHR$(34) + FinalFilePathAndName$
(MainLoopCount
) + CHR$(34) PRINT "----------------------------------------------------" PRINT "----------------------------------------------------" PRINT "Creation of the updated ISO image completed."
' Now we will delete the temporary files and folders that we no longer need and update the user with
' how many images we have updated so far. Note that we do not use a "Scratch" folder in the process
' of injecting drivers as we do for the process of injecting Windows updates, but just in case there
' is a left over scratch forlder from a previous Windows update injection process we attempt to delete
' it here to keep things tidy.
' > Phase 6 of 6 <
PRINT "Phase 6 of 6 - Cleaning up temporary files and folders." Cmd$
= "rmdir " + CHR$(34) + DestinationFolder$
+ "ISO_Files" + CHR$(34) + " /s /q"Cmd$
= "rmdir " + CHR$(34) + DestinationFolder$
+ "Mount" + CHR$(34) + " /s /q"Cmd$
= "rmdir " + CHR$(34) + DestinationFolder$
+ "Scratch" + CHR$(34) + " /s /q"PRINT "Cleanup completed." PRINT "**********************************************" PRINT MainLoopCount;
"ISO image(s) out of"; FileCount;
"have been updated." PRINT "**********************************************"
IF CurrentFileNum
= NumberOfFilesToUpdate
THEN GOTO FinishedWithUpdates2
IF MainLoopCount
<> FileCount
THEN MainLoopCount = MainLoopCount + 1
PRINT "The program will begin processing the next image in a few seconds."
' If we reach point, then we have finished updating all images that need to be updated.
' Inform the user that we are done, then return to the main menu.
FinishedWithUpdates2:
ClearScreen
_CONSOLETITLE "WIM Version" + ProgramVersion$
+ " by Hannes Sehestedt" PRINT "All images have been processed."
' **********************************************************************
' * Menu Item #3: Make a bootable thumb drive from a Windows ISO image *
' **********************************************************************
MakeBootDisk:
' This routine will allow you to create a bootable thumb drive for installing Windows or
' to be used as an emergency boot disk. The option to create one or more additional
' partitions will be given. For each of those additional partitions, you will also have
' the option to bitlocker encrypt those partititions.
' Initialize arrays
' Get Windows ISO path to copy to the thumb drive
GetSourceISOForMakeBoot:
MakeBootableSourceISO$ = "" ' Set initial value
ClearScreen
PRINT "This routine will create a bootable thumb drive from a Windows ISO image. It can optionally create additional partitions" PRINT "and even bitlocker encrypt them if desired." INPUT "Enter the full path and file name for the Windows ISO image you want to copy to the thumb drive: "; MakeBootableSourceISO$
PRINT "That path is not valid. Please try again." GOTO GetSourceISOForMakeBoot
' If we reach this point, then the path provided is valid.
AskForPartitions:
AddPart$ = "" ' Set initial value
ClearScreen
PRINT "We will create two partitions to facilitate making a boot disk that can be booted on" PRINT "both BIOS and UEFI based systems. If you want, additional partitions can be created to" PRINT "store other data. We can even bitlocker encrypt any of these additional partitions if" PRINT "you wish to do so. Please note that you can add a maximum of 2 additional partitions," PRINT "for a total of 4 partitions." INPUT "Do you want to create additional partitions "; AddPart$
' Parse the users response to determine if it is a valid yes / no response.
YesOrNo AddPart$
AddPart$ = YN$
PRINT "Please provide a valid response."
' The user eneterd a valid response
' User does not want to create additional partitions so move on to asking what disk thay want to use.
' Also, we know that the total number of partitions will be 2.
TotalPartitions = 2
' The user wants to add partitions. Ask how many additional partitions.
HowManyPartitions:
AdditionalPartitions = 0 ' Set initial value
ClearScreen
INPUT "How many additional partitions do you want to create "; AdditionalPartitions
IF (AdditionalPartitions
< 1) OR (AdditionalPartitions
> 2) THEN PRINT "The number of additional partitions must be 1 or 2."
' For each additional partition, except the first and last partition, ask for the size.
' The first partition will 500 MB and the last partition will be assigned all remaining space.
PartitionSizes:
TotalPartitions = AdditionalPartitions + 2
' Get partion sizes. We don't have to ask about the first partition. It will always be 500 MB.
' We need to remove the leading space for the partition size so we are going to convert it
' to a string. If there are only 2 partitions then we do not need to ask for partition sizes
' since the last partition will be set to occupy all remaining space on the drive. In addition,
' we don't need to ask about encryption.
PartitionSize$(1) = "500"
FOR x
= 2 TO (TotalPartitions
- 1)
RedoPartitionSize:
PRINT "Enter the size of partition number"; x;
"in MB";:
INPUT PartitionSize$
(x
) PRINT "You must enter a size larger than 0."
' For each added partition, ask if it should be bitlocker encrypted.
AskAboutEncryption:
FOR x
= 1 TO AdditionalPartitions
RedoAskAboutEncryption:
PRINT "You have specified that"; AdditionalPartitions;
"additional partitions should be added." PRINT "Do you want to Bitlocker encrypt partition #"; x
+ 2;:
INPUT BitLockerFlag$
(x
+ 2) IF BitLockerFlag$
(x
+ 2) = "" THEN GOTO RedoAskAboutEncryption
BitLockerFlag$
(x
+ 2) = UCASE$(BitLockerFlag$
(x
+ 2)) BitLockerFlag$
(x
+ 2) = LEFT$(BitLockerFlag$
(x
+ 2), 1) ' User does not want to Bitlocker encrypt this partitition.
PRINT "Please provide a valid response." ClearScreen
GOTO RedoAskAboutEncryption
ResponseIsValid2:
ClearScreen
IF BitLockerFlag$
(x
+ 2) = "Y" THEN AskAboutAutoUnlock:
INPUT "Do you also want to autounlock this drive on this system "; AutoUnlock$
(x
+ 2) IF AutoUnlock$
(x
+ 2) = "" THEN GOTO ResponseIsValid2
AutoUnlock$
(x
+ 2) = UCASE$(AutoUnlock$
(x
+ 2)) AutoUnlock$
(x
+ 2) = LEFT$(AutoUnlock$
(x
+ 2), 1) ' User does not want to autounlock the partition.
PRINT "Please provide a valid response." ClearScreen
ResponseIsValid3:
' Display a list of disks and ask which one to use. If user needs more detail on a disk, display that detail.
SelectDisk:
ClearScreen
PRINT "Below is a list of disks seen by your system. Note the ID for the disk that you want to make bootable." PRINT "We will also offer to show more details for any disk if you wish. Once you have no more disks for which" PRINT "you need to see additional detail, you can then select a disk." PRINT #1, "(echo list disk" DiskID = 0 ' Set initial value
PRINT "If you want to see more detail for a disk, enter the number here. If not, just hit Enter." INPUT "Enter the Disk ID for which you want additional information or hit Enter: "; DiskID
PRINT #1, "(echo select disk "; DiskID
PRINT #1, "echo detail disk"
PickDisk:
DiskID = 0 ' Set initial value
ClearScreen
INPUT "Enter the Disk ID of the disk you want to make bootable"; DiskID
' Verify that the selected disk is removable
ClearScreen
PRINT #1, "(echo select disk"; DiskID
PRINT #1, "echo detail disk" PRINT #1, ") | diskpart > DiskpartOut.txt"
' Parse the DiskpartOut.txt file
LineCount = 0 ' Set initial value
LineCount = LineCount + 1
' We are interested in the 4th line from the end so are setting a variable called
' NumberOfLine to be LineCount - 3 and we will then analyze that line.
NumberOfLines = LineCount - 3
FOR x
= 1 TO NumberOfLines
' If the disk is removable, then the 9 characters starting with the 39th character of Temp$ will equal "Removable".
NotRemovable:
ClearScreen
PRINT "The drive that you have chosen is not a removable drive. Please select another drive."
Removable:
ClearScreen
PRINT "Initializing disk..." PRINT #1, "(echo select disk"; DiskID
PRINT #1, "echo convert mbr" PRINT #1, "echo ) | diskpart > NUL"
' Get drive letter to assign to each partition
FOR x
= 1 TO TotalPartitions
RepeatLetter:
ClearScreen
PRINT "For all"; TotalPartitions;
" partitions, enter the drive letter to assign. Enter only the letter" PRINT "without a colon (:)." Letter$(x) = "" ' Set initial value
PRINT "Enter the drive letter for partition #"; x;:
INPUT Letter$
(x
)
Letter$
(x
) = UCASE$(Letter$
(x
)) IF (LEN(Letter$
(x
)) > 1) OR (Letter$
(x
)) = "" OR ((ASC(Letter$
(x
))) < 65) OR ((ASC(Letter$
(x
))) > 90) THEN PRINT "That was not a valid entry. Please try again." PRINT "That drive letter is already in use."
ClearScreen
PRINT "Preparing disk. Note that this can take a long time, especially if your disk is slow." PRINT "Please standby..." PRINT #1, "(echo select disk"; DiskID
FOR x
= 1 TO TotalPartitions
PRINT #1, "echo create partition primary size="; PartitionSize$
(x
) PRINT #1, "echo format fs=fat32 quick" PRINT #1, "echo assign letter="; Letter$
(x
) PRINT #1, "echo format FS=NTFS quick" PRINT #1, "echo assign letter="; Letter$
(x
) PRINT #1, "echo create partition primary" PRINT #1, "echo format FS=NTFS quick" PRINT #1, "echo assign letter="; Letter$
(x
)
PRINT #1, "echo ) | diskpart > NUL"
' Handle Bitlocker encryption
ClearScreen
IF TotalPartitions
= 2 THEN GOTO DoneWithBitlocker
' We know that there are additional partitions. Determine how many are to be encrypted.
BitlockerCount = 0 ' Set initial value
FOR x
= 3 TO TotalPartitions
IF BitLockerFlag$
(x
) = "Y" THEN BitlockerCount = BitlockerCount + 1
FOR x
= 3 TO TotalPartitions
IF BitLockerFlag$
(x
) = "Y" THEN Cmd$ = "%windir%\system32\manage-bde.exe -on " + Letter$(x) + ":" + " -pw -used -em xts_aes128"
Cmd$ = "%windir%\system32\manage-bde.exe -autounlock -enable " + Letter$(x) + ":"
DoneWithBitlocker:
MountISO MakeBootableSourceISO$
CDROM$ = MountedImageDriveLetter$
' There will only be a file called autounattend.xml if the user has an image configured with
' an unattended answer file. If this file does not exist an error will be displayed. So as not
' to alarm the user, we will hide the output from that command by redirecting to NUL.
PRINT #1, "robocopy "; CDROM$;
"\ "; Letter$
(1);
":\ /mir /xd sources /njs /256" PRINT #1, "robocopy "; CDROM$;
"\sources "; Letter$
(1);
":\sources boot.wim /njh /njs /256" PRINT #1, "robocopy "; CDROM$;
"\sources "; Letter$
(2);
":\sources /mir /njh /njs /xf boot.wim /256" PRINT #1, "robocopy "; Letter$
(1);
"\ "; Letter$
(2);
"\ /mov autounattend.xml /njh /njs > NUL" PRINT #1, "powershell.exe -command ";
CHR$(34);
"Dismount-DiskImage ";
CHR$(34);
"'"; MakeBootableSourceISO$;
"'";
CHR$(34);
CHR$(34) PRINT "Ready to copy files to thumb drive. Review the TEMP.BAT to make sure it is okay."
' Making the file ei.cfg on the partition 2, in the sources folder.
Temp$ = Letter$(2) + ":\sources\ei.cfg"
' All operations are complete.
ClearScreen
' Subroutine - Shows patition information.
ShowPartitionSizes:
ClearScreen
PRINT "*******************" PRINT "* PARTITION SIZES *" PRINT "*******************" PRINT "Partition #1: 500 MB (Holds boot files)" IF PartitionSize$
(2) = "" THEN PRINT " NOT YET DEFINED ";
PRINT " "; PartitionSize$
(2);
" MB ";
PRINT "(Must be large enough to hold contents of Windows image)"
IF PartitionSize$
(3) = "" THEN PRINT PartitionSize$
(3);
" MB" PRINT "Partition #4: (All remaining space not assigned to the first three partitions)" PRINT "Partition #3: (All remaining space not assigned to the first three partitions)"
' ****************************************************************************
' * Menu Item #4: Create a bootable ISO image from Windows files in a folder *
' ****************************************************************************
MakeBootDisk2:
' This routine will take a folder containing Windows files and create a bootable ISO image from those files.
' All files and folders must be present.
' Clear any pre-existing values for paths and filenames
MakeBootablePath$ = ""
DestinationFolder$ = ""
DestinationFileName$ = ""
VolumeName$ = ""
ClearScreen
PRINT "This routine will take a folder containing Windows files and create a bootable ISO image from those files." PRINT "All Windows files and folders must be present. You can include an autounattend.xml file and/or modified WIM files." INPUT "Enter the path to the folder holding the Windows files to be made into a bootable ISO image: "; MakeBootablePath$
CleanPath MakeBootablePath$
MakeBootablePath$ = Temp$
TempPath$ = MakeBootablePath$ + "\sources\install.wim"
' We cannot check for all files, but we are at least checking to see if an "INSTALL.WIM" is present at the specified location as a simple sanity check that the folder specified is likely valid.
PRINT "That path is not valid. No INSTALL.WIM file found at that location. Please try again."
' If we reach this point, then the path provided exists and it contains an INSTALL.WIM file in the SOURCES folder.
ISODestinationPath:
DestinationFolder$ = "" ' Set initial value
ClearScreen
INPUT "Enter the destination path. This is the path only without a file name: "; DestinationFolder$
CleanPath DestinationFolder$
DestinationFolder$ = Temp$
' Verify that the path specified exists.
' The destination path does not exist. We will now attempt to create it.
ClearScreen
PRINT "Destination folder does not exist. Attempting to create it..." Cmd$
= "md " + CHR$(34) + DestinationFolder$
+ CHR$(34)
' Checking for existance of folder again again to see if we were able to create it.
PRINT "We were not able to create the destination folder." PRINT "Please recheck the path you have specified and try again."
' If we have arrived here it means that the destination path already exists
' or we were able to create it successfully.
DestinationFileName$ = "" ' Set initial value
ClearScreen
INPUT "Enter the name of the file to create, without an extension: "; DestinationFileName$
GetVolumeName1:
' Get the volume name for the ISO image
ClearScreen
INPUT "Enter the volume name to give the ISO image or press Enter for none: "; VolumeName$
PRINT "That volume name is invalid! The volume name is limited to 32 characters."
' Create the ISO image
ClearScreen
Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe" + CHR$(34) + " -m -o -u2 -udfver102 -l" + CHR$(34) + VolumeName$
+ CHR$(34) + " -bootdata:2#p0,e,b" + CHR$(34) + MakeBootablePath$
+ "\boot\etfsboot.com" + CHR$(34) + "#pEF,e,b" + CHR$(34) + MakeBootablePath$
+ "\efi\microsoft\boot\efisys.bin" + CHR$(34) + " " + CHR$(34) + MakeBootablePath$
+ CHR$(34) + " " + CHR$(34) + DestinationFolder$
+ "\" + DestinationFileName$
+ ".iso" + CHR$(34)PRINT "Creating the ISO image. Please standby..."
' *************************************************
' * Menu Item #5: Export drivers from this system *
' *************************************************
ExportDrivers:
ExportFolder$ = "" ' Set initial value
ClearScreen
PRINT "This routine will export all the drivers in use by this system to a location of your choice." INPUT "Enter the full path to the location where you want the drivers to be exported: "; ExportFolder$
CleanPath ExportFolder$
ExportFolder$ = Temp$
' Verify that the path specified exists.
' The destination path does not exist. We will now attempt to create it.
ClearScreen
PRINT "Destination folder does not exist. Attempting to create it..." Cmd$
= "md " + CHR$(34) + ExportFolder$
+ CHR$(34)
' Checking for existance of folder again again to see if we were able to create it.
PRINT "We were not able to create the destination folder." PRINT "Please recheck the path you have specified and try again."
' If we have arrived here it means that the path specified already exists
' or we were able to create it successfully.
' NOTE: If compiled as a 32-Bit program you will need to replace the "system32" in the paths below with "sysnative".
' PNPUTIL is only available as a 64-Bit app on 64-bit version of Windows.
' As a result, when you try to call it, you have to specify %windir%\sysnative\pnputil.exe rather than %windir%\system32\pnputil.exe
' because otherwise Windows will try to redirect the call to %windir%\SysWOW64 to run 32-bit commands.
Cmd$
= CHR$(34) + "%windir%\system32\pnputil.exe" + CHR$(34) + " /export-driver * " + CHR$(34) + ExportFolder$
+ CHR$(34)ClearScreen
PRINT "Drivers have been exported to the following location: "; ExportFolder$
' ********************************************************
' * Menu Item #6: Expand drivers supplied in a .CAB file *
' ********************************************************
ExpandDrivers:
' This routine will expand drivers that are distributed in .CAB files. Once expanded, Menu Item #5 can
' then be used to inject these drivers into Windows ISO images. Note that not all .CAB files can be opened by the
' Windows EXPAND utility. Files that cannot be opened will simply be copied to the destination.
SourceFolder$ = "" ' Set initial value
ClearScreen
PRINT "This routine will expand drivers that are distributed in .CAB files. Once expanded, Menu Item #5 can" PRINT "then be used to inject these drivers into Windows ISO images. Note that not all .CAB files can be" PRINT "opened by the Windows EXPAND utility. Files that cannot be opened will simply be copied to the destination." PRINT "Drivers that you download from the Microsoft Update Catalog should work with this routine." INPUT "Enter the path to the drivers that are in .CAB files: "; SourceFolder$
CleanPath SourceFolder$
SourceFolder$ = Temp$ + "\"
' Verify that the path specified exists.
ClearScreen
PRINT "The location that you specified does not exist or is not valid."
' Perform a check to see if files with a .CAB extension exist in specified folder.
' We are going to call the FileTypeSearch subroutine for this. We will pass to it
' the path to search and the extension to search for. It will return to us the number
' of files with that extension in the variable called filecount and the name of
' each file in an array called FileArray$(x).
FileTypeSearch SourceFolder$, ".CAB"
' NumberOfFiles is only a temporary value returned by the FileTypeSearch subroutime. Save this to FileCount now so that NumberOfFiles is available to be changed by the subroutine again
FileCount = NumberOfFiles
ClearScreen
PRINT "No files with the .CAB extension were found." PRINT "Please specify another folder."
' Initialize arrays
' Take the temporary array called TempArray$() and save the values in FileArray$()
FileArray$(x) = TempArray$(x)
' We already have the names of all the CAB images to be update in the FileArray$() variables. However,
' we also want to have the name of the files with the path stripped out. We are going to store the file
' names without a path in the array called SourceFileNameOnly$().
FileArray$(x) = TempArray$(x)
SourceFileNameOnly$
(x
) = MID$(FileArray$
(x
), (_INSTRREV(FileArray$
(x
), "\") + 1))
GetDestinationPath3:
' Now that we have a valid source directory and we know that there are CAB files
' located there, ask the user for the location where we should save the expanded
' files.
DestinationFolder$ = "" ' Set initial value
ClearScreen
INPUT "Enter the destination path: "; DestinationFolder$
CleanPath DestinationFolder$
DestinationFolder$ = Temp$ + "\"
' Verify that the path specified exists.
' The destination path does not exist. We will now attempt to create it.
ClearScreen
PRINT "Destination folder does not exist. Attempting to create it..." Cmd$
= "md " + CHR$(34) + DestinationFolder$
+ CHR$(34)
' Checking for existance of folder again again to see if we were able to create it.
PRINT "We were not able to create the destination folder." PRINT "Please recheck the path you have specified and try again."
' If we have arrived here it means that the destination path already exists
' or we were able to create it successfully.
' The first time into the update process, we want the MainLoopCouter to be equal to 1. Then, with each loop through
' the process we will increment the counter. See the comments below for the purpose of the AutoCleanup variable.
MainLoopCount = 1 ' Set initial value
' Create the folders we need for the project.
ClearScreen
PRINT "Processing .CAB file"; x;
" of"; FileCount
Cmd$
= "md " + CHR$(34) + DestinationFolder$
+ "\" + SourceFileNameOnly$
(x
) + CHR$(34) + " > NUL" Cmd$
= "expand " + CHR$(34) + FileArray$
(x
) + CHR$(34) + " -f:*.* " + CHR$(34) + DestinationFolder$
+ SourceFileNameOnly$
(x
) + CHR$(34) + " > NUL"
' If we reach point, then we have finished updating all images that need to be updated.
' Inform the user that we are done, then return to the main menu.
ClearScreen
PRINT "All .CAB files have been processed."
' **********************************************
' * Menu Item #7: Create a Virtual Disk (VHDX) *
' **********************************************
CreateVHDX:
VHDXPath$ = "" ' Set initial value
ClearScreen
PRINT "This routine will allow you to create a Virtual Disk (VHDX)." PRINT "Please specify the location where you would like to create the Virtual Hard Disk." PRINT "If the path does not exist, we will try to create it. Do not include a file name." INPUT "Please enter path: "; VHDXPath$
' Remove quotes and trailing backslash.
CleanPath VHDXPath$
VHDXPath$ = Temp$
' Verify that the path specified exists.
' The destination path does not exist. We will now attempt to create it.
ClearScreen
PRINT "Destination folder does not exist. Attempting to create it..." Cmd$
= "md " + CHR$(34) + VHDXPath$
+ CHR$(34)
' Checking for existance of folder again again to see if we were able to create it.
PRINT "We were not able to create the destination folder." PRINT "Please recheck the path you have specified and try again."
' If we have arrived here it means that the destination path already exists
' or we were able to create it successfully.
' Get a name for the Virtual Hard Disk file.
VHDXFileName$ = "" ' Set initial value
VHDXSize = 0 ' Set initial value
ClearScreen
PRINT "Please provide a name for the Virtual Hard disk file. Do not include a file extension." INPUT "Enter file name: "; VHDXFileName$
ClearScreen
INPUT "Enter size of Virtual Disk in MB (NOT in GB!): "; VHDXSize
' We need to strip the leading space from the size, so we are going to convert the size to a string.
VHDXSizeString$
= STR$(VHDXSize
)VHDXSizeString$
= RIGHT$(VHDXSizeString$
, (LEN(VHDXSizeString$
) - 1))
GetVHDXLetter:
ClearScreen
INPUT "What drive letter do you want to assign to the Virtual Disk. Enter only the letter, no colon (:): "; VHDXLetter$
VHDXLetter$
= UCASE$(VHDXLetter$
)
IF (LEN(VHDXLetter$
) > 1) OR (VHDXLetter$
) = "" OR ((ASC(VHDXLetter$
)) < 65) OR ((ASC(VHDXLetter$
)) > 90) THEN PRINT "That was not a valid entry. Please try again."
PRINT "That drive letter is already in use."
' Create the Virtual Disk
ClearScreen
PRINT "We are now creating the disk. This could possibly take a little while." PRINT #1, "(echo create vdisk file=";
CHR$(34); VHDXPath$;
"\"; VHDXFileName$;
".VHDX";
CHR$(34);
" type=expandable maximum="; VHDXSizeString$
PRINT #1, "echo select vdisk file=";
CHR$(34); VHDXPath$;
"\"; VHDXFileName$;
".VHDX";
CHR$(34) PRINT #1, "echo attach vdisk" PRINT #1, "echo create partition primary" PRINT #1, "echo format fs=ntfs quick" PRINT #1, "echo assign letter="; VHDXLetter$
PRINT #1, ") | diskpart > NUL" ClearScreen
PRINT "Virtual Hard Disk has been created." PRINT "Please note that this disk was created as an expandable disk so the initial size of the file may" PRINT "appear much smaller than the size that you specified." PRINT "If you created this disk on removable media, be sure to eject the Virtual Disk before you eject the"
' *********************************************************************************
' * Menu Item #8: Create a generic ISO image and inject files and folders into it *
' *********************************************************************************
CreateISOImage:
' Initialize variables
SourcePath$ = ""
VolumeName$ = ""
ClearScreen
PRINT "This will create an ISO image from all files and subfolders within the folder that you specify."
' Get the location to the files / folders that should be injected into an ISO file.
INPUT "Enter the path containing the data to place into an ISO image: "; SourcePath$
' Verify that the path specified exists.
ClearScreen
PRINT "The location that you specified does not exist or is not valid."
GetDestination:
DestinationPath$ = "" ' Set initial value
ClearScreen
INPUT "Enter the destination path. This is the path only without a file name: "; DestinationPath$
CleanPath DestinationPath$
DestinationPath$ = Temp$ + "\"
' Verify that the path specified exists.
' The destination path does not exist. We will now attempt to create it.
ClearScreen
PRINT "Destination path does not exist. Attempting to create it..." Cmd$
= "md " + CHR$(34) + DestinationPath$
+ CHR$(34)
' Checking for existance of folder again again to see if we were able to create it.
PRINT "We were not able to create the destination folder." PRINT "Please recheck the path you have specified and try again."
' If we have arrived here it means that the destination path already exists
' or we were able to create it successfully.
DestinationFileName$ = "" ' Set initial value
' Get the name of the ISO image that we are creating.
ClearScreen
INPUT "Enter the name of the file to create, without an extension: "; DestinationFileName$
DestinationPathAndFile$ = DestinationPath$ + DestinationFileName$ + ".iso"
' Get the volume name for the ISO image
GetVolumeName2:
ClearScreen
INPUT "Enter the volume name to give the ISO image or press Enter for none: "; VolumeName$
PRINT "That volume name is invalid! The volume name is limited to 32 characters."
' Build the command that needs to be run to create the ISO image.
Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe" + CHR$(34) + " " + "-o -m -h -k -u2 -udfver102 -l" + CHR$(34) + VolumeName$
+ CHR$(34) + " " + CHR$(34) + SourcePath$
+ CHR$(34) + " " + CHR$(34) + DestinationPathAndFile$
+ CHR$(34)
' Create the ISO image
ClearScreen
PRINT "Creating the image. Please standby..." PRINT "ISO Image created."
' **************************************************************
' * Menu Item #9: Strip the time and date stamp from filenames *
' **************************************************************
StripTimeAndDateStamp:
' Initialize variables
Location$ = ""
UpdateAll$ = ""
ClearScreen
PRINT "When you use either the routine to inject Windows updates or drivers into Windows ISO images," PRINT "a time and date stamp is added to the file name. If you want to strip off this time and date" PRINT "stamp, this routine will do that for you." INPUT "Enter the full path the files from which to remove the time and date stamp: "; Location$
' Clean up the path to strip off any quoite marks or trailing backslash from the path.
CleanPath Location$
Location$ = Temp$
' The path does not exist. Inform user and allow them to try again.
ClearScreen
PRINT "That folder does not exist. Please try again." GOTO StripTimeAndDateStamp
' If we have arrived here it means that the updates path is valid.
' Now, verify that files actually exist in this location.
StripAllFiles:
ClearScreen
INPUT "Do you want to remove the time and date stamp from all files in this location"; UpdateAll$
YesOrNo UpdateAll$
UpdateAll$ = YN$
PRINT "Please enter a valid response."
Cmd$
= "DIR /B /A:-D " + CHR$(34) + Location$
+ "\*.*" + CHR$(34) + " > TEMP.TXT"
OpenParenPosition = 0 ' Set initial value
ClosedParenPosition = 0 ' Set initial value
IF ((OpenParenPosition
> 1) AND (ClosedParenPosition
> OpenParenPosition
)) THEN NewFile$
= RTRIM$(LEFT$(file$
, (OpenParenPosition
- 1))) NewFile$
= NewFile$
+ RIGHT$(file$
, (LEN(file$
) - ClosedParenPosition
)) Cmd$
= "rename " + CHR$(34) + Location$
+ "\" + file$
+ CHR$(34) + " " + CHR$(34) + NewFile$
+ CHR$(34)
AskAboutTSRemoval:
ClearScreen
INPUT "Do you want to remove the time and date stamp from the above file"; StripThisFile$
YesOrNo StripThisFile$
StripThisFile$ = YN$
IF StripThisFile$
= "X" THEN GOTO AskAboutTSRemoval
Cmd$
= "rename " + CHR$(34) + Location$
+ "\" + file$
+ CHR$(34) + " " + CHR$(34) + NewFile$
+ CHR$(34)
ClearScreen
' ********************************************
' * Menu Item #10: Cleanup files and folders *
' ********************************************
GetFolderToClean:
DestinationPath$ = "" ' Set initial value
ClearScreen
PRINT "If the routine to inject updates or drivers into an ISO image is aborted prematurely," PRINT "Then it is possible that you may not be able to delete files or folders in the" PRINT "destination. Use this routine to try fixing that issue." INPUT "Please enter the full path to the folder to be cleaned: "; DestinationPath$
CleanPath DestinationPath$
DestinationPath$ = Temp$ + "\"
Cleanup DestinationPath$
ClearScreen
PRINT "The contents of the folder ";
CHR$(34); DestinationPath$;
CHR$(34);
" were successfully cleaned."
' ***************************************************************
' * Menu Item #11: Display help and information for this program *
' ***************************************************************
ShowHelp:
MenuSelection = 0 ' Set initial value
ClearScreen
PRINT "Please choose the topic that you would like help with or press Enter to return to the main menu:" PRINT " 1) General Information" PRINT " 2) System Requirements" PRINT " 3) Inject Windows updates into one or more Windows ISO images" PRINT " 4) Inject drivers into one or more Windows images" PRINT " 5) Make a bootable thumb drive from a Windows ISO image" PRINT " 6) Create a bootable ISO image from Windows files in a folder" PRINT " 7) Export drivers from this system" PRINT " 8) Expand drivers supplied in a .CAB file" PRINT " 9) Create a Virtual Disk (VHDX)" PRINT " 10) Create a generic ISO image and inject files and folders into it" PRINT " 11) Strip the time and date stamp from filenames" PRINT " 12) Cleanup files and folders" PRINT " 13) Display help and information for this program" INPUT " Please make a selection by number or press Enter alone to exit from help: "; MenuSelection
LOOP WHILE ((MenuSelection
< 0) OR (MenuSelection
> 14))
Help1: ' General Information
ClearScreen
PRINT "The main purpose of this program is to aid in injecting Windows updates and / or drivers into your Windows" PRINT "The process of injecting updates or drivers is long and tedious, involves a lot of long commands, and" PRINT "is very error prone. This program will automate all those tasks for you. In addition, if you want to" PRINT "inject updates into multiple ISO images, this program will do that for you. Simply supply a few pieces" PRINT "of information and the program will do the rest." PRINT "There also several helpful utilities available. Please see the help topics for each individual option for" PRINT "When you are asked for a path, it's okay to enter the path either with or without quotes even when there" PRINT "are spaces in the pathname of filename." PRINT "TIP: You can copy and paste long path names into the program." PRINT "Hard Disk vs. Removable Media:" PRINT "When creating a bootable thumb drive, you must use a thumb drive that reports as a removable disk. The" PRINT "program will tell you if the disk you select is not a removable disk." PRINT "For the process of injecting Windows updates, one of the Microsoft utilities used (DISM) requires the use" PRINT "of a fixed disk, not a removable disk. Make sure that you use a fixed disk to store your project. Note that" PRINT "you can work around this by creating a Virtual Hard Disk on a removable disk and then using that Virtual" PRINT "Hard Disk to store your project. There is an option on the main menu to help you create a Virtual Hard Disk."
Help2: ' System requirements
ClearScreen
PRINT "This program is a 64-bit program designed to work only on 64-bit systems, not 32-bit or ARM based systems." PRINT "It is also designed to work with Windows ISO images that contain an INSTALL.WIM file in the \sources" PRINT "folder, not an INSTALL.ESD. Finally, only 64-bit Windows ISO images should be used. If you have a need for" PRINT "version that will work with 32-bit versions of Windows, let me know and I may possibly be convinced to" PRINT "create a 32-bit version if there is enough interest." PRINT "This program requires the Windows ADK to be installed. Only the";
CHR$(34);
"Deployment Tools";
CHR$(34);
" component needs to be"
Help3: ' Inject Windows updates into one or more Windows ISO images
ClearScreen
PRINT "This routine will inject Windows updates into your ISO images." PRINT "Place all your images to be updated in one folder and the program will apply the updates to all of the" PRINT "images in that folder. Make sure that there are no other files with a .ISO extension in this folder. Any" PRINT "other files are okay since they will simply be ignored." PRINT "NOTE: The original images will remain unmodified so it is ok to save to the same folder with the source files." PRINT "IMPORTANT: You want to make sure that updates are applied in a certain order. To ensure that this happens," PRINT "put a numerical tag in front of the filename to indicate the load order. Here are some examples (the numbers" PRINT "are part of the filename):" PRINT "1 - KB000001 - Latest Servicing Stack Update.msu" PRINT "2 - KB000002 - Apr 2019 Cumulative Update.msu" PRINT "3 - Any file name you want.msu" PRINT "Updates should be applied in the following order:" PRINT "1) Servicing stack updates" PRINT "2) Windows cumulative updates (Note that .NET updates should be considered ";
CHR$(34);
"other updates";
CHR$(34);
")" PRINT "IMPORTANT: This process will NOT apply driver updates. There is a seperate option from the main menu that" PRINT "will allow you to inject drivers." ClearScreen
PRINT " *************************" PRINT " * Obtaining the Updates *" PRINT " *************************" PRINT " *****************************************" PRINT " * Latest SSUs (Servicing Stack Updates) *" PRINT " *****************************************" PRINT "Visit this web site to see a list of available SSUs and to download them:" PRINT "https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/ADV990001" PRINT " ****************************" PRINT " * Microsoft Update Catalog *" PRINT " ****************************" PRINT "Download updates for Windows here:" PRINT "http://www.catalog.update.microsoft.com/home.aspx" PRINT "On the Windows Update catalog web site I like to do a search similar to this: Windows 1809 x64 and then sort by" PRINT "release date to narrow down the search to the updates for my version of Windows." ClearScreen
PRINT " **************************" PRINT " * Windows Update History *" PRINT " **************************" PRINT "View Windows update history here:" PRINT "https://support.microsoft.com/en-us/help/4464619" PRINT "TIP: I like to maintain a copy of Windows in a VM that has no updates applied and has networking disabled." PRINT "I keep a snapshot (known as a checkpoint in Hyper-V) of this state so I can easily return to it. After Microsoft" PRINT " releases new updates I enable networking to allow the updates to be installed. Then, you can look at the update" PRINT "history to easily verify what updates get installed. I then know exactly what I need to download."
Help4: ' Inject drivers into one or more Windows images
ClearScreen
PRINT "This option will take take all the drivers located in a folder and inject them into one or more Windows ISO" PRINT "images. All subfolders of the location that you specify will be recursed for drivers." PRINT "TIPS: You can add drivers from multiple systems all at once. Simply export the drivers from multiple systems" PRINT "using menu item to export drivers from a system, then place them all under one main folder like this:" PRINT " Drivers from desktop" PRINT " Drivers from laptop" PRINT " Drivers from media system" PRINT "Next, use this menu item to inject the drivers. Simply point to the ";
CHR$(34);
"Drivers";
CHR$(34);
" folder and the program will" PRINT "recurse all subfolders for drivers and inject them into the image." PRINT "IMPORTANT: The drivers to be injected must be expanded and have their .INF files accessible. If you have drivers" PRINT "in a .CAB or .EXE file, this routine will not be able to inject those drivers."
Help5: ' Make a bootable thumb drive from a Windows ISO image
ClearScreen
PRINT "Use this option to make a bootable thumb drive from a Windows ISO image. The resulting thumb drive will be" PRINT "bootable from both BIOS and UEFI based systems." PRINT "Important: The thumb drive that you use must report itself as a removavle drive. If the drive reports itself" PRINT "as a fixed disk it will not work. The program will automatically notify you if the drive is not removable." PRINT "You will have the option to create up to 2 additional partitions on the thumb drive to hold other data. The" PRINT "program can even encrypt these partitions with Bitlocker and optionally configure it to autounlock on your system." PRINT "As a result, you can have a Windows 10 bootable thumb drive that also holds other data which can be optionally" PRINT "Bitlocker protected."
Help6: ' Create a bootable ISO image from Windows files in a folder
ClearScreen
PRINT "If you have Windows files on a DVD, hard disk, SSD, thumb drive, etc. you can create a bootable ISO image from" PRINT "those files using this option."
Help7: ' Export drivers from this system
ClearScreen
PRINT "This option takes all the active drivers from your current Windows installation and exports them to a folder." PRINT "You can then use the menu option to inject these drivers into a Windows image."
Help8: ' Expand drivers supplied in a .CAB file
ClearScreen
PRINT "The menu option to inject drivers can only inject drivers where the .INF file is accessible. If you have drivers in" PRINT "a .CAB file, you can use this routine to expand the CAB files. You can place multiple files in one folder and this" PRINT "routine will expand all of them." PRINT "Please be aware that the Windows EXPAND utility may not be able to expand all CAB files, however, any drivers" PRINT "in CAB files from the Microsoft Update Catalog should work with this utility. If a CAB file cannot be expanded," PRINT "then the CAB file will simply be copied to the destination as is."
Help9: ' Create a Virtual Disk (VHDX)
ClearScreen
PRINT "This option will create a Virtual Hard Disk and mount it to the drive letter of your choosing. This can be" PRINT "especially useful for saving updated ISO images on removable media since that process requires a fixed disk." PRINT "Placing a Virtual Hard Disk on a removable drive provides a workaround."
Help10: ' Create a generic ISO image and inject files and folders into it
ClearScreen
PRINT "This option will take all the files from a folder and place them into an ISO image. If you are working on a" PRINT "project where you have a lot of folders, files, documents, etc. that you want to keep together, this may be" PRINT "useful. Naturally, you could always create a ZIP or other archive file, but I like ISO images because I can" PRINT "simply mount them as another drive letter natively within Windows. Creation of an ISO is also quick."
Help11: ' Strip the time and date stamp from filenames
ClearScreen
PRINT "When you use the routines to inject either updates or drivers into Windows ISO images, a time and date stamp" PRINT "is added to the file names. If you want to strip off this time and date stamp from the files, run this routine" PRINT "to automate the process. This is especially handy if you have a lot of Windows images all in the same folder."
Help12: ' Cleanup files and folders
ClearScreen
PRINT "If the routine to inject updates or drivers into an ISO image is aborted prematurely, it is possible that you" PRINT "may not be able to delete files or folders in the destination. Use this routine to try fixing that issue. If" PRINT "this routine cannot resolve the issue then it will inform you that you may need to reboot and then try the" PRINT "This routine is useful because a reboot alone may not resolve the issue. It may still be necessary to unmount" PRINT "any image previously mounted by DISM and this routine will do that." PRINT "IMPORTANT: Please note that if the program is terminated while an ISO image is mounted, you will need to unmount" PRINT "it manually. You can simply right-click the drive representing the image in File Explorer and choose ";
CHR$(34);
"Eject";
CHR$(34);
"."
Help13: ' Display help and information for this program
ClearScreen
PRINT "This will display the main help menu from which you selected this option."
Help14: ' Exit
ClearScreen
PRINT "This will end the program."
' ***********************
' * Menu Item #12: Exit *
' ***********************
ProgramEnd:
' *******************************************************************************************
' * Sub PROCEDURES go here. These are subroutines that are still a part of the main program *
' * and that are not in SUB / END SUB blocks. *
' *******************************************************************************************
DisplayIndices:
MountISO FileArray$(IndexLoop)
Cmd$ = "dism /Get-WimInfo /WimFile:" + MountedImageDriveLetter$ + "\Sources\Install.wim > WimInfo.txt"
Cmd$
= "powershell.exe -command " + CHR$(34) + "DiskMount-DiskImage " + CHR$(34) + "'" + FileArray$
(IndexLoop
) + "'" + CHR$(34) + CHR$(34) + " > NUL"
' Display wiminfo.txt which lists all of the indicies now. Then, delete wiminfo.txt
ClearScreen
' PRINT "Following is a list of Windows Editions and the index number associated with each edition."
' PRINT "Note the index number for the edition that you want to update."
PRINT "We are about to display the list of Windows editions and the associated index numbers for this file:" PRINT SourceFileNameOnly$
(IndexLoop
) PRINT "If the listing exceeds one page in length, press Enter to advance one line at a time or press the" PRINT "spacebar to advance a full page." ClearScreen
SHELL "type wiminfo.txt | more"
' ********************************************************************************
' * Below are subroutines that need to appear after the end of the program. *
' * Subroutines called by name reference and placed in SUB / END SUB blocks must *
' * be placed after the end of the main program. *
' ********************************************************************************
' Remove any quotes or trailing backslash from a path
' We the inner loop will strip the quotes off a path and a backslash if the path ends
' with a backslash. However, if the trailing backslash is within the quotes this won't
' get stripped. As a result, we'll run that inner loop a second time because now the
' trailing backslash is at the end of the path so it will get stripped this time.
' Note that we may want a trailing backslash in various places in the program, but to
' prevent getting a double backslash we are stripping off the trailing backslash here
' so that we always consistently have a path without the trailing backslash to start.
' To use this subroutine: Pass the path this sub, the sub will return the path
' without quotes or a trailing backslash in Temp$.
Temp$ = ""
Character$
= MID$(Path$
, Y
, 1) Temp$ = Temp$ + Character$
' END OF CleanPath SUBROUTINE
SUB FileTypeSearch
(Path$
, FileType$
)
' This routine will receive a path and a file name extension. It will search the path specified
' for any occurances of files with the specified extension. It will return the number of those files
' found in NumberOfFiles and each of those file names in an array called TempArray$().
' The path passed to this subroutine should end with a trailing backslash. Example: D:\MyFolder\
' Initialize the variables
NumberOfFiles = 0 ' Set initial value
' Build the command to be run
Cmd$
= "DIR /B " + CHR$(34) + Path$
+ "*" + FileType$
+ CHR$(34) + " > TEMP.TXT"
NumberOfFiles = NumberOfFiles + 1
TempArray$(NumberOfFiles) = Path$ + file$
SUB MountISO
(ImagePath$
)
' This routine will mount the ISO image at the path passed from the main program and
' will get the CDROM ID (Ex. \\.\CDROM0) and save in MountedImageCDROMID$. It will also
' get the drive letter from and save in MountedImageDriveLetter$.
CleanPath (ImagePath$)
ImagePath$ = Temp$
cmd$
= "powershell.exe -command " + CHR$(34) + "Mount-DiskImage " + CHR$(34) + "'" + ImagePath$
+ "'" + CHR$(34) + CHR$(34) + " > MountInfo1.txt" cmd$
= "powershell.exe -command " + CHR$(34) + "Get-DiskImage -ImagePath '" + ImagePath$
+ "' | Get-Volume" + CHR$(34) + " > MountInfo2.txt"
MountedImageCDROMID$
= RIGHT$(GetLine$
, LEN(GetLine$
) - ((INSTR(1, GetLine$
, "\\.\CDROM"))) + 1)
MountedImageDriveLetter$
= LEFT$(GetLine$
, 1) + ":"
' When a QB64 program is run with display going to the Windows console rather than the Program Console,
' the CLS command will not work. This will issue a CLS from the Windows console.
SUB Cleanup
(CleanupPath$
)
' Pass the name of the folder to cleanup to this routine. It will return
' CleanupSuccess=0 if it failed, CleanupSuccess=1 if successful, CleanupSuccess=2 if
' the specified folder does not exist. NOTE: Currently, nothing is checking for a
' return of 2. This routine itself simply displays a message if the folder does not exist.
' When injecting either Windows updates or drivers into an ISO image, if
' the process is aborted without being allowed to finish, files may be
' left behind that you cannot delete manually. This routine will try to
' correct that situation. Note that this routine will only try to delete folders named
' "Mount", "ISO_Files", and "Scratch". This assures that we maintain any log files in
' the "LOGS" folder.
' If the files still exist after the intial cleanup attempt, then we will attempt
' a fix by closing any open DISM session. If that still fails, we will inform the
' user that a reboot may be needed.
' After an automatic cleanup attempt, we will set the variable AutoCleanup to 1 so that we know that a
' cleanup has already been attempted. That way, if we fail again we know that we need to abort and
' warn the user.
' Initialize AutoCleanup to 0 before the start of the cleanup process.
' First, let's check to see if the specified folder even exists. If not, then there
' is nothing to cleanup but we still consider that successful because there are no
' unwanted files present.
ClearScreen
PRINT "There is nothing to cleanup because that folder does not exist!" CleanupSuccess = 2
AutoCleanup = 0 ' Set initial value
ClearScreen
PRINT "Attempting cleanup of the folder: ";
CHR$(34); CleanupPath$;
CHR$(34) PRINT "Please standby. This may take a little while..."
StartCleanup:
TempPath$ = CleanupPath$ + "Mount\"
TempPath$ = CleanupPath$ + "ISO_Files\"
TempPath$ = CleanupPath$ + "Scratch\"
' If we reach this point, then the folders we tried to delete were successfully deleted.
' The following code handles the situation if we were not able to delete the folders.
FoldersNotDeleted:
' If we arrive here, it means that trying to delete the "ISO_Files", "Mount", or "Scratch"
' folders from the location specified by the user failed. We are now going to try other measures
' to correct this.
Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /Unmount-Image /MountDir:" + CHR$(34) + CleanupPath$
+ "Mount" + CHR$(34) + " /discard" Cmd$
= CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /cleanup-wim" AutoCleanup = 1
' If we arrive here, it means that we failed to delete at least one folder, and we have attempted an
' automatic cleanup, but that also failed.
ClearScreen
PRINT "We were not able to delete one or more folders under the" PRINT "The program has already automatically attempted to correct the situation but was not able to" PRINT "do so. The most likely cause for this is that a previous run of this program may have been" PRINT "aborted while the Microsoft DISM utility still had files locked." PRINT "Please try rebooting the computer, and then try running this program again."
CleanupSuccess = 0
DeletionSuccessful:
CleanupSuccess = 1
NoSuchFolder:
CleanupSuccess = 2
EndCleanup:
' This routine checks whether a user responded with a valid "yes" or "no" response. The routine will return a capital "Y" in YN$
' if the user response was a valid "yes" response, a capital "N" if it was a valid "no" response, or an "X" if not a valid response.
' Valid responses are the words "yes" or "no" or the letters "y" or "n" in any case (upper, lower, or mixed). Anything else is invalid.
YN$ = "Y"
YN$ = "N"
YN$ = "X"