Author Topic: A program to manage and update Windows 10 images  (Read 4121 times)

0 Members and 1 Guest are viewing this topic.

Offline hanness

  • Forum Regular
  • Posts: 210
    • View Profile
A program to manage and update Windows 10 images
« on: May 02, 2019, 11:06:24 pm »
Before I even begin, let me point out that I am not a professional programmer so if this code doesn't look too elegant or efficient, my apologies. I'm sure some of you will look at sections of the code and wonder what on earth I was thinking :-)

This program was the result of my playing with Unattended Setup for Windows 10. As I was learning, I started to find that my learning was being impaired by having to run the same long, tedious commands over and over. I started to seek a way to automate these processes and started writing a little code. As I went along, I kept running into new things that I thought might be handy to add. I just thought that I would share in case anyone else found this helpful.

As it stands today, this program's main features are:

1) Inject the latest Windows updates into your Windows ISO images to keep them up to date. You can point to a folder that has multiple ISO images. The program will let you update all the images in that folder or select those images that you want to update.

2) Inject drivers into your ISO images. By injecting all your drivers into an image, you can assure that after you install a clean copy of Windows, all the drivers for your system will already be installed. As with #1, you can work on all files in a folder, or any images you select.

3) Make a bootable thumb drive from a Windows ISO image. The bootable thumb drive that this routine creates is a little different than most you have probably seen before. It creates two partitions on the thumb drive. The first partition is a FAT partition since some computers don't like booting from NTFS, and the second partition is NTFS so that images over 4GB can be easily implemented without having to split them. This part of the program will also allow you to add additional partitions to your Windows bootable thumb drive so that you can keep additional data on the same drive. It will even optionally bitlocker encrypt those partitions for you, if you wish.

4) If you have a folder with files from a Windows image, you can create a bootable image from those files. I use this if I have been modifying files or adding files such as an autounattend.xml for creating unattended Windows installations.

5) Export drivers from your system. This will allow you to export all the drivers from yur system to a folder. You can then use #2 to inject them into a Windows image.

6) Expand drivers in a CAB file. If you have drivers that you want to inject into a Windows ISO image that are provided in a CAB file, the CAB file first needs to be expanded.

7) Create a Virtual Hard Disk (VHDX). Sometimes, when I'm working on projects, I like to create a VHDX to work with. This automates the process.

8) Create a generic ISO image. Sometimes I prefer to drop files into an ISO image rather than a ZIP. An ISO image can be created very quickly and you can simply mount it to a drive letter for super handy access.


Code: QB64: [Select]
  1. $EXEICON:'iso.ico'
  2. $VERSIONINFO:FILEVERSION#=1,5,0,0
  3. $VERSIONINFO:ProductName=Windows Image Manager
  4.  
  5. ' Windows Image Manager
  6.  
  7. ' This program is intended to be run on Windows 10 64-Bit and compiled with 64-Bit QB64 1.3.
  8.  
  9. ' We are running a lot of Windows command line commands so we are setting this program to run within a command console.
  10.  
  11.  
  12. ' **************************
  13. ' * Declare variables here *
  14. ' **************************
  15.  
  16. DIM AdditionalPartitions AS INTEGER ' The number of partitions that a user wants to add to a bootable thumb drive
  17. DIM AddPart AS STRING ' Set to either "Y" or "N" depending upon whether user wishes to add additional partitions to a bootable thumb drive
  18. 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.
  19. DIM BitlockerCount AS INTEGER ' Stores the number of partitions that need to be encrypted
  20. DIM CDROM AS STRING ' The drive letter assigned to the mounted ISO image
  21. DIM ClosedParenPosition AS INTEGER ' Used in the StripTimeAndDateStamp routine to hold the position within the file name where the last Closed Parenthesis is located
  22. 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
  23. DIM CurrentFileNum AS INTEGER ' Keeps track of how many files we have updated in the loop to inject updates or drivers
  24. DIM DestinationFileName AS STRING ' The file name of the ISO image to be created without a path
  25. 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
  26. DIM DestinationPath AS STRING ' The destination path for the ISO image without a file name
  27. DIM DestinationPathAndFile AS STRING ' The full path including the file name of the ISO image to be created
  28. 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
  29. DIM DiskID AS INTEGER ' Used in 2 places to ask the user for a DiskID as presented by the Microsoft DiskPart utility
  30. DIM ExportFolder AS STRING ' Used by the routine for exporting drivers from a system. This string is the path to which drivers are exported
  31. 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
  32. 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
  33. DIM IndexLoop AS INTEGER ' Used as a counter to keep track of file we are asking user for an index number
  34. DIM LineCount AS INTEGER ' General purpose counter that holds how many lines we have read from a file
  35. DIM Location AS STRING ' Used in the StripTimeAndDateStamp routine to hold the path of files to be processed
  36. 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
  37. 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
  38. DIM MenuSelection AS INTEGER ' Will hold the number of the menu option selected by the user
  39. DIM NewFile AS STRING ' Used in the StripTimeAndDateStamp routine to hold the file name with the time and date stamp stripped off
  40. DIM NumberOfFilesToUpdate AS INTEGER ' Holds the number of files in the folder that the user wants to update
  41. DIM NumberOfLines AS INTEGER ' Tell the program how many lines to read in a file
  42. DIM OpenParenPosition AS INTEGER ' Used in the StripTimeAndDateStamp routine to hold the position within the file name where the last Open Parenthesis is located
  43. DIM ProgramVersion AS STRING ' Holds the current version number of the program
  44. DIM ProgramReleaseDate AS STRING ' Holds the release date of the program
  45. DIM SourceFolder AS STRING ' Will hold a folder name
  46. DIM StripThisFile AS STRING ' Holds a Yes / No response from user in the routine to strip time and date stamp from file names
  47. DIM TempPath AS STRING ' A temporary variable used while manipulating strings
  48. 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
  49. DIM TotalPartitions AS INTEGER ' The total number of partitions that need to be created on a bootable thumb drive
  50. DIM SourcePath AS STRING ' Holds the path containing the files to be injected into an ISO image file
  51. 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
  52. DIM Updates AS STRING ' The path to the Windows updates that are to be injected into the ISO images
  53. DIM VHDXPath AS STRING ' The path to where a virtual disk drive is to be created
  54. DIM VHDXFileName AS STRING ' The file name to give a virtual disk drive that is to be created
  55. DIM VHDXSize AS INTEGER ' The size in MB to create the virtual disk drive
  56. DIM VHDXLetter AS STRING ' The drive letter to assign to the virtual disk drive after it is created and mounted
  57. 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
  58. DIM x AS INTEGER ' Generic variable reused throughout program, mainly as a counter in FOR ... NEXT loops
  59.  
  60. ' Variables dimensioned as SHARED (Globally accessible to the main program and SUB procedures)
  61.  
  62. DIM SHARED Temp AS STRING ' Temporary string value that can be shared with subroutines and also used elsewhere as temporary storage
  63. DIM SHARED FileCount AS INTEGER ' The number of ISO image files that need to be processed
  64. 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
  65. DIM SHARED MountedImageCDROMID AS STRING ' The MountISO returns this value
  66. DIM SHARED MountedImageDriveLetter AS STRING ' The MountISO returns this value
  67. 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
  68. 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
  69.  
  70. ' Arrays
  71.  
  72. 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.
  73.  
  74. ' The following arrays are dimensioned dynamically within the program.
  75.  
  76. ' AutoUnlock$() -  Flag is set to either "Y" or "N" to indicate whether this partition should be autounlocked on current system
  77. ' BitLockerFlag$() - Flag is set to either "Y" or "N" to indicate whether this partition should be encrypted
  78. ' FileArray$() - Will hold the name of each ISO image file to be updated.
  79. ' FinalFileNameOnly$() - This is the destination file name without a path
  80. ' FinalFilePathAndName$() - This is the full path and file name of the destination file to be created
  81. ' IndexArray$() - Holds the selected the index number for the Windows edition chosen for each ISO image.
  82. ' PartitionSize$() - The size to create a partition converted to a string so that leading space is stripped out.
  83. ' SourceFileNameOnly$() - The name of the source ISO image file without a path
  84.  
  85. ' ********************************
  86. ' * End of variable declarations *
  87. ' ********************************
  88.  
  89.  
  90. ' ***********************************************************
  91. ' ***********************************************************
  92. ' ** The following strings hold the program version number **
  93. ' **               and program release date.               **
  94. ' **            Make sure to keep this updated.            **
  95. ' ***********************************************************
  96. ' ***********************************************************
  97.  
  98. ProgramVersion$ = "1.5.0"
  99. ProgramReleaseDate$ = "May 2, 2019"
  100.  
  101. ' This application needs to be run with elevated permissions. Perform a test to see if user started the program with elevated permissions.
  102. ' If not running with elevated permissions, then relaunch the program with elevated permissions and close the original instance of the program.
  103.  
  104. Cmd$ = ">nul 2>&1 " + CHR$(34) + "%SYSTEMROOT%\system32\cacls.exe" + CHR$(34) + " " + CHR$(34) + "%SYSTEMROOT%\system32\config\system" + CHR$(34)
  105. errorlevel = _SHELLHIDE(Cmd$)
  106. IF errorlevel <> 0 THEN
  107.     Cmd$ = "powershell.exe Start-Process '" + (MID$(COMMAND$(0), _INSTRREV(COMMAND$(0), "\") + 1)) + "' -Verb runAs"
  108.     SHELL Cmd$
  109.     SYSTEM
  110.  
  111. ' If we reach this point then the program was run elevated.
  112.  
  113. ' Display the main menu
  114.  
  115. MainMenu:
  116.  
  117. _CONSOLETITLE "WIM Version" + ProgramVersion$ + " by Hannes Sehestedt"
  118.  
  119. ClearScreen
  120. PRINT " Windows Image Manager (WIM) Tools"
  121. PRINT " Version "; ProgramVersion$
  122. PRINT " Released "; ProgramReleaseDate$
  123. PRINT "    1) Inject Windows updates into one or more Windows ISO images"
  124. PRINT "    2) Inject drivers into one or more Windows images"
  125. PRINT "    3) Make a bootable thumb drive from a Windows ISO image"
  126. PRINT "    4) Create a bootable ISO image from Windows files in a folder"
  127. PRINT "    5) Export drivers from this system"
  128. PRINT "    6) Expand drivers supplied in a .CAB file"
  129. PRINT "    7) Create a Virtual Disk (VHDX)"
  130. PRINT "    8) Create a generic ISO image and inject files and folders into it"
  131. PRINT "    9) Strip the time and date stamp from filenames"
  132. PRINT "   10) Cleanup files and folders"
  133. PRINT "   11) Display help and information for this program"
  134. PRINT "   12) Exit"
  135. INPUT "    Please make a selection by number: "; MenuSelection
  136.  
  137. IF MenuSelection = 1 THEN GOTO InjectUpdates
  138. IF MenuSelection = 2 THEN GOTO InjectDrivers
  139. IF MenuSelection = 3 THEN GOTO MakeBootDisk
  140. IF MenuSelection = 4 THEN GOTO MakeBootDisk2
  141. IF MenuSelection = 5 THEN GOTO ExportDrivers
  142. IF MenuSelection = 6 THEN GOTO ExpandDrivers
  143. IF MenuSelection = 7 THEN GOTO CreateVHDX
  144. IF MenuSelection = 8 THEN GOTO CreateISOImage
  145. IF MenuSelection = 9 THEN GOTO StripTimeAndDateStamp
  146. IF MenuSelection = 10 THEN GOTO GetFolderToClean
  147. IF MenuSelection = 11 THEN GOTO ShowHelp
  148. IF MenuSelection = 12 THEN GOTO ProgramEnd
  149.  
  150. ' We arrive here if the user makes an invalid selection from the main menu
  151.  
  152. ClearScreen
  153. PRINT "You have made an invalid selection. You need to make a selection by entering a number from 1 to 12."
  154. SHELL "pause"
  155. GOTO MainMenu
  156.  
  157.  
  158. ' ***************************************************************************
  159. ' * Menu item #1: Inject Windows updates into one or more Windows ISO images *
  160. ' ***************************************************************************
  161.  
  162. InjectUpdates:
  163.  
  164. ' This routine will inject Windows updates into one or more Windows ISO images. The ISO images can be
  165. ' clean, plain Windows ISO images, or they can be ISO images with an answer file for unattended setup,
  166. ' or even ISO images with a sysprep installation.
  167.  
  168. ' Make sure that SourceFolder$ is initially empty.
  169.  
  170. SourceFolder$ = ""
  171.  
  172.     ClearScreen
  173.     PRINT "This routine will allow you to update your Windows 10 ISO images by injecting Windows updates into the images."
  174.     PRINT "You can update update all images in the location that you specify, or just some of them."
  175.     PRINT
  176.     INPUT "Enter the path to the source files: "; SourceFolder$
  177. LOOP WHILE SourceFolder$ = ""
  178.  
  179. CleanPath SourceFolder$
  180. SourceFolder$ = Temp$ + "\"
  181.  
  182. ' Verify that the path specified exists.
  183.  
  184. IF NOT (_DIREXISTS(SourceFolder$)) THEN
  185.     ClearScreen
  186.     PRINT "The location that you specified does not exist or is not valid."
  187.     PRINT
  188.     SHELL "pause"
  189.     GOTO InjectUpdates
  190.  
  191. ' Perform a check to see if files with a .ISO extension exist in specified folder.
  192. ' We are going to call the FileTypeSearch subroutine for this. We will pass to it
  193. ' the path to search and the extension to search for. It will return to us the number
  194. ' of files with that extension in the variable called filecount and the name of
  195. ' each file in an array called FileArray$(x).
  196.  
  197. FileTypeSearch SourceFolder$, ".ISO"
  198.  
  199. ' 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
  200.  
  201. FileCount = NumberOfFiles
  202.  
  203. ClearScreen
  204.  
  205. IF FileCount = 0 THEN
  206.     PRINT "No files with the .ISO extension were found."
  207.     PRINT "Please specify another folder."
  208.     PRINT
  209.     SHELL "pause"
  210.     GOTO InjectUpdates
  211.  
  212. ' Initialize arrays and other variables
  213.  
  214. NumberOfFilesToUpdate = 0
  215. UpdateAll$ = ""
  216.  
  217. REDIM FileArray(FileCount) AS STRING
  218. REDIM IndexArray(FileCount) AS INTEGER
  219. REDIM FinalFilePathAndName(FileCount) AS STRING
  220. REDIM FinalFileNameOnly(FileCount) AS STRING
  221. REDIM SourceFileNameOnly(FileCount) AS STRING
  222. REDIM UpdateFlag(FileCount) AS STRING
  223.  
  224.  
  225. ' Take the temporary array called TempArray$() and save the values in FileArray$()
  226. ' We also want to have the name of the files with the path stripped out. We are going to store the file
  227. ' names without a path in the array called SourceFileNameOnly$().
  228.  
  229. FOR x = 1 TO FileCount
  230.     FileArray$(x) = TempArray$(x)
  231.     SourceFileNameOnly$(x) = MID$(FileArray$(x), (_INSTRREV(FileArray$(x), "\") + 1))
  232.  
  233. UpdateAll:
  234.  
  235. ClearScreen
  236. IF FileCount > 1 THEN
  237.     INPUT "Do you want to update all ISO images in this folder "; UpdateAll$
  238.     UpdateAll$ = "Y"
  239.  
  240. YesOrNo UpdateAll$
  241. UpdateAll$ = YN$
  242. IF UpdateAll$ = "X" THEN GOTO UpdateAll
  243.  
  244. IF UpdateAll$ = "Y" THEN
  245.     FOR x = 1 TO FileCount
  246.         UpdateFlag$(x) = "Y"
  247.     NEXT x
  248.     NumberOfFilesToUpdate = FileCount
  249.     GOTO GetDestinationPath
  250.  
  251. FOR x = 1 TO FileCount
  252.  
  253.     Marker1:
  254.  
  255.     ClearScreen
  256.     PRINT "Do you want to add updates to the file named: "; CHR$(34); SourceFileNameOnly$(x); CHR$(34);
  257.     INPUT UpdateFlag$(x)
  258.     YesOrNo UpdateFlag$(x)
  259.     SELECT CASE YN$
  260.         CASE "X"
  261.             PRINT
  262.             PRINT "Please provide a valid response."
  263.             PRINT
  264.             SHELL "pause"
  265.             GOTO Marker1
  266.         CASE "Y"
  267.             UpdateFlag(x) = "Y"
  268.             NumberOfFilesToUpdate = NumberOfFilesToUpdate + 1
  269.         CASE ELSE
  270.             UpdateFlag$(x) = "N"
  271.     END SELECT
  272.  
  273. IF NumberOfFilesToUpdate = 0 THEN
  274.     ClearScreen
  275.     PRINT "You have not selected any files to update. We will now return to the main menu."
  276.     PRINT
  277.     SHELL "pause"
  278.     GOTO MainMenu
  279.  
  280. GetDestinationPath:
  281.  
  282. ' Now that we have a valid source directory and we know that there are ISO images
  283. ' located there, ask the user for the location where we should save the updated
  284. ' files with the Windows updates applied.
  285.  
  286. DestinationFolder$ = "" ' Set initial value
  287.  
  288.     ClearScreen
  289.     INPUT "Enter the path to which we should save the updated files: "; DestinationFolder$
  290. LOOP WHILE DestinationFolder$ = ""
  291.  
  292. CleanPath DestinationFolder$
  293. DestinationFolder$ = Temp$ + "\"
  294.  
  295. ' Verify that the path specified exists.
  296.  
  297. IF NOT (_DIREXISTS(DestinationFolder$)) THEN
  298.  
  299.     ' The destination path does not exist. We will now attempt to create it.
  300.  
  301.     ClearScreen
  302.     PRINT "Destination folder does not exist. Attempting to create it..."
  303.     Cmd$ = "md " + CHR$(34) + DestinationFolder$ + CHR$(34)
  304.     SHELL _HIDE Cmd$
  305.  
  306.     ' Checking for existance of folder again again to see if we were able to create it.
  307.  
  308.     IF NOT (_DIREXISTS(DestinationFolder$)) THEN
  309.         PRINT
  310.         PRINT "We were not able to create the destination folder."
  311.         PRINT "Please recheck the path you have specified and try again."
  312.         PRINT
  313.         SHELL "pause"
  314.         GOTO GetDestinationPath
  315.     END IF
  316.  
  317. ' If we have arrived here it means that the destination path already exists
  318. ' or we were able to create it successfully.
  319.  
  320. ' Now, ask for the location of the Windows updates to inject
  321.  
  322. GetUpdatesLocation:
  323.  
  324. Updates$ = "" 'Set initial value
  325.  
  326.     ClearScreen
  327.     INPUT "Enter the path to the Windows update files: "; Updates$
  328. LOOP WHILE Updates$ = ""
  329.  
  330. CleanPath Updates$
  331. Updates$ = Temp$
  332.  
  333. ' Verify that the path specified exists.
  334.  
  335. IF NOT (_DIREXISTS(Updates$)) THEN
  336.  
  337.     ' The path does not exist. Inform user and allow them to try again.
  338.  
  339.     ClearScreen
  340.     PRINT "The updates folder does not exist. Please try again."
  341.     PRINT
  342.     SHELL "pause"
  343.     GOTO GetUpdatesLocation
  344.  
  345. ' If we have arrived here it means that the updates path is valid.
  346. ' Now, verify that update files actually exist in this location.
  347.  
  348. FileTypeSearch Updates$ + "\", ".MSU"
  349. NumberOfUpdates = NumberOfFiles
  350.  
  351. IF NumberOfUpdates = 0 THEN
  352.     PRINT
  353.     PRINT "No update files were found in this location."
  354.     PRINT "Please specify another location."
  355.     PRINT
  356.     SHELL "PAUSE"
  357.     GOTO GetUpdatesLocation
  358.  
  359. ' Go through the list of ISO images and ask what index is associated with the edition that the
  360. ' user wants to update. If user does not know the index, display the indexs available.
  361.  
  362. ClearScreen
  363. PRINT "Each Windows ISO image can contain multiple editions of Windows. For example,"
  364. PRINT "Windows Professional, Home, Education edition, etc."
  365. PRINT "Each edition has an index number associated with it. We need to know the index"
  366. PRINT "number for the edition that you want to update."
  367. PRINT "We will now ask you for the index number associated with the edition that you"
  368. PRINT "want to update. If you do not know the index number, simply press ENTER and the"
  369. PRINT "program will display a list of Windows editions and the associated index numbers."
  370. SHELL "pause"
  371.  
  372. FOR IndexLoop = 1 TO FileCount
  373.     IF UpdateFlag$(IndexLoop) = "Y" THEN
  374.         DO
  375.             ClearScreen
  376.             PRINT "Enter the index number for this file:"
  377.             PRINT SourceFileNameOnly$(IndexLoop)
  378.             PRINT
  379.             INPUT "Enter index number: "; IndexArray(IndexLoop)
  380.             IF IndexArray(IndexLoop) = 0 THEN
  381.                 ClearScreen
  382.                 PRINT "Preparing to display a list of Windows Editions and the associated index numbers."
  383.                 PRINT "This may take a little while. Please standby..."
  384.                 PRINT
  385.                 GOSUB DisplayIndices
  386.             END IF
  387.         LOOP UNTIL IndexArray(IndexLoop) <> 0
  388.     END IF
  389. NEXT IndexLoop
  390.  
  391. ' If we arrive here, then we have retrieved all indices.
  392.  
  393. ' Ask user if they want to see detailed status while we update their Windows images.
  394.  
  395. Detail:
  396.  
  397. Detail$ = "" ' Set initial value
  398. ClearScreen
  399. PRINT "Do you want to display detailed status information?"
  400. PRINT "If you choose not to display detailed status, then we display a clean,"
  401. PRINT "minimal status."
  402. INPUT "Display detailed status (YES / NO) "; Detail$
  403.  
  404. ' Parse the users response for a valid yes / no response
  405.  
  406. YesOrNo Detail$
  407. Detail$ = YN$
  408. IF Detail$ = "X" THEN
  409.     PRINT
  410.     PRINT "Please provide a valid response."
  411.     PRINT
  412.     SHELL "pause"
  413.     GOTO Detail
  414.  
  415. ' The user entered a valid response
  416.  
  417. DetailIsValid:
  418.  
  419. ' If there is an old logs folder made before this project, delete it, then create a new, empty logs folder
  420. ' so we start clean with this project.
  421.  
  422. ' The logs folder is different than the other folders we will use in the project because we want to keep
  423. ' it through each loop for each file that we process. As a result, we create it before the main loop
  424. ' through each file to process is started.
  425.  
  426. ClearScreen
  427. PRINT "Please standby while we do a little housekeeping...."
  428. TempPath$ = DestinationFolder$ + "logs\"
  429.  
  430. IF _DIREXISTS(TempPath$) THEN
  431.     Cmd$ = "rmdir " + CHR$(34) + TempPath$ + CHR$(34) + " /s /q"
  432.     SHELL _HIDE Cmd$
  433.  
  434. IF NOT (_DIREXISTS(TempPath$)) THEN
  435.     MKDIR DestinationFolder$ + "logs"
  436.  
  437. ' The first time into the update process, we want the MainLoopCount to be equal to 1. Then, with each loop through
  438. ' the process we will increment the counter. See the comments below for the purpose of the AutoCleanup variable.
  439.  
  440. ' Initial variables
  441.  
  442. CurrentFileNum = 0
  443. MainLoopCount = 1
  444. AutoCleanup = 0
  445.  
  446. BeginUpdateProcess:
  447.  
  448. IF UpdateFlag$(MainLoopCount) = "Y" THEN GOTO UpdateThisImage
  449. IF MainLoopCount = FileCount THEN GOTO FinishedWithUpdates
  450. MainLoopCount = MainLoopCount + 1
  451. GOTO BeginUpdateProcess
  452.  
  453. UpdateThisImage:
  454.  
  455. CurrentFileNum = CurrentFileNum + 1
  456.  
  457. ' Before starting the update process, verify that there are no leftover files sitting in the
  458. ' destination.
  459.  
  460. Cleanup DestinationFolder$
  461. IF CleanupSuccess = 0 THEN GOTO MainMenu
  462.  
  463. ' Create the folders we need for the project.
  464.  
  465. MKDIR DestinationFolder$ + "Mount"
  466. MKDIR DestinationFolder$ + "ISO_Files"
  467. MKDIR DestinationFolder$ + "Scratch"
  468.  
  469. ' Set a couple of variables to the current image to be processed and the index number to be processed.
  470.  
  471. CurrentSourceFile$ = FileArray$(MainLoopCount)
  472. CurrentIndex = IndexArray(MainLoopCount)
  473.  
  474. ' 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$
  475.  
  476. Index$ = STR$(IndexArray(MainLoopCount))
  477. Index$ = RIGHT$(Index$, ((LEN(Index$) - 1)))
  478.  
  479. ' Time to finally start the real work
  480.  
  481. ClearScreen
  482. PRINT "    Currently working on image #"; CurrentFileNum; "of"; NumberOfFilesToUpdate
  483. PRINT "    "; SourceFileNameOnly$(MainLoopCount)
  484. _CONSOLETITLE "WIM Version " + ProgramVersion$ + " - Working on Image" + STR$(CurrentFileNum) + " of" + STR$(NumberOfFilesToUpdate) + " - " + CHR$(34) + SourceFileNameOnly$(MainLoopCount) + CHR$(34)
  485.  
  486. ' 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
  487. ' that command into a string named Cmd$. If the user does not want detailed status, then we run the command with a SHELL _HIDE
  488. ' which will hide the detailed status information.
  489.  
  490. ' > Phase 1 of 8 <
  491.  
  492. PRINT "Phase 1 of 8 - Copying files from the original ISO image to a temporary working folder."
  493.  
  494. ' Mount the original ISO image and copy the files to a temporary working directory.
  495.  
  496. MountISO FileArray$(MainLoopCount)
  497. Cmd$ = "robocopy " + MountedImageDriveLetter$ + "\ " + CHR$(34) + DestinationFolder$ + "ISO_Files" + CHR$(34) + " /mir /zb /256 /R:5 /W:5 /njh /njs"
  498.  
  499. IF Detail$ = "N" THEN
  500.     SHELL _HIDE Cmd$
  501.     SHELL Cmd$
  502.  
  503. ' The next command dismounts the ISO image since we are now done with it. The messages displayed by the process are
  504. ' not really helpful so we are going to hide those messages even if detailed status is selected by the user.
  505.  
  506. Cmd$ = "powershell.exe -command " + CHR$(34) + "Dismount-DiskImage " + CHR$(34) + "'" + CurrentSourceFile$ + "'" + CHR$(34) + CHR$(34)
  507.  
  508. ' The next command removes the Read-Only attribute from all of the files that we just copied.
  509. ' There is no need to display this so we are hiding the output.
  510.  
  511. Cmd$ = "attrib -r " + CHR$(34) + DestinationFolder$ + "ISO_Files\*.*" + CHR$(34) + " /s /d"
  512. PRINT "Copy completed."
  513.  
  514. ' > Phase 2 of 8 <
  515.  
  516. PRINT "Phase 2 of 8 - Mounting the install.wim image in preparation for the update process."
  517. 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)
  518.  
  519. IF Detail$ = "N" THEN
  520.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  521.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  522.  
  523. PRINT "Mount completed."
  524.  
  525. ' > Phase 3 of 8 <
  526.  
  527. PRINT "Phase 3 of 8 - Adding Windows update packages."
  528. 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)
  529.  
  530. IF Detail$ = "N" THEN
  531.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  532.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  533.  
  534. PRINT "Addition of update packages completed."
  535.  
  536. ' > Phase 4 of 8 <
  537.  
  538. PRINT "Phase 4 of 8 - Locking in the updates."
  539. 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)
  540.  
  541. IF Detail$ = "N" THEN
  542.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  543.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  544.  
  545. PRINT "Locking in of the updates completed."
  546.  
  547. ' > Phase 5 of 8 <
  548.  
  549. PRINT "Phase 5 of 8 - Creating log files."
  550. 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)
  551. SHELL CHR$(34) + Cmd$ + CHR$(34)
  552. PRINT "Log creation completed."
  553.  
  554. ' > Phase 6 of 8 <
  555.  
  556. PRINT "Phase 6 of 8 - Unmounting the image and commiting changes."
  557. 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"
  558. IF Detail$ = "N" THEN
  559.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  560.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  561.  
  562. PRINT "Unmount and commit of the image completed."
  563.  
  564. ' Create the final destination file name. We have waited until this point in the process to create
  565. ' these names because we want to assign the timestamp to the file name just before writting the file.
  566. ' The TIME$ function returs the time with colons (:) in the string. These are not valid in file names
  567. ' so we need to convert those to dashes.
  568.  
  569. ' Store the full name and path of the DESTINATION file in the FinalFilePathAndName$() array.
  570. ' Store the name only of the DESTINATION file in the FinalFileNameOnly() array.
  571.  
  572. ' Note that we are not filling the entire array. We are only processing the current file.
  573. ' This ensures that the timestamp appended to the file name is obtained only once we
  574. ' actually start processing that file.
  575.  
  576. TimeFixed$ = ""
  577. Temp$ = TIME$
  578.  
  579. FOR x = 1 TO LEN(Temp$)
  580.     IF MID$(Temp$, x, 1) = ":" THEN
  581.         TimeFixed$ = TimeFixed$ + "-"
  582.     ELSE
  583.         TimeFixed$ = TimeFixed$ + MID$(Temp$, x, 1)
  584.     END IF
  585.  
  586. FinalFilePathAndName$(MainLoopCount) = DestinationFolder$ + (LEFT$(SourceFileNameOnly$(MainLoopCount), (LEN(SourceFileNameOnly$(MainLoopCount)) - 4)) + " (UPDATED " + DATE$ + " " + TimeFixed$) + ").ISO"
  587.  
  588. FOR x = LEN(FinalFilePathAndName$(MainLoopCount)) TO 1 STEP -1
  589.     IF MID$(FinalFilePathAndName$(MainLoopCount), x, 1) = "\" THEN
  590.         FinalFileNameOnly$(MainLoopCount) = RIGHT$(FinalFilePathAndName$(MainLoopCount), (LEN(FinalFilePathAndName$(MainLoopCount)) - x))
  591.         EXIT FOR
  592.     ELSE
  593.         FinalFileNameOnly$(MainLoopCount) = FinalFilePathAndName$(MainLoopCount)
  594.     END IF
  595.  
  596. ' > Phase 7 of 8 <
  597.  
  598. PRINT "Phase 7 of 8 - Creating the final ISO image"
  599. PRINT "               >>> "; FinalFileNameOnly$(MainLoopCount); " <<<"
  600. 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)
  601. IF Detail$ = "N" THEN
  602.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  603.     PRINT "----------------------------------------------------"
  604.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  605.     PRINT "----------------------------------------------------"
  606. PRINT "Creation of the updated ISO image completed."
  607.  
  608. ' Now we will delete the temporary files and folders that we no longer need and update the user with
  609. ' how many images we have updated so far.
  610.  
  611. ' > Phase 8 of 8 <
  612.  
  613. PRINT "Phase 8 of 8 - Cleaning up temporary files and folders."
  614. Cmd$ = "rmdir " + CHR$(34) + DestinationFolder$ + "ISO_Files" + CHR$(34) + " /s /q"
  615. Cmd$ = "rmdir " + CHR$(34) + DestinationFolder$ + "Mount" + CHR$(34) + " /s /q"
  616. Cmd$ = "rmdir " + CHR$(34) + DestinationFolder$ + "Scratch" + CHR$(34) + " /s /q"
  617. PRINT "Cleanup completed."
  618. PRINT "**********************************************"
  619. PRINT MainLoopCount; "ISO image(s) out of"; FileCount; "have been updated."
  620. PRINT "**********************************************"
  621.  
  622. IF CurrentFileNum = NumberOfFilesToUpdate THEN GOTO FinishedWithUpdates
  623.  
  624. IF MainLoopCount <> FileCount THEN
  625.     MainLoopCount = MainLoopCount + 1
  626.     PRINT
  627.     PRINT "Updates inected."
  628.     PRINT "The program will begin processing the next image in a few seconds."
  629.     PRINT
  630.     SLEEP 10
  631.     GOTO BeginUpdateProcess
  632.  
  633. ' If we reach point, then we have finished updating all images that need to be updated.
  634. ' Inform the user that we are done, then return to the main menu.
  635.  
  636. FinishedWithUpdates:
  637.  
  638. ClearScreen
  639. _CONSOLETITLE "WIM Version" + ProgramVersion$ + " by Hannes Sehestedt"
  640. PRINT "That's all!"
  641. PRINT "All images have been processed."
  642. SHELL "pause"
  643. GOTO MainMenu:
  644.  
  645.  
  646. ' ****************************************************************
  647. ' * Menu Item #2: Inject drivers into one or more Windows images *
  648. ' ****************************************************************
  649.  
  650. InjectDrivers:
  651.  
  652. ' This routine will inject drivers into one or more Windows ISO images. The ISO images can be
  653. ' clean, plain Windows ISO images, or they can be ISO images with an answer file for unattended setup,
  654. ' or even ISO images with a sysprep installation.
  655. '
  656. ' Note that this section of code is mostly a copy / paste of the code section used to install Windows
  657. ' updates. To avoid duplicate label names I have simply renamed the labels in this section by adding
  658. ' a "2" to the end of the label name. For example, the label GetDestinationPath becomes GetDestinationPath2.
  659.  
  660. SourceFolder$ = "" ' Set initial value
  661.  
  662.     ClearScreen
  663.     PRINT "This routine will inject drivers into Windows ISO images. You can update all images in"
  664.     PRINT "the location you specify or just some of them."
  665.     PRINT
  666.     INPUT "Enter the path to the ISO images to be updated: "; SourceFolder$
  667. LOOP WHILE SourceFolder$ = ""
  668.  
  669. CleanPath SourceFolder$
  670. SourceFolder$ = Temp$ + "\"
  671.  
  672. ' Verify that the path specified exists.
  673.  
  674. IF NOT (_DIREXISTS(SourceFolder$)) THEN
  675.     ClearScreen
  676.     PRINT "The location that you specified does not exist or is not valid."
  677.     PRINT
  678.     SHELL "pause"
  679.     GOTO InjectDrivers
  680.  
  681. ' Perform a check to see if files with a .ISO extension exist in specified folder.
  682. ' We are going to call the FileTypeSearch subroutine for this. We will pass to it
  683. ' the path to search and the extension to search for. It will return to us the number
  684. ' of files with that extension in the variable called filecount and the name of
  685. ' each file in an array called FileArray$(x).
  686.  
  687. FileTypeSearch SourceFolder$, ".ISO"
  688.  
  689. ' 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
  690.  
  691. FileCount = NumberOfFiles
  692. ClearScreen
  693.  
  694. IF FileCount = 0 THEN
  695.     PRINT "No files with the .ISO extension were found."
  696.     PRINT "Please specify another folder."
  697.     PRINT
  698.     SHELL "pause"
  699.     GOTO InjectDrivers
  700.  
  701. ' Initialize arrays and other variables
  702.  
  703. NumberOfFilesToUpdate = 0
  704. UpdateAll$ = ""
  705.  
  706. REDIM FileArray(FileCount) AS STRING
  707. REDIM IndexArray(FileCount) AS INTEGER
  708. REDIM FinalFilePathAndName(FileCount) AS STRING
  709. REDIM FinalFileNameOnly(FileCount) AS STRING
  710. REDIM SourceFileNameOnly(FileCount) AS STRING
  711. REDIM UpdateFlag(FileCount) AS STRING
  712.  
  713. ' Take the temporary array called TempArray$() and save the values in FileArray$()
  714. ' We also want to have the name of the files with the path stripped out. We are going to store the file
  715. ' names without a path in the array called SourceFileNameOnly$().
  716.  
  717. FOR x = 1 TO FileCount
  718.     FileArray$(x) = TempArray$(x)
  719.     SourceFileNameOnly$(x) = MID$(FileArray$(x), (_INSTRREV(FileArray$(x), "\") + 1))
  720.  
  721. UpdateAll2:
  722.  
  723.     ClearScreen
  724.     IF FileCount > 1 THEN
  725.         INPUT "Do you want to update all ISO images in this folder "; UpdateAll$
  726.     ELSE
  727.         UpdateAll$ = "Y"
  728.     END IF
  729. LOOP WHILE UpdateAll$ = ""
  730.  
  731. YesOrNo UpdateAll$
  732. UpdateAll$ = YN$
  733. IF UpdateAll$ = "X" THEN GOTO UpdateAll2
  734. IF UpdateAll$ = "Y" THEN
  735.     FOR x = 1 TO FileCount
  736.         UpdateFlag$(x) = "Y"
  737.     NEXT x
  738.     NumberOfFilesToUpdate = FileCount
  739.     GOTO GetDestinationPath2
  740.  
  741. FOR x = 1 TO FileCount
  742.  
  743.     Marker2:
  744.  
  745.     ClearScreen
  746.     PRINT "Do you want to add updates to the file named: "; CHR$(34); SourceFileNameOnly$(x); CHR$(34);
  747.     INPUT UpdateFlag$(x)
  748.     YesOrNo UpdateFlag$(x)
  749.  
  750.     SELECT CASE YN$
  751.         CASE "X"
  752.             PRINT
  753.             PRINT "Please provide a valid response."
  754.             PRINT
  755.             SHELL "pause"
  756.             GOTO Marker2
  757.         CASE "Y"
  758.             UpdateFlag(x) = "Y"
  759.             NumberOfFilesToUpdate = NumberOfFilesToUpdate + 1
  760.         CASE ELSE
  761.             UpdateFlag$(x) = "N"
  762.     END SELECT
  763.  
  764. IF NumberOfFilesToUpdate = 0 THEN
  765.     ClearScreen
  766.     PRINT "You have not selected any files to update. We will now return to the main menu."
  767.     PRINT
  768.     SHELL "pause"
  769.     GOTO MainMenu
  770.  
  771. GetDestinationPath2:
  772.  
  773. ' Now that we have a valid source directory and we know that there are ISO images
  774. ' located there, ask the user for the location where we should save the updated
  775. ' files with the added drivers applied.
  776.  
  777. DestinationFolder$ = "" ' Set initial value
  778.  
  779.     ClearScreen
  780.     INPUT "Enter the destination path: "; DestinationFolder$
  781. LOOP WHILE DestinationFolder$ = ""
  782.  
  783. CleanPath DestinationFolder$
  784. DestinationFolder$ = Temp$ + "\"
  785.  
  786. ' Verify that the path specified exists.
  787.  
  788. IF NOT (_DIREXISTS(DestinationFolder$)) THEN
  789.  
  790.     ' The destination path does not exist. We will now attempt to create it.
  791.  
  792.     ClearScreen
  793.     PRINT "Destination folder does not exist. Attempting to create it..."
  794.     Cmd$ = "md " + CHR$(34) + DestinationFolder$ + CHR$(34)
  795.     SHELL _HIDE Cmd$
  796.  
  797.     ' Checking for existance of folder again again to see if we were able to create it.
  798.  
  799.     IF NOT (_DIREXISTS(DestinationFolder$)) THEN
  800.         PRINT
  801.         PRINT "We were not able to create the destination folder."
  802.         PRINT "Please recheck the path you have specified and try again."
  803.         PRINT
  804.         SHELL "pause"
  805.         GOTO GetDestinationPath2
  806.     END IF
  807.  
  808. ' If we have arrived here it means that the destination path already exists
  809. ' or we were able to create it successfully.
  810.  
  811. ' Now, ask for the location of the Windows updates to inject
  812.  
  813. GetUpdatesLocation2:
  814.  
  815. Updates$ = "" ' Set initial value
  816.  
  817.     ClearScreen
  818.     PRINT "Enter the path to the Windows drivers to inject. Note: All subfolders will be included."
  819.     INPUT "Enter the path: "; Updates$
  820. LOOP WHILE Updates$ = ""
  821.  
  822. CleanPath Updates$
  823. Updates$ = Temp$
  824.  
  825. ' Verify that the path specified exists.
  826.  
  827. IF NOT (_DIREXISTS(Updates$)) THEN
  828.  
  829.     ' The path does not exist. Inform user and allow them to try again.
  830.  
  831.     ClearScreen
  832.     PRINT "The updates folder does not exist. Please try again."
  833.     PRINT
  834.     SHELL "pause"
  835.     GOTO GetUpdatesLocation2
  836.  
  837. ' If we have arrived here it means that the drivers path is valid.
  838.  
  839. ' Go through the list of ISO images and ask what index is associated with the edition that the
  840. ' user wants to update. If user does not know the index, display the indexs available.
  841.  
  842. ClearScreen
  843. PRINT "Each Windows ISO image can contain multiple editions of Windows. For example,"
  844. PRINT "Windows Professional, Home, Education edition, etc."
  845. PRINT "Each edition has an index number associated with it. We need to know the index"
  846. PRINT "number for the edition that you want to add drivers to."
  847. PRINT "We will now ask you for the index number associated with the edition that you"
  848. PRINT "want to update. If you do not know the index number, simply press ENTER and the"
  849. PRINT "program will display a list of Windows editions and the associated index numbers."
  850. SHELL "pause"
  851.  
  852. FOR IndexLoop = 1 TO FileCount
  853.     IF UpdateFlag$(IndexLoop) = "Y" THEN
  854.         DO
  855.             ClearScreen
  856.             PRINT "Enter the index number for this file:"
  857.             PRINT SourceFileNameOnly$(IndexLoop)
  858.             PRINT
  859.             INPUT "Enter index number: "; IndexArray(IndexLoop)
  860.             IF IndexArray(IndexLoop) = 0 THEN
  861.                 ClearScreen
  862.                 PRINT "Preparing to display a list of Windows Editions and the associated index numbers."
  863.                 PRINT "This may take a little while. Please standby..."
  864.                 PRINT
  865.                 GOSUB DisplayIndices
  866.             END IF
  867.         LOOP UNTIL IndexArray(IndexLoop) <> 0
  868.     END IF
  869. NEXT IndexLoop
  870.  
  871. ' If we arrive here, then we have retrieved all indices.
  872.  
  873. Detail2:
  874.  
  875. ' Ask user if they want to see detailed status while we update their Windows images.
  876.  
  877. Detail$ = "" ' Set initial value
  878.  
  879. ClearScreen
  880. PRINT "Do you want to display a detailed status as the update process progresses?"
  881. PRINT "If you choose not to display detailed status, then we display a clean,"
  882. PRINT "streamlined status but without details for each phase."
  883. INPUT "Display detailed status (YES / NO) "; Detail$
  884.  
  885. ' Parse the users response to see determine if it is a valid yes / no response.
  886.  
  887. YesOrNo Detail$
  888. Detail$ = YN$
  889. IF Detail$ = "X" THEN
  890.     PRINT
  891.     PRINT "Please provide a valid response."
  892.     PRINT
  893.     SHELL "pause"
  894.     GOTO Detail2
  895.  
  896. ' The user entered a valid response
  897.  
  898. DetailIsValid2:
  899.  
  900. ' If there is an old logs folder made before this project, delete it, then create a new, empty logs folder
  901. ' so we start clean with this project.
  902.  
  903. ' The logs folder is different than the other folders we will use in the project because we want to keep
  904. ' it through each loop for each file that we process. As a result, we create it before the main loop
  905. ' through each file to process is started.
  906.  
  907. ClearScreen
  908. PRINT "Please standby while we do a little housekeeping...."
  909. TempPath$ = DestinationFolder$ + "logs\"
  910.  
  911. IF _DIREXISTS(TempPath$) THEN
  912.     Cmd$ = "rmdir " + CHR$(34) + TempPath$ + CHR$(34) + " /s /q"
  913.     SHELL _HIDE Cmd$
  914.  
  915. IF NOT (_DIREXISTS(TempPath$)) THEN
  916.     MKDIR DestinationFolder$ + "logs"
  917.  
  918. ' The first time into the update process, we want the MainLoopCouter to be equal to 1. Then, with each loop through
  919. ' the process we will increment the counter. See the comments below for the purpose of the AutoCleanup variable.
  920.  
  921. MainLoopCount = 1 ' Set initial value
  922. AutoCleanup = 0 ' Set initial value
  923.  
  924. BeginUpdateProcess2:
  925.  
  926. IF UpdateFlag$(MainLoopCount) = "Y" THEN GOTO UpdateThisImage2
  927. IF MainLoopCount = FileCount THEN GOTO FinishedWithUpdates2
  928. MainLoopCount = MainLoopCount + 1
  929. GOTO BeginUpdateProcess2
  930.  
  931. UpdateThisImage2:
  932.  
  933. CurrentFileNum = CurrentFileNum + 1
  934.  
  935. ' Before starting the update process, verify that there are no leftover files sitting in the
  936. ' destination.
  937.  
  938. Cleanup DestinationFolder$
  939. IF CleanupSuccess = 0 THEN GOTO MainMenu
  940.  
  941. ' Create the folders we need for the project.
  942.  
  943. MKDIR DestinationFolder$ + "Mount"
  944. MKDIR DestinationFolder$ + "ISO_Files"
  945.  
  946. ' Set a couple of variables to the current image to be processed and the index number to be processed.
  947.  
  948. CurrentSourceFile$ = FileArray$(MainLoopCount)
  949. CurrentIndex = IndexArray(MainLoopCount)
  950.  
  951. ' 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$
  952.  
  953. Index$ = STR$(IndexArray(MainLoopCount))
  954. Index$ = RIGHT$(Index$, ((LEN(Index$) - 1)))
  955.  
  956. ' Time to finally start the real work
  957.  
  958. ClearScreen
  959. PRINT "    Currently working on image #"; CurrentFileNum; "of"; NumberOfFilesToUpdate
  960. PRINT "    "; SourceFileNameOnly$(MainLoopCount)
  961. _CONSOLETITLE "WIM Version " + ProgramVersion$ + " - Working on Image" + STR$(CurrentFileNum) + " of" + STR$(NumberOfFilesToUpdate) + " - " + CHR$(34) + SourceFileNameOnly$(MainLoopCount) + CHR$(34)
  962.  
  963. ' 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
  964. ' that command into a string named Cmd$. If the user does not want detailed status, then we run the command with a SHELL _HIDE
  965. ' which will hide the detailed status information.
  966.  
  967. ' Mount the original ISO image and copy the files to a temporary working directory.
  968.  
  969. ' > Phase 1 of 6 <
  970.  
  971. PRINT "Phase 1 of 6 - Copying files from the original ISO image to a temporary working folder."
  972. MountISO FileArray$(MainLoopCount)
  973. Cmd$ = "robocopy " + MountedImageDriveLetter$ + "\ " + CHR$(34) + DestinationFolder$ + "ISO_Files" + CHR$(34) + " /mir /zb /256 /R:5 /W:5 /njh /njs"
  974.  
  975. IF Detail$ = "N" THEN
  976.     SHELL _HIDE Cmd$
  977.     SHELL Cmd$
  978.  
  979. ' The next command dismounts the ISO image since we are now done with it. The messages displayed by the process are
  980. ' not really helpful so we are going to hide those messages even if detailed status is selected by the user.
  981.  
  982. Cmd$ = "powershell.exe -command " + CHR$(34) + "Dismount-DiskImage " + CHR$(34) + "'" + CurrentSourceFile$ + "'" + CHR$(34) + CHR$(34)
  983.  
  984. ' The next command removes the Read-Only attribute from all of the files that we just copied.
  985. ' There is no need to display this so we are hiding the output.
  986.  
  987. Cmd$ = "attrib -r " + CHR$(34) + DestinationFolder$ + "ISO_Files\*.*" + CHR$(34) + " /s /d"
  988. PRINT "Copy completed."
  989.  
  990. ' > Phase 2 of 6 <
  991.  
  992. PRINT "Phase 2 of 6 - Mounting the install.wim image in preparation for the update process."
  993. 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)
  994.  
  995. IF Detail$ = "N" THEN
  996.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  997.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  998.  
  999. PRINT "Mount completed."
  1000.  
  1001. ' > Phase 3 of 6 <
  1002.  
  1003. PRINT "Phase 3 of 6 - Adding Drivers."
  1004. 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)
  1005.  
  1006. IF Detail$ = "N" THEN
  1007.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  1008.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  1009.  
  1010. PRINT "Addition of drivers completed."
  1011.  
  1012. ' > Phase 4 of 6 <
  1013.  
  1014. PRINT "Phase 4 of 6 - Unmounting the image and commiting changes."
  1015. 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"
  1016.  
  1017. IF Detail$ = "N" THEN
  1018.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  1019.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  1020.  
  1021. PRINT "Unmount and commit of the image completed."
  1022.  
  1023. ' Create the final destination file name. We have waited until this point in the process to create
  1024. ' these names because we want to assign the timestamp to the file name just before writting the file.
  1025. ' The TIME$ function returs the time with colons (:) in the string. These are not valid in file names
  1026. ' so we need to convert those to dashes.
  1027.  
  1028. ' Store the full name and path of the DESTINATION file in the FinalFilePathAndName$() array.
  1029. ' Store the name only of the DESTINATION file in the FinalFileNameOnly() array.
  1030.  
  1031. ' Note that we are not filling the entire array. We are only processing the current file.
  1032. ' This ensures that the timestamp appended to the file name is obtained only once we
  1033. ' actually start processing that file.
  1034.  
  1035. TimeFixed$ = "" ' Set initial value
  1036. Temp$ = TIME$
  1037.  
  1038. FOR x = 1 TO LEN(Temp$)
  1039.     IF MID$(Temp$, x, 1) = ":" THEN
  1040.         TimeFixed$ = TimeFixed$ + "-"
  1041.     ELSE
  1042.         TimeFixed$ = TimeFixed$ + MID$(Temp$, x, 1)
  1043.     END IF
  1044.  
  1045. FinalFilePathAndName$(MainLoopCount) = DestinationFolder$ + (LEFT$(SourceFileNameOnly$(MainLoopCount), (LEN(SourceFileNameOnly$(MainLoopCount)) - 4)) + " (UPDATED " + DATE$ + " " + TimeFixed$) + ").ISO"
  1046.  
  1047. FOR x = LEN(FinalFilePathAndName$(MainLoopCount)) TO 1 STEP -1
  1048.     IF MID$(FinalFilePathAndName$(MainLoopCount), x, 1) = "\" THEN
  1049.         FinalFileNameOnly$(MainLoopCount) = RIGHT$(FinalFilePathAndName$(MainLoopCount), (LEN(FinalFilePathAndName$(MainLoopCount)) - x))
  1050.         EXIT FOR
  1051.     ELSE
  1052.         FinalFileNameOnly$(MainLoopCount) = FinalFilePathAndName$(MainLoopCount)
  1053.     END IF
  1054.  
  1055. ' > Phase 5 of 6 <
  1056.  
  1057. PRINT "Phase 5 of 6 - Creating the final ISO image"
  1058. PRINT "               >>> "; FinalFileNameOnly$(MainLoopCount); " <<<"
  1059. 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)
  1060. IF Detail$ = "N" THEN
  1061.     SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  1062.     PRINT "----------------------------------------------------"
  1063.     SHELL CHR$(34) + Cmd$ + CHR$(34)
  1064.     PRINT "----------------------------------------------------"
  1065. PRINT "Creation of the updated ISO image completed."
  1066.  
  1067. ' Now we will delete the temporary files and folders that we no longer need and update the user with
  1068. ' how many images we have updated so far. Note that we do not use a "Scratch" folder in the process
  1069. ' of injecting drivers as we do for the process of injecting Windows updates, but just in case there
  1070. ' is a left over scratch forlder from a previous Windows update injection process we attempt to delete
  1071. ' it here to keep things tidy.
  1072.  
  1073. ' > Phase 6 of 6 <
  1074.  
  1075. PRINT "Phase 6 of 6 - Cleaning up temporary files and folders."
  1076. Cmd$ = "rmdir " + CHR$(34) + DestinationFolder$ + "ISO_Files" + CHR$(34) + " /s /q"
  1077. Cmd$ = "rmdir " + CHR$(34) + DestinationFolder$ + "Mount" + CHR$(34) + " /s /q"
  1078. Cmd$ = "rmdir " + CHR$(34) + DestinationFolder$ + "Scratch" + CHR$(34) + " /s /q"
  1079. PRINT "Cleanup completed."
  1080. PRINT "**********************************************"
  1081. PRINT MainLoopCount; "ISO image(s) out of"; FileCount; "have been updated."
  1082. PRINT "**********************************************"
  1083.  
  1084. IF CurrentFileNum = NumberOfFilesToUpdate THEN GOTO FinishedWithUpdates2
  1085.  
  1086. IF MainLoopCount <> FileCount THEN
  1087.     MainLoopCount = MainLoopCount + 1
  1088.     PRINT
  1089.     PRINT "The program will begin processing the next image in a few seconds."
  1090.     PRINT
  1091.     SLEEP 10
  1092.     GOTO BeginUpdateProcess2
  1093.  
  1094. ' If we reach point, then we have finished updating all images that need to be updated.
  1095. ' Inform the user that we are done, then return to the main menu.
  1096.  
  1097. FinishedWithUpdates2:
  1098.  
  1099. ClearScreen
  1100. _CONSOLETITLE "WIM Version" + ProgramVersion$ + " by Hannes Sehestedt"
  1101. PRINT "That's all!"
  1102. PRINT "All images have been processed."
  1103. SHELL "pause"
  1104. GOTO MainMenu:
  1105.  
  1106.  
  1107. ' **********************************************************************
  1108. ' * Menu Item #3: Make a bootable thumb drive from a Windows ISO image *
  1109. ' **********************************************************************
  1110.  
  1111. MakeBootDisk:
  1112.  
  1113. ' This routine will allow you to create a bootable thumb drive for installing Windows or
  1114. ' to be used as an emergency boot disk. The option to create one or more additional
  1115. ' partitions will be given. For each of those additional partitions, you will also have
  1116. ' the option to bitlocker encrypt those partititions.
  1117.  
  1118. ' Initialize arrays
  1119.  
  1120. REDIM PartitionSize(4) AS STRING
  1121. REDIM BitLockerFlag(4) AS STRING
  1122. REDIM AutoUnlock(4) AS STRING
  1123. REDIM Letter(4) AS STRING
  1124.  
  1125. ' Get Windows ISO path to copy to the thumb drive
  1126.  
  1127. GetSourceISOForMakeBoot:
  1128.  
  1129. MakeBootableSourceISO$ = "" ' Set initial value
  1130.  
  1131.     ClearScreen
  1132.  
  1133.     PRINT "This routine will create a bootable thumb drive from a Windows ISO image. It can optionally create additional partitions"
  1134.     PRINT "and even bitlocker encrypt them if desired."
  1135.     PRINT
  1136.     INPUT "Enter the full path and file name for the Windows ISO image you want to copy to the thumb drive: "; MakeBootableSourceISO$
  1137. LOOP WHILE MakeBootableSourceISO$ = ""
  1138.  
  1139. IF NOT (_FILEEXISTS(MakeBootableSourceISO$)) THEN
  1140.     PRINT
  1141.     PRINT "That path is not valid. Please try again."
  1142.     PRINT
  1143.     SHELL "pause"
  1144.     GOTO GetSourceISOForMakeBoot
  1145.  
  1146. ' If we reach this point, then the path provided is valid.
  1147.  
  1148. AskForPartitions:
  1149.  
  1150. AddPart$ = "" ' Set initial value
  1151.  
  1152. ClearScreen
  1153. PRINT "We will create two partitions to facilitate making a boot disk that can be booted on"
  1154. PRINT "both BIOS and UEFI based systems. If you want, additional partitions can be created to"
  1155. PRINT "store other data. We can even bitlocker encrypt any of these additional partitions if"
  1156. PRINT "you wish to do so. Please note that you can add a maximum of 2 additional partitions,"
  1157. PRINT "for a total of 4 partitions."
  1158. INPUT "Do you want to create additional partitions "; AddPart$
  1159.  
  1160. ' Parse the users response to determine if it is a valid yes / no response.
  1161.  
  1162. YesOrNo AddPart$
  1163. AddPart$ = YN$
  1164. IF AddPart$ = "X" THEN
  1165.     PRINT
  1166.     PRINT "Please provide a valid response."
  1167.     PRINT
  1168.     SHELL "pause"
  1169.     GOTO AskForPartitions
  1170.  
  1171. ' The user eneterd a valid response
  1172.  
  1173. IF AddPart$ = "N" THEN
  1174.     ' User does not want to create additional partitions so move on to asking what disk thay want to use.
  1175.     ' Also, we know that the total number of partitions will be 2.
  1176.     TotalPartitions = 2
  1177.     GOTO PartitionSizes
  1178.  
  1179. ' The user wants to add partitions. Ask how many additional partitions.
  1180.  
  1181. HowManyPartitions:
  1182.  
  1183. AdditionalPartitions = 0 ' Set initial value
  1184. ClearScreen
  1185. INPUT "How many additional partitions do you want to create "; AdditionalPartitions
  1186.  
  1187. IF (AdditionalPartitions < 1) OR (AdditionalPartitions > 2) THEN
  1188.     PRINT "The number of additional partitions must be 1 or 2."
  1189.     PRINT
  1190.     SHELL "pause"
  1191.     GOTO HowManyPartitions
  1192.  
  1193. ' For each additional partition, except the first and last partition, ask for the size.
  1194. ' The first partition will 500 MB and the last partition will be assigned all remaining space.
  1195.  
  1196. PartitionSizes:
  1197.  
  1198. TotalPartitions = AdditionalPartitions + 2
  1199.  
  1200. ' Get partion sizes. We don't have to ask about the first partition. It will always be 500 MB.
  1201. ' We need to remove the leading space for the partition size so we are going to convert it
  1202. ' to a string. If there are only 2 partitions then we do not need to ask for partition sizes
  1203. ' since the last partition will be set to occupy all remaining space on the drive. In addition,
  1204. ' we don't need to ask about encryption.
  1205.  
  1206. PartitionSize$(1) = "500"
  1207.  
  1208. IF TotalPartitions = 2 THEN GOTO SelectDisk
  1209.  
  1210. FOR x = 2 TO (TotalPartitions - 1)
  1211.  
  1212.     RedoPartitionSize:
  1213.  
  1214.     GOSUB ShowPartitionSizes
  1215.  
  1216.     PRINT
  1217.     PRINT "Enter the size of partition number"; x; "in MB";: INPUT PartitionSize$(x)
  1218.     IF (VAL(PartitionSize$(x))) <= 0 THEN
  1219.         PRINT "You must enter a size larger than 0."
  1220.         PRINT
  1221.         GOTO RedoPartitionSize
  1222.     END IF
  1223.  
  1224. ' For each added partition, ask if it should be bitlocker encrypted.
  1225.  
  1226. AskAboutEncryption:
  1227.  
  1228. FOR x = 1 TO AdditionalPartitions
  1229.  
  1230.     RedoAskAboutEncryption:
  1231.  
  1232.     GOSUB ShowPartitionSizes
  1233.  
  1234.     PRINT
  1235.     PRINT "You have specified that"; AdditionalPartitions; "additional partitions should be added."
  1236.     PRINT
  1237.     PRINT "Do you want to Bitlocker encrypt partition #"; x + 2;: INPUT BitLockerFlag$(x + 2)
  1238.     IF BitLockerFlag$(x + 2) = "" THEN GOTO RedoAskAboutEncryption
  1239.     BitLockerFlag$(x + 2) = UCASE$(BitLockerFlag$(x + 2))
  1240.     BitLockerFlag$(x + 2) = LEFT$(BitLockerFlag$(x + 2), 1)
  1241.     SELECT CASE BitLockerFlag$(x + 2)
  1242.         CASE "Y"
  1243.             GOTO ResponseIsValid2
  1244.         CASE "N"
  1245.             ' User does not want to Bitlocker encrypt this partitition.
  1246.         CASE ELSE
  1247.             PRINT
  1248.             PRINT "Please provide a valid response."
  1249.             PRINT
  1250.             SHELL "pause"
  1251.             ClearScreen
  1252.             GOTO RedoAskAboutEncryption
  1253.     END SELECT
  1254.  
  1255.     ResponseIsValid2:
  1256.  
  1257.     ClearScreen
  1258.     IF BitLockerFlag$(x + 2) = "Y" THEN
  1259.         AskAboutAutoUnlock:
  1260.         INPUT "Do you also want to autounlock this drive on this system "; AutoUnlock$(x + 2)
  1261.         IF AutoUnlock$(x + 2) = "" THEN GOTO ResponseIsValid2
  1262.         AutoUnlock$(x + 2) = UCASE$(AutoUnlock$(x + 2))
  1263.         AutoUnlock$(x + 2) = LEFT$(AutoUnlock$(x + 2), 1)
  1264.         SELECT CASE AutoUnlock$(x + 2)
  1265.             CASE "Y"
  1266.                 GOTO ResponseIsValid3
  1267.             CASE "N"
  1268.                 ' User does not want to autounlock the partition.
  1269.             CASE ELSE
  1270.                 PRINT
  1271.                 PRINT "Please provide a valid response."
  1272.                 PRINT
  1273.                 SHELL "pause"
  1274.                 ClearScreen
  1275.                 GOTO AskAboutAutoUnlock
  1276.         END SELECT
  1277.  
  1278.         ResponseIsValid3:
  1279.  
  1280.     END IF
  1281.  
  1282. ' Display a list of disks and ask which one to use. If user needs more detail on a disk, display that detail.
  1283.  
  1284. SelectDisk:
  1285.  
  1286. ClearScreen
  1287. PRINT "Below is a list of disks seen by your system. Note the ID for the disk that you want to make bootable."
  1288. PRINT "We will also offer to show more details for any disk if you wish. Once you have no more disks for which"
  1289. PRINT "you need to see additional detail, you can then select a disk."
  1290. OPEN "TEMP.BAT" FOR OUTPUT AS #1
  1291. PRINT #1, "@echo off"
  1292. PRINT #1, "(echo list disk"
  1293. PRINT #1, "echo exit"
  1294. PRINT #1, ") | diskpart"
  1295. SHELL "TEMP.BAT"
  1296. IF _FILEEXISTS("TEMP.BAT") THEN KILL "TEMP.BAT"
  1297. DiskID = 0 ' Set initial value
  1298. PRINT "If you want to see more detail for a disk, enter the number here. If not, just hit Enter."
  1299. INPUT "Enter the Disk ID for which you want additional information or hit Enter: "; DiskID
  1300. IF DiskID = 0 THEN GOTO PickDisk:
  1301. OPEN "TEMP.BAT" FOR OUTPUT AS #1
  1302. PRINT #1, "@echo off"
  1303. PRINT #1, "(echo select disk "; DiskID
  1304. PRINT #1, "echo detail disk"
  1305. PRINT #1, "echo exit"
  1306. PRINT #1, ") | diskpart"
  1307. SHELL "TEMP.BAT"
  1308. IF _FILEEXISTS("TEMP.BAT") THEN KILL "TEMP.BAT"
  1309. SHELL "pause"
  1310. GOTO SelectDisk
  1311.  
  1312. PickDisk:
  1313.  
  1314. DiskID = 0 ' Set initial value
  1315.  
  1316.     ClearScreen
  1317.     INPUT "Enter the Disk ID of the disk you want to make bootable"; DiskID
  1318. LOOP WHILE DiskID = 0
  1319.  
  1320. ' Verify that the selected disk is removable
  1321.  
  1322. ClearScreen
  1323. PRINT "Standby..."
  1324. OPEN "TEMP.BAT" FOR OUTPUT AS #1
  1325. PRINT #1, "@echo off"
  1326. PRINT #1, "(echo select disk"; DiskID
  1327. PRINT #1, "echo detail disk"
  1328. PRINT #1, "echo exit"
  1329. PRINT #1, ") | diskpart > DiskpartOut.txt"
  1330. SHELL "TEMP.BAT"
  1331. IF _FILEEXISTS("TEMP.BAT") THEN KILL "TEMP.BAT"
  1332.  
  1333. ' Parse the DiskpartOut.txt file
  1334.  
  1335. LineCount = 0 ' Set initial value
  1336.  
  1337. OPEN "DiskpartOut.txt" FOR INPUT AS #1
  1338.  
  1339.     LINE INPUT #1, Temp$
  1340.     LineCount = LineCount + 1
  1341.  
  1342.  
  1343. ' We are interested in the 4th line from the end so are setting a variable called
  1344. ' NumberOfLine to be LineCount - 3 and we will then analyze that line.
  1345.  
  1346. OPEN "DiskpartOut.txt" FOR INPUT AS #1
  1347. NumberOfLines = LineCount - 3
  1348.  
  1349. FOR x = 1 TO NumberOfLines
  1350.     LINE INPUT #1, Temp$
  1351.  
  1352. KILL "DiskpartOut.txt"
  1353.  
  1354. ' If the disk is removable, then the 9 characters starting with the 39th character of Temp$ will equal "Removable".
  1355.  
  1356. IF (LEN(Temp$) < 48) THEN GOTO NotRemovable
  1357.  
  1358. IF MID$(Temp$, 40, 9) = "Removable" THEN
  1359.     GOTO Removable
  1360.     GOTO NotRemovable
  1361.  
  1362. NotRemovable:
  1363.  
  1364. ClearScreen
  1365. PRINT "The drive that you have chosen is not a removable drive. Please select another drive."
  1366. SHELL "pause"
  1367. GOTO SelectDisk
  1368.  
  1369. Removable:
  1370.  
  1371. ClearScreen
  1372. PRINT "Initializing disk..."
  1373. OPEN "TEMP.BAT" FOR OUTPUT AS #1
  1374. PRINT #1, "@echo off"
  1375. PRINT #1, "(echo select disk"; DiskID
  1376. PRINT #1, "echo clean"
  1377. PRINT #1, "echo convert mbr"
  1378. PRINT #1, "echo exit"
  1379. PRINT #1, "echo ) | diskpart > NUL"
  1380. SHELL "TEMP.BAT"
  1381. IF _FILEEXISTS("TEMP.BAT") THEN KILL "TEMP.BAT"
  1382.  
  1383. ' Get drive letter to assign to each partition
  1384.  
  1385. FOR x = 1 TO TotalPartitions
  1386.     DO
  1387.  
  1388.         RepeatLetter:
  1389.  
  1390.         ClearScreen
  1391.         PRINT "For all"; TotalPartitions; " partitions, enter the drive letter to assign. Enter only the letter"
  1392.         PRINT "without a colon (:)."
  1393.         PRINT
  1394.         Letter$(x) = "" ' Set initial value
  1395.         PRINT "Enter the drive letter for partition #"; x;: INPUT Letter$(x)
  1396.     LOOP WHILE Letter$(x) = ""
  1397.  
  1398.     Letter$(x) = UCASE$(Letter$(x))
  1399.     IF (LEN(Letter$(x)) > 1) OR (Letter$(x)) = "" OR ((ASC(Letter$(x))) < 65) OR ((ASC(Letter$(x))) > 90) THEN
  1400.         PRINT
  1401.         PRINT "That was not a valid entry. Please try again."
  1402.         PRINT
  1403.         GOTO RepeatLetter
  1404.     END IF
  1405.     IF _DIREXISTS(Letter$(x) + ":") THEN
  1406.         PRINT
  1407.         PRINT "That drive letter is already in use."
  1408.         SHELL "pause"
  1409.         GOTO RepeatLetter
  1410.     END IF
  1411.  
  1412. ClearScreen
  1413. PRINT "Preparing disk. Note that this can take a long time, especially if your disk is slow."
  1414. PRINT "Please standby..."
  1415. OPEN "TEMP.BAT" FOR OUTPUT AS #1
  1416. PRINT #1, "@echo off"
  1417. PRINT #1, "(echo select disk"; DiskID
  1418.  
  1419. FOR x = 1 TO TotalPartitions
  1420.     IF x < TotalPartitions THEN
  1421.         PRINT #1, "echo create partition primary size="; PartitionSize$(x)
  1422.         IF x = 1 THEN
  1423.             PRINT #1, "echo active"
  1424.             PRINT #1, "echo format fs=fat32 quick"
  1425.             PRINT #1, "echo assign letter="; Letter$(x)
  1426.         ELSE
  1427.             PRINT #1, "echo format FS=NTFS quick"
  1428.             PRINT #1, "echo assign letter="; Letter$(x)
  1429.         END IF
  1430.     ELSE
  1431.         PRINT #1, "echo create partition primary"
  1432.         PRINT #1, "echo format FS=NTFS quick"
  1433.         PRINT #1, "echo assign letter="; Letter$(x)
  1434.     END IF
  1435.  
  1436. PRINT #1, "echo exit"
  1437. PRINT #1, "echo ) | diskpart > NUL"
  1438. SHELL "TEMP.BAT"
  1439. IF _FILEEXISTS("TEMP.BAT") THEN KILL "TEMP.BAT"
  1440.  
  1441. ' Handle Bitlocker encryption
  1442.  
  1443. ClearScreen
  1444. IF TotalPartitions = 2 THEN GOTO DoneWithBitlocker
  1445.  
  1446. ' We know that there are additional partitions. Determine how many are to be encrypted.
  1447.  
  1448. BitlockerCount = 0 ' Set initial value
  1449.  
  1450. FOR x = 3 TO TotalPartitions
  1451.     IF BitLockerFlag$(x) = "Y" THEN
  1452.         BitlockerCount = BitlockerCount + 1
  1453.     END IF
  1454.  
  1455. IF BitlockerCount = 0 THEN GOTO DoneWithBitlocker
  1456. OPEN "TEMP.BAT" FOR OUTPUT AS #1
  1457. PRINT #1, "@echo off"
  1458.  
  1459. FOR x = 3 TO TotalPartitions
  1460.     IF BitLockerFlag$(x) = "Y" THEN
  1461.         Cmd$ = "%windir%\system32\manage-bde.exe -on " + Letter$(x) + ":" + " -pw -used -em xts_aes128"
  1462.         PRINT #1, Cmd$
  1463.         IF AutoUnlock$(x) = "Y" THEN
  1464.             Cmd$ = "%windir%\system32\manage-bde.exe -autounlock -enable " + Letter$(x) + ":"
  1465.             PRINT #1, Cmd$
  1466.         END IF
  1467.     END IF
  1468.  
  1469. SHELL "TEMP.BAT"
  1470. IF _FILEEXISTS("TEMP.BAT") THEN KILL "TEMP.BAT"
  1471.  
  1472. DoneWithBitlocker:
  1473.  
  1474. MountISO MakeBootableSourceISO$
  1475. CDROM$ = MountedImageDriveLetter$
  1476.  
  1477. ' There will only be a file called autounattend.xml if the user has an image configured with
  1478. ' an unattended answer file. If this file does not exist an error will be displayed. So as not
  1479. ' to alarm the user, we will hide the output from that command by redirecting to NUL.
  1480.  
  1481. OPEN "TEMP.BAT" FOR OUTPUT AS #1
  1482. PRINT #1, "@echo off"
  1483. PRINT #1, "robocopy "; CDROM$; "\ "; Letter$(1); ":\ /mir /xd sources /njs /256"
  1484. PRINT #1, "robocopy "; CDROM$; "\sources "; Letter$(1); ":\sources boot.wim /njh /njs /256"
  1485. PRINT #1, "robocopy "; CDROM$; "\sources "; Letter$(2); ":\sources /mir /njh /njs /xf boot.wim /256"
  1486. PRINT #1, "robocopy "; Letter$(1); "\ "; Letter$(2); "\ /mov autounattend.xml /njh /njs > NUL"
  1487. PRINT #1, "powershell.exe -command "; CHR$(34); "Dismount-DiskImage "; CHR$(34); "'"; MakeBootableSourceISO$; "'"; CHR$(34); CHR$(34)
  1488. PRINT "Ready to copy files to thumb drive. Review the TEMP.BAT to make sure it is okay."
  1489. SHELL "TEMP.BAT"
  1490. IF _FILEEXISTS("TEMP.BAT") THEN KILL "TEMP.BAT"
  1491.  
  1492. ' Making the file ei.cfg on the partition 2, in the sources folder.
  1493.  
  1494. Temp$ = Letter$(2) + ":\sources\ei.cfg"
  1495. OPEN (Temp$) FOR OUTPUT AS #1
  1496. PRINT #1, "[CHANNEL]"
  1497. PRINT #1, "Retail"
  1498.  
  1499. ' All operations are complete.
  1500.  
  1501. ClearScreen
  1502. PRINT "Done!"
  1503. SHELL "pause"
  1504. GOTO MainMenu
  1505.  
  1506.  
  1507. ' Subroutine - Shows patition information.
  1508.  
  1509. ShowPartitionSizes:
  1510.  
  1511. ClearScreen
  1512. PRINT "*******************"
  1513. PRINT "* PARTITION SIZES *"
  1514. PRINT "*******************"
  1515. PRINT "Partition #1: 500 MB (Holds boot files)"
  1516. PRINT "Partition #2:";
  1517. IF PartitionSize$(2) = "" THEN
  1518.     PRINT " NOT YET DEFINED ";
  1519.     PRINT " "; PartitionSize$(2); " MB ";
  1520.  
  1521. PRINT "(Must be large enough to hold contents of Windows image)"
  1522.  
  1523. SELECT CASE TotalPartitions
  1524.     CASE 4
  1525.         PRINT "Partition #3: ";
  1526.         IF PartitionSize$(3) = "" THEN
  1527.             PRINT "NOT YET DEFINED"
  1528.         ELSE
  1529.             PRINT PartitionSize$(3); " MB"
  1530.         END IF
  1531.         PRINT "Partition #4: (All remaining space not assigned to the first three partitions)"
  1532.     CASE 3
  1533.         PRINT "Partition #3: (All remaining space not assigned to the first three partitions)"
  1534.  
  1535.  
  1536.  
  1537. ' ****************************************************************************
  1538. ' * Menu Item #4: Create a bootable ISO image from Windows files in a folder *
  1539. ' ****************************************************************************
  1540.  
  1541. MakeBootDisk2:
  1542.  
  1543. ' This routine will take a folder containing Windows files and create a bootable ISO image from those files.
  1544. ' All files and folders must be present.
  1545.  
  1546. ' Clear any pre-existing values for paths and filenames
  1547.  
  1548. MakeBootablePath$ = ""
  1549. DestinationFolder$ = ""
  1550. DestinationFileName$ = ""
  1551. VolumeName$ = ""
  1552.  
  1553.     ClearScreen
  1554.     PRINT "This routine will take a folder containing Windows files and create a bootable ISO image from those files."
  1555.     PRINT "All Windows files and folders must be present. You can include an autounattend.xml file and/or modified WIM files."
  1556.     PRINT
  1557.     INPUT "Enter the path to the folder holding the Windows files to be made into a bootable ISO image: "; MakeBootablePath$
  1558. LOOP WHILE MakeBootablePath$ = ""
  1559.  
  1560. CleanPath MakeBootablePath$
  1561. MakeBootablePath$ = Temp$
  1562. TempPath$ = MakeBootablePath$ + "\sources\install.wim"
  1563.  
  1564. ' 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.
  1565.  
  1566. IF NOT (_FILEEXISTS(TempPath$)) THEN
  1567.     PRINT
  1568.     PRINT "That path is not valid. No INSTALL.WIM file found at that location. Please try again."
  1569.     PRINT
  1570.     SHELL "pause"
  1571.     GOTO MakeBootDisk2
  1572.  
  1573. ' If we reach this point, then the path provided exists and it contains an INSTALL.WIM file in the SOURCES folder.
  1574.  
  1575. ISODestinationPath:
  1576.  
  1577. DestinationFolder$ = "" ' Set initial value
  1578.  
  1579.     ClearScreen
  1580.     INPUT "Enter the destination path. This is the path only without a file name: "; DestinationFolder$
  1581. LOOP WHILE DestinationFolder$ = ""
  1582.  
  1583. CleanPath DestinationFolder$
  1584. DestinationFolder$ = Temp$
  1585.  
  1586. ' Verify that the path specified exists.
  1587.  
  1588. IF NOT (_DIREXISTS(DestinationFolder$)) THEN
  1589.  
  1590.     ' The destination path does not exist. We will now attempt to create it.
  1591.  
  1592.     ClearScreen
  1593.     PRINT "Destination folder does not exist. Attempting to create it..."
  1594.     PRINT
  1595.     Cmd$ = "md " + CHR$(34) + DestinationFolder$ + CHR$(34)
  1596.     SHELL _HIDE Cmd$
  1597.  
  1598.     ' Checking for existance of folder again again to see if we were able to create it.
  1599.  
  1600.     IF NOT (_DIREXISTS(DestinationFolder$)) THEN
  1601.         PRINT
  1602.         PRINT "We were not able to create the destination folder."
  1603.         PRINT "Please recheck the path you have specified and try again."
  1604.         PRINT
  1605.         SHELL "pause"
  1606.         GOTO ISODestinationPath
  1607.     END IF
  1608.  
  1609. ' If we have arrived here it means that the destination path already exists
  1610. ' or we were able to create it successfully.
  1611.  
  1612. DestinationFileName$ = "" ' Set initial value
  1613.  
  1614.     ClearScreen
  1615.     INPUT "Enter the name of the file to create, without an extension: "; DestinationFileName$
  1616. LOOP WHILE DestinationFileName$ = ""
  1617.  
  1618. GetVolumeName1:
  1619.  
  1620. ' Get the volume name for the ISO image
  1621.  
  1622. ClearScreen
  1623. INPUT "Enter the volume name to give the ISO image or press Enter for none: "; VolumeName$
  1624.  
  1625. IF LEN(VolumeName$) > 32 THEN
  1626.     PRINT
  1627.     PRINT "That volume name is invalid! The volume name is limited to 32 characters."
  1628.     PRINT
  1629.     SHELL "pause"
  1630.     GOTO GetVolumeName1
  1631.  
  1632. ' Create the ISO image
  1633.  
  1634. ClearScreen
  1635. 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)
  1636. PRINT "Creating the ISO image. Please standby..."
  1637. SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  1638. PRINT "Image created."
  1639. SHELL "Pause"
  1640. GOTO MainMenu
  1641.  
  1642.  
  1643. ' *************************************************
  1644. ' * Menu Item #5: Export drivers from this system *
  1645. ' *************************************************
  1646.  
  1647. ExportDrivers:
  1648.  
  1649. ExportFolder$ = "" ' Set initial value
  1650.  
  1651.     ClearScreen
  1652.     PRINT "This routine will export all the drivers in use by this system to a location of your choice."
  1653.     PRINT
  1654.     INPUT "Enter the full path to the location where you want the drivers to be exported: "; ExportFolder$
  1655. LOOP WHILE ExportFolder$ = ""
  1656.  
  1657. CleanPath ExportFolder$
  1658. ExportFolder$ = Temp$
  1659.  
  1660. ' Verify that the path specified exists.
  1661.  
  1662. IF NOT (_DIREXISTS(ExportFolder$)) THEN
  1663.  
  1664.     ' The destination path does not exist. We will now attempt to create it.
  1665.  
  1666.     ClearScreen
  1667.     PRINT "Destination folder does not exist. Attempting to create it..."
  1668.     Cmd$ = "md " + CHR$(34) + ExportFolder$ + CHR$(34)
  1669.     SHELL _HIDE Cmd$
  1670.  
  1671.     ' Checking for existance of folder again again to see if we were able to create it.
  1672.  
  1673.     IF NOT (_DIREXISTS(ExportFolder$)) THEN
  1674.         PRINT
  1675.         PRINT "We were not able to create the destination folder."
  1676.         PRINT "Please recheck the path you have specified and try again."
  1677.         PRINT
  1678.         SHELL "pause"
  1679.         GOTO ExportDrivers
  1680.     END IF
  1681.  
  1682. ' If we have arrived here it means that the path specified already exists
  1683. ' or we were able to create it successfully.
  1684.  
  1685. ' NOTE: If compiled as a 32-Bit program you will need to replace the "system32" in the paths below with "sysnative".
  1686. ' PNPUTIL is only available as a 64-Bit app on 64-bit version of Windows.
  1687. ' As a result, when you try to call it, you have to specify %windir%\sysnative\pnputil.exe rather than %windir%\system32\pnputil.exe
  1688. ' because otherwise Windows will try to redirect the call to %windir%\SysWOW64 to run 32-bit commands.
  1689.  
  1690. Cmd$ = CHR$(34) + "%windir%\system32\pnputil.exe" + CHR$(34) + " /export-driver * " + CHR$(34) + ExportFolder$ + CHR$(34)
  1691. SHELL CHR$(34) + Cmd$ + CHR$(34)
  1692. ClearScreen
  1693. PRINT "Drivers have been exported to the following location: "; ExportFolder$
  1694. SHELL "pause"
  1695. GOTO MainMenu
  1696.  
  1697.  
  1698. ' ********************************************************
  1699. ' * Menu Item #6: Expand drivers supplied in a .CAB file *
  1700. ' ********************************************************
  1701.  
  1702. ExpandDrivers:
  1703.  
  1704. ' This routine will expand drivers that are distributed in .CAB files. Once expanded, Menu Item #5 can
  1705. ' then be used to inject these drivers into Windows ISO images. Note that not all .CAB files can be opened by the
  1706. ' Windows EXPAND utility. Files that cannot be opened will simply be copied to the destination.
  1707.  
  1708. SourceFolder$ = "" ' Set initial value
  1709.  
  1710.     ClearScreen
  1711.     PRINT "This routine will expand drivers that are distributed in .CAB files. Once expanded, Menu Item #5 can"
  1712.     PRINT "then be used to inject these drivers into Windows ISO images. Note that not all .CAB files can be"
  1713.     PRINT "opened by the Windows EXPAND utility. Files that cannot be opened will simply be copied to the destination."
  1714.     PRINT "Drivers that you download from the Microsoft Update Catalog should work with this routine."
  1715.     PRINT
  1716.     INPUT "Enter the path to the drivers that are in .CAB files: "; SourceFolder$
  1717. LOOP WHILE SourceFolder$ = ""
  1718.  
  1719. CleanPath SourceFolder$
  1720. SourceFolder$ = Temp$ + "\"
  1721.  
  1722. ' Verify that the path specified exists.
  1723.  
  1724. IF NOT (_DIREXISTS(SourceFolder$)) THEN
  1725.     ClearScreen
  1726.     PRINT "The location that you specified does not exist or is not valid."
  1727.     PRINT
  1728.     SHELL "pause"
  1729.     GOTO ExpandDrivers
  1730.  
  1731. ' Perform a check to see if files with a .CAB extension exist in specified folder.
  1732. ' We are going to call the FileTypeSearch subroutine for this. We will pass to it
  1733. ' the path to search and the extension to search for. It will return to us the number
  1734. ' of files with that extension in the variable called filecount and the name of
  1735. ' each file in an array called FileArray$(x).
  1736.  
  1737. FileTypeSearch SourceFolder$, ".CAB"
  1738.  
  1739. ' 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
  1740.  
  1741. FileCount = NumberOfFiles
  1742.  
  1743. ClearScreen
  1744.  
  1745. IF FileCount = 0 THEN
  1746.     PRINT "No files with the .CAB extension were found."
  1747.     PRINT "Please specify another folder."
  1748.     PRINT
  1749.     SHELL "pause"
  1750.     GOTO ExpandDrivers
  1751.  
  1752. ' Initialize arrays
  1753.  
  1754. REDIM FileArray(FileCount) AS STRING
  1755. REDIM SourceFileNameOnly(FileCount) AS STRING
  1756.  
  1757. ' Take the temporary array called TempArray$() and save the values in FileArray$()
  1758.  
  1759. FOR x = 1 TO FileCount
  1760.     FileArray$(x) = TempArray$(x)
  1761.  
  1762. ' We already have the names of all the CAB images to be update in the FileArray$() variables. However,
  1763. ' we also want to have the name of the files with the path stripped out. We are going to store the file
  1764. ' names without a path in the array called SourceFileNameOnly$().
  1765.  
  1766. FOR x = 1 TO FileCount
  1767.     FileArray$(x) = TempArray$(x)
  1768.     SourceFileNameOnly$(x) = MID$(FileArray$(x), (_INSTRREV(FileArray$(x), "\") + 1))
  1769.  
  1770. GetDestinationPath3:
  1771.  
  1772. ' Now that we have a valid source directory and we know that there are CAB files
  1773. ' located there, ask the user for the location where we should save the expanded
  1774. ' files.
  1775.  
  1776. DestinationFolder$ = "" ' Set initial value
  1777.  
  1778.     ClearScreen
  1779.     INPUT "Enter the destination path: "; DestinationFolder$
  1780. LOOP WHILE DestinationFolder$ = ""
  1781.  
  1782. CleanPath DestinationFolder$
  1783. DestinationFolder$ = Temp$ + "\"
  1784.  
  1785. ' Verify that the path specified exists.
  1786.  
  1787. IF NOT (_DIREXISTS(DestinationFolder$)) THEN
  1788.  
  1789.     ' The destination path does not exist. We will now attempt to create it.
  1790.  
  1791.     ClearScreen
  1792.     PRINT "Destination folder does not exist. Attempting to create it..."
  1793.     Cmd$ = "md " + CHR$(34) + DestinationFolder$ + CHR$(34)
  1794.     SHELL _HIDE Cmd$
  1795.  
  1796.     ' Checking for existance of folder again again to see if we were able to create it.
  1797.  
  1798.     IF NOT (_DIREXISTS(DestinationFolder$)) THEN
  1799.         PRINT
  1800.         PRINT "We were not able to create the destination folder."
  1801.         PRINT "Please recheck the path you have specified and try again."
  1802.         PRINT
  1803.         SHELL "pause"
  1804.         GOTO GetDestinationPath3
  1805.     END IF
  1806.  
  1807. ' If we have arrived here it means that the destination path already exists
  1808. ' or we were able to create it successfully.
  1809.  
  1810. ' The first time into the update process, we want the MainLoopCouter to be equal to 1. Then, with each loop through
  1811. ' the process we will increment the counter. See the comments below for the purpose of the AutoCleanup variable.
  1812.  
  1813. MainLoopCount = 1 ' Set initial value
  1814.  
  1815. ' Create the folders we need for the project.
  1816.  
  1817. FOR x = 1 TO FileCount
  1818.     ClearScreen
  1819.     PRINT "Processing .CAB file"; x; " of"; FileCount
  1820.     Cmd$ = "md " + CHR$(34) + DestinationFolder$ + "\" + SourceFileNameOnly$(x) + CHR$(34) + " > NUL"
  1821.     SHELL Cmd$
  1822.     Cmd$ = "expand " + CHR$(34) + FileArray$(x) + CHR$(34) + " -f:*.* " + CHR$(34) + DestinationFolder$ + SourceFileNameOnly$(x) + CHR$(34) + " > NUL"
  1823.     SHELL Cmd$
  1824.  
  1825. ' If we reach point, then we have finished updating all images that need to be updated.
  1826. ' Inform the user that we are done, then return to the main menu.
  1827.  
  1828. ClearScreen
  1829. PRINT "That's all!"
  1830. PRINT "All .CAB files have been processed."
  1831. SHELL "pause"
  1832. GOTO MainMenu:
  1833.  
  1834.  
  1835. ' **********************************************
  1836. ' * Menu Item #7: Create a Virtual Disk (VHDX) *
  1837. ' **********************************************
  1838.  
  1839. CreateVHDX:
  1840.  
  1841. VHDXPath$ = "" ' Set initial value
  1842.  
  1843.     ClearScreen
  1844.     PRINT "This routine will allow you to create a Virtual Disk (VHDX)."
  1845.     PRINT
  1846.     PRINT "Please specify the location where you would like to create the Virtual Hard Disk."
  1847.     PRINT "If the path does not exist, we will try to create it. Do not include a file name."
  1848.     PRINT
  1849.     INPUT "Please enter path: "; VHDXPath$
  1850. LOOP WHILE VHDXPath$ = ""
  1851.  
  1852. ' Remove quotes and trailing backslash.
  1853.  
  1854. CleanPath VHDXPath$
  1855. VHDXPath$ = Temp$
  1856.  
  1857. ' Verify that the path specified exists.
  1858.  
  1859. IF NOT (_DIREXISTS(VHDXPath$)) THEN
  1860.  
  1861.     ' The destination path does not exist. We will now attempt to create it.
  1862.  
  1863.     ClearScreen
  1864.     PRINT "Destination folder does not exist. Attempting to create it..."
  1865.     Cmd$ = "md " + CHR$(34) + VHDXPath$ + CHR$(34)
  1866.     SHELL _HIDE Cmd$
  1867.  
  1868.     ' Checking for existance of folder again again to see if we were able to create it.
  1869.  
  1870.     IF NOT (_DIREXISTS(VHDXPath$)) THEN
  1871.         PRINT
  1872.         PRINT "We were not able to create the destination folder."
  1873.         PRINT "Please recheck the path you have specified and try again."
  1874.         PRINT
  1875.         SHELL "pause"
  1876.         GOTO CreateVHDX
  1877.     END IF
  1878.  
  1879. ' If we have arrived here it means that the destination path already exists
  1880. ' or we were able to create it successfully.
  1881.  
  1882. ' Get a name for the Virtual Hard Disk file.
  1883.  
  1884. VHDXFileName$ = "" ' Set initial value
  1885. VHDXSize = 0 ' Set initial value
  1886.  
  1887.     ClearScreen
  1888.     PRINT "Please provide a name for the Virtual Hard disk file. Do not include a file extension."
  1889.     PRINT
  1890.     INPUT "Enter file name: "; VHDXFileName$
  1891. LOOP WHILE VHDXFileName$ = ""
  1892.  
  1893.     ClearScreen
  1894.     INPUT "Enter size of Virtual Disk in MB (NOT in GB!): "; VHDXSize
  1895. LOOP WHILE VHDXSize = 0
  1896.  
  1897. ' We need to strip the leading space from the size, so we are going to convert the size to a string.
  1898.  
  1899. VHDXSizeString$ = STR$(VHDXSize)
  1900. VHDXSizeString$ = RIGHT$(VHDXSizeString$, (LEN(VHDXSizeString$) - 1))
  1901.  
  1902. GetVHDXLetter:
  1903.  
  1904. ClearScreen
  1905. INPUT "What drive letter do you want to assign to the Virtual Disk. Enter only the letter, no colon (:): "; VHDXLetter$
  1906. VHDXLetter$ = UCASE$(VHDXLetter$)
  1907.  
  1908. IF (LEN(VHDXLetter$) > 1) OR (VHDXLetter$) = "" OR ((ASC(VHDXLetter$)) < 65) OR ((ASC(VHDXLetter$)) > 90) THEN
  1909.     PRINT
  1910.     PRINT "That was not a valid entry. Please try again."
  1911.     PRINT
  1912.     GOTO GetVHDXLetter
  1913.  
  1914. IF _DIREXISTS(VHDXLetter$ + ":") THEN
  1915.     PRINT
  1916.     PRINT "That drive letter is already in use."
  1917.     PRINT
  1918.     SHELL "pause"
  1919.     GOTO GetVHDXLetter
  1920.  
  1921. ' Create the Virtual Disk
  1922.  
  1923. ClearScreen
  1924. PRINT "We are now creating the disk. This could possibly take a little while."
  1925. OPEN "TEMP.BAT" FOR OUTPUT AS #1
  1926. PRINT #1, "@echo off"
  1927. PRINT #1, "(echo create vdisk file="; CHR$(34); VHDXPath$; "\"; VHDXFileName$; ".VHDX"; CHR$(34); " type=expandable maximum="; VHDXSizeString$
  1928. PRINT #1, "echo select vdisk file="; CHR$(34); VHDXPath$; "\"; VHDXFileName$; ".VHDX"; CHR$(34)
  1929. PRINT #1, "echo attach vdisk"
  1930. PRINT #1, "echo create partition primary"
  1931. PRINT #1, "echo format fs=ntfs quick"
  1932. PRINT #1, "echo assign letter="; VHDXLetter$
  1933. PRINT #1, "echo exit"
  1934. PRINT #1, ") | diskpart > NUL"
  1935. SHELL "TEMP.BAT"
  1936. IF _FILEEXISTS("TEMP.BAT") THEN KILL "TEMP.BAT"
  1937. ClearScreen
  1938. PRINT "Virtual Hard Disk has been created."
  1939. PRINT "Please note that this disk was created as an expandable disk so the initial size of the file may"
  1940. PRINT "appear much smaller than the size that you specified."
  1941. PRINT "If you created this disk on removable media, be sure to eject the Virtual Disk before you eject the"
  1942. PRINT "removable media."
  1943. SHELL "pause"
  1944. GOTO MainMenu
  1945.  
  1946.  
  1947. ' *********************************************************************************
  1948. ' * Menu Item #8: Create a generic ISO image and inject files and folders into it *
  1949. ' *********************************************************************************
  1950.  
  1951. CreateISOImage:
  1952.  
  1953. ' Initialize variables
  1954.  
  1955. SourcePath$ = ""
  1956. VolumeName$ = ""
  1957.  
  1958.     ClearScreen
  1959.     PRINT "This will create an ISO image from all files and subfolders within the folder that you specify."
  1960.     PRINT
  1961.  
  1962.     ' Get the location to the files / folders that should be injected into an ISO file.
  1963.  
  1964.     INPUT "Enter the path containing the data to place into an ISO image: "; SourcePath$
  1965. LOOP WHILE SourcePath$ = ""
  1966.  
  1967. ' Verify that the path specified exists.
  1968.  
  1969. IF NOT (_DIREXISTS(SourcePath$)) THEN
  1970.     ClearScreen
  1971.     PRINT "The location that you specified does not exist or is not valid."
  1972.     PRINT
  1973.     SHELL "pause"
  1974.     GOTO CreateISOImage
  1975.  
  1976. GetDestination:
  1977.  
  1978. DestinationPath$ = "" ' Set initial value
  1979.  
  1980.     ClearScreen
  1981.     INPUT "Enter the destination path. This is the path only without a file name: "; DestinationPath$
  1982. LOOP WHILE DestinationPath = ""
  1983.  
  1984. CleanPath DestinationPath$
  1985. DestinationPath$ = Temp$ + "\"
  1986.  
  1987. ' Verify that the path specified exists.
  1988.  
  1989. IF NOT (_DIREXISTS(DestinationPath$)) THEN
  1990.  
  1991.     ' The destination path does not exist. We will now attempt to create it.
  1992.  
  1993.     ClearScreen
  1994.  
  1995.     PRINT "Destination path does not exist. Attempting to create it..."
  1996.     Cmd$ = "md " + CHR$(34) + DestinationPath$ + CHR$(34)
  1997.     SHELL _HIDE Cmd$
  1998.  
  1999.     ' Checking for existance of folder again again to see if we were able to create it.
  2000.  
  2001.     IF NOT (_DIREXISTS(DestinationPath$)) THEN
  2002.         PRINT
  2003.         PRINT "We were not able to create the destination folder."
  2004.         PRINT "Please recheck the path you have specified and try again."
  2005.         PRINT
  2006.         SHELL "pause"
  2007.         GOTO GetDestination
  2008.     END IF
  2009.  
  2010. ' If we have arrived here it means that the destination path already exists
  2011. ' or we were able to create it successfully.
  2012.  
  2013. DestinationFileName$ = "" ' Set initial value
  2014.  
  2015. ' Get the name of the ISO image that we are creating.
  2016.  
  2017.     ClearScreen
  2018.     INPUT "Enter the name of the file to create, without an extension: "; DestinationFileName$
  2019. LOOP WHILE DestinationFileName$ = ""
  2020.  
  2021. DestinationPathAndFile$ = DestinationPath$ + DestinationFileName$ + ".iso"
  2022.  
  2023. ' Get the volume name for the ISO image
  2024.  
  2025. GetVolumeName2:
  2026.  
  2027. ClearScreen
  2028. INPUT "Enter the volume name to give the ISO image or press Enter for none: "; VolumeName$
  2029.  
  2030. IF LEN(VolumeName$) > 32 THEN
  2031.     PRINT
  2032.     PRINT "That volume name is invalid! The volume name is limited to 32 characters."
  2033.     PRINT
  2034.     SHELL "pause"
  2035.     GOTO GetVolumeName2
  2036.  
  2037. ' Build the command that needs to be run to create the ISO image.
  2038.  
  2039. 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)
  2040.  
  2041. ' Create the ISO image
  2042.  
  2043. ClearScreen
  2044. PRINT "Creating the image. Please standby..."
  2045. SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  2046. PRINT "ISO Image created."
  2047. SHELL "Pause"
  2048. GOTO MainMenu
  2049.  
  2050.  
  2051. ' **************************************************************
  2052. ' * Menu Item #9: Strip the time and date stamp from filenames *
  2053. ' **************************************************************
  2054.  
  2055. StripTimeAndDateStamp:
  2056.  
  2057. ' Initialize variables
  2058.  
  2059. Location$ = ""
  2060. UpdateAll$ = ""
  2061.  
  2062.     ClearScreen
  2063.     PRINT "When you use either the routine to inject Windows updates or drivers into Windows ISO images,"
  2064.     PRINT "a time and date stamp is added to the file name. If you want to strip off this time and date"
  2065.     PRINT "stamp, this routine will do that for you."
  2066.     PRINT
  2067.     INPUT "Enter the full path the files from which to remove the time and date stamp: "; Location$
  2068. LOOP WHILE Location$ = ""
  2069.  
  2070. ' Clean up the path to strip off any quoite marks or trailing backslash from the path.
  2071.  
  2072. CleanPath Location$
  2073. Location$ = Temp$
  2074.  
  2075. IF NOT (_DIREXISTS(Location$)) THEN
  2076.  
  2077.     ' The path does not exist. Inform user and allow them to try again.
  2078.  
  2079.     ClearScreen
  2080.     PRINT "That folder does not exist. Please try again."
  2081.     PRINT
  2082.     SHELL "pause"
  2083.     GOTO StripTimeAndDateStamp
  2084.  
  2085. ' If we have arrived here it means that the updates path is valid.
  2086. ' Now, verify that files actually exist in this location.
  2087.  
  2088. StripAllFiles:
  2089.  
  2090.     ClearScreen
  2091.     INPUT "Do you want to remove the time and date stamp from all files in this location"; UpdateAll$
  2092. LOOP WHILE UpdateAll$ = ""
  2093.  
  2094. YesOrNo UpdateAll$
  2095. UpdateAll$ = YN$
  2096. IF UpdateAll$ = "X" THEN
  2097.     PRINT "Please enter a valid response."
  2098.     PRINT
  2099.     GOTO StripAllFiles
  2100.  
  2101. Cmd$ = "DIR /B /A:-D " + CHR$(34) + Location$ + "\*.*" + CHR$(34) + " > TEMP.TXT"
  2102.  
  2103. IF _FILEEXISTS("TEMP.TXT") THEN
  2104.     OPEN "TEMP.TXT" FOR INPUT AS #1
  2105.     DO UNTIL EOF(1)
  2106.         OpenParenPosition = 0 ' Set initial value
  2107.         ClosedParenPosition = 0 ' Set initial value
  2108.         LINE INPUT #1, file$
  2109.         OpenParenPosition = _INSTRREV(file$, "(")
  2110.         ClosedParenPosition = _INSTRREV(file$, ")")
  2111.         IF ((OpenParenPosition > 1) AND (ClosedParenPosition > OpenParenPosition)) THEN
  2112.             NewFile$ = RTRIM$(LEFT$(file$, (OpenParenPosition - 1)))
  2113.             IF (LEN(file$) > ClosedParenPosition) THEN
  2114.                 NewFile$ = NewFile$ + RIGHT$(file$, (LEN(file$) - ClosedParenPosition))
  2115.             END IF
  2116.             IF UpdateAll$ = "Y" THEN
  2117.                 Cmd$ = "rename " + CHR$(34) + Location$ + "\" + file$ + CHR$(34) + " " + CHR$(34) + NewFile$ + CHR$(34)
  2118.                 SHELL _HIDE Cmd$
  2119.             ELSE
  2120.  
  2121.                 AskAboutTSRemoval:
  2122.  
  2123.                 DO
  2124.                     ClearScreen
  2125.                     PRINT file$
  2126.                     PRINT
  2127.                     INPUT "Do you want to remove the time and date stamp from the above file"; StripThisFile$
  2128.                 LOOP WHILE StripThisFile$ = ""
  2129.                 YesOrNo StripThisFile$
  2130.                 StripThisFile$ = YN$
  2131.                 IF StripThisFile$ = "X" THEN GOTO AskAboutTSRemoval
  2132.                 IF StripThisFile$ = "Y" THEN
  2133.                     Cmd$ = "rename " + CHR$(34) + Location$ + "\" + file$ + CHR$(34) + " " + CHR$(34) + NewFile$ + CHR$(34)
  2134.                     SHELL _HIDE Cmd$
  2135.                 END IF
  2136.             END IF
  2137.         END IF
  2138.     LOOP
  2139.     CLOSE #1
  2140. IF _FILEEXISTS("TEMP.TXT") THEN KILL "TEMP.TXT"
  2141.  
  2142. ClearScreen
  2143. PRINT "Done!"
  2144. SHELL "pause"
  2145. GOTO MainMenu
  2146.  
  2147.  
  2148. ' ********************************************
  2149. ' * Menu Item #10: Cleanup files and folders *
  2150. ' ********************************************
  2151.  
  2152. GetFolderToClean:
  2153.  
  2154. DestinationPath$ = "" ' Set initial value
  2155.  
  2156.     ClearScreen
  2157.     PRINT "If the routine to inject updates or drivers into an ISO image is aborted prematurely,"
  2158.     PRINT "Then it is possible that you may not be able to delete files or folders in the"
  2159.     PRINT "destination. Use this routine to try fixing that issue."
  2160.     PRINT
  2161.     INPUT "Please enter the full path to the folder to be cleaned: "; DestinationPath$
  2162. LOOP WHILE DestinationPath$ = ""
  2163.  
  2164. CleanPath DestinationPath$
  2165. DestinationPath$ = Temp$ + "\"
  2166. Cleanup DestinationPath$
  2167.  
  2168. IF CleanupSuccess = 1 THEN
  2169.     ClearScreen
  2170.     PRINT "The contents of the folder "; CHR$(34); DestinationPath$; CHR$(34); " were successfully cleaned."
  2171.     PRINT
  2172.     SHELL "pause"
  2173.     GOTO MainMenu
  2174.  
  2175. GOTO MainMenu
  2176.  
  2177.  
  2178. ' ***************************************************************
  2179. ' * Menu Item #11: Display help and information for this program *
  2180. ' ***************************************************************
  2181.  
  2182. ShowHelp:
  2183.  
  2184. MenuSelection = 0 ' Set initial value
  2185.  
  2186.     ClearScreen
  2187.     PRINT
  2188.     PRINT " *************"
  2189.     PRINT " * Help Menu *"
  2190.     PRINT " *************"
  2191.     PRINT
  2192.     PRINT "Please choose the topic that you would like help with or press Enter to return to the main menu:"
  2193.     PRINT
  2194.     PRINT "    1) General Information"
  2195.     PRINT "    2) System Requirements"
  2196.     PRINT "    3) Inject Windows updates into one or more Windows ISO images"
  2197.     PRINT "    4) Inject drivers into one or more Windows images"
  2198.     PRINT "    5) Make a bootable thumb drive from a Windows ISO image"
  2199.     PRINT "    6) Create a bootable ISO image from Windows files in a folder"
  2200.     PRINT "    7) Export drivers from this system"
  2201.     PRINT "    8) Expand drivers supplied in a .CAB file"
  2202.     PRINT "    9) Create a Virtual Disk (VHDX)"
  2203.     PRINT "   10) Create a generic ISO image and inject files and folders into it"
  2204.     PRINT "   11) Strip the time and date stamp from filenames"
  2205.     PRINT "   12) Cleanup files and folders"
  2206.     PRINT "   13) Display help and information for this program"
  2207.     PRINT "   14) Exit"
  2208.     PRINT
  2209.     INPUT "    Please make a selection by number or press Enter alone to exit from help: "; MenuSelection
  2210. LOOP WHILE ((MenuSelection < 0) OR (MenuSelection > 14))
  2211.  
  2212.  
  2213. SELECT CASE MenuSelection
  2214.     CASE 0
  2215.         GOTO MainMenu
  2216.     CASE 1
  2217.         GOTO Help1
  2218.     CASE 2
  2219.         GOTO Help2
  2220.     CASE 3
  2221.         GOTO Help3
  2222.     CASE 4
  2223.         GOTO Help4
  2224.     CASE 5
  2225.         GOTO Help5
  2226.     CASE 6
  2227.         GOTO Help6
  2228.     CASE 7
  2229.         GOTO Help7
  2230.     CASE 8
  2231.         GOTO Help8
  2232.     CASE 9
  2233.         GOTO Help9
  2234.     CASE 10
  2235.         GOTO Help10
  2236.     CASE 11
  2237.         GOTO Help11
  2238.     CASE 12
  2239.         GOTO Help12
  2240.     CASE 13
  2241.         GOTO Help13
  2242.     CASE 14
  2243.         GOTO Help14
  2244.  
  2245.  
  2246. Help1: ' General Information
  2247.  
  2248. ClearScreen
  2249. PRINT "The main purpose of this program is to aid in injecting Windows updates and / or drivers into your Windows"
  2250. PRINT "ISO images."
  2251. PRINT "The process of injecting updates or drivers is long and tedious, involves a lot of long commands, and"
  2252. PRINT "is very error prone. This program will automate all those tasks for you. In addition, if you want to"
  2253. PRINT "inject updates into multiple ISO images, this program will do that for you. Simply supply a few pieces"
  2254. PRINT "of information and the program will do the rest."
  2255. PRINT "There also several helpful utilities available. Please see the help topics for each individual option for"
  2256. PRINT "details."
  2257. PRINT "When you are asked for a path, it's okay to enter the path either with or without quotes even when there"
  2258. PRINT "are spaces in the pathname of filename."
  2259. PRINT "TIP: You can copy and paste long path names into the program."
  2260. PRINT "Hard Disk vs. Removable Media:"
  2261. PRINT "When creating a bootable thumb drive, you must use a thumb drive that reports as a removable disk. The"
  2262. PRINT "program will tell you if the disk you select is not a removable disk."
  2263. PRINT "For the process of injecting Windows updates, one of the Microsoft utilities used (DISM) requires the use"
  2264. PRINT "of a fixed disk, not a removable disk. Make sure that you use a fixed disk to store your project. Note that"
  2265. PRINT "you can work around this by creating a Virtual Hard Disk on a removable disk and then using that Virtual"
  2266. PRINT "Hard Disk to store your project. There is an option on the main menu to help you create a Virtual Hard Disk."
  2267. SHELL "pause"
  2268. GOTO ShowHelp
  2269.  
  2270. Help2: ' System requirements
  2271.  
  2272. ClearScreen
  2273. PRINT "This program is a 64-bit program designed to work only on 64-bit systems, not 32-bit or ARM based systems."
  2274. PRINT "It is also designed to work with Windows ISO images that contain an INSTALL.WIM file in the \sources"
  2275. PRINT "folder, not an INSTALL.ESD. Finally, only 64-bit Windows ISO images should be used. If you have a need for"
  2276. PRINT "version that will work with 32-bit versions of Windows, let me know and I may possibly be convinced to"
  2277. PRINT "create a 32-bit version if there is enough interest."
  2278. PRINT "This program requires the Windows ADK to be installed. Only the"; CHR$(34); "Deployment Tools"; CHR$(34); " component needs to be"
  2279. PRINT "installed."
  2280. SHELL "pause"
  2281. GOTO ShowHelp
  2282.  
  2283. Help3: ' Inject Windows updates into one or more Windows ISO images
  2284.  
  2285. ClearScreen
  2286. PRINT "This routine will inject Windows updates into your ISO images."
  2287. PRINT "Place all your images to be updated in one folder and the program will apply the updates to all of the"
  2288. PRINT "images in that folder. Make sure that there are no other files with a .ISO extension in this folder. Any"
  2289. PRINT "other files are okay since they will simply be ignored."
  2290. PRINT "NOTE: The original images will remain unmodified so it is ok to save to the same folder with the source files."
  2291. PRINT "IMPORTANT: You want to make sure that updates are applied in a certain order. To ensure that this happens,"
  2292. PRINT "put a numerical tag in front of the filename to indicate the load order. Here are some examples (the numbers"
  2293. PRINT "are part of the filename):"
  2294. PRINT "1 - KB000001 - Latest Servicing Stack Update.msu"
  2295. PRINT "2 - KB000002 - Apr 2019 Cumulative Update.msu"
  2296. PRINT "3 - Any file name you want.msu"
  2297. PRINT "Updates should be applied in the following order:"
  2298. PRINT "1) Servicing stack updates"
  2299. PRINT "2) Windows cumulative updates (Note that .NET updates should be considered "; CHR$(34); "other updates"; CHR$(34); ")"
  2300. PRINT "3) Other updates"
  2301. PRINT "IMPORTANT: This process will NOT apply driver updates. There is a seperate option from the main menu that"
  2302. PRINT "will allow you to inject drivers."
  2303. SHELL "pause"
  2304. ClearScreen
  2305. PRINT "      *************************"
  2306. PRINT "      * Obtaining the Updates *"
  2307. PRINT "      *************************"
  2308. PRINT "         *****************************************"
  2309. PRINT "         * Latest SSUs (Servicing Stack Updates) *"
  2310. PRINT "         *****************************************"
  2311. PRINT "Visit this web site to see a list of available SSUs and to download them:"
  2312. PRINT "https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/ADV990001"
  2313. PRINT "         ****************************"
  2314. PRINT "         * Microsoft Update Catalog *"
  2315. PRINT "         ****************************"
  2316. PRINT "Download updates for Windows here:"
  2317. PRINT "http://www.catalog.update.microsoft.com/home.aspx"
  2318. PRINT "On the Windows Update catalog web site I like to do a search similar to this: Windows 1809 x64 and then sort by"
  2319. PRINT "release date to narrow down the search to the updates for my version of Windows."
  2320. SHELL "pause"
  2321. ClearScreen
  2322. PRINT "         **************************"
  2323. PRINT "         * Windows Update History *"
  2324. PRINT "         **************************"
  2325. PRINT "View Windows update history here:"
  2326. PRINT "https://support.microsoft.com/en-us/help/4464619"
  2327. PRINT "TIP: I like to maintain a copy of Windows in a VM that has no updates applied and has networking disabled."
  2328. PRINT "I keep a snapshot (known as a checkpoint in Hyper-V) of this state so I can easily return to it. After Microsoft"
  2329. PRINT " releases new updates I enable networking to allow the updates to be installed. Then, you can look at the update"
  2330. PRINT "history to easily verify what updates get installed. I then know exactly what I need to download."
  2331. SHELL "pause"
  2332. GOTO ShowHelp
  2333.  
  2334. Help4: ' Inject drivers into one or more Windows images
  2335.  
  2336. ClearScreen
  2337. PRINT "This option will take take all the drivers located in a folder and inject them into one or more Windows ISO"
  2338. PRINT "images. All subfolders of the location that you specify will be recursed for drivers."
  2339. PRINT "TIPS: You can add drivers from multiple systems all at once. Simply export the drivers from multiple systems"
  2340. PRINT "using menu item to export drivers from a system, then place them all under one main folder like this:"
  2341. PRINT "Drivers"
  2342. PRINT "   Drivers from desktop"
  2343. PRINT "   Drivers from laptop"
  2344. PRINT "   Drivers from media system"
  2345. PRINT "Next, use this menu item to inject the drivers. Simply point to the "; CHR$(34); "Drivers"; CHR$(34); " folder and the program will"
  2346. PRINT "recurse all subfolders for drivers and inject them into the image."
  2347. PRINT "IMPORTANT: The drivers to be injected must be expanded and have their .INF files accessible. If you have drivers"
  2348. PRINT "in a .CAB or .EXE file, this routine will not be able to inject those drivers."
  2349. SHELL "pause"
  2350. GOTO ShowHelp
  2351.  
  2352. Help5: ' Make a bootable thumb drive from a Windows ISO image
  2353.  
  2354. ClearScreen
  2355. PRINT "Use this option to make a bootable thumb drive from a Windows ISO image. The resulting thumb drive will be"
  2356. PRINT "bootable from both BIOS and UEFI based systems."
  2357. PRINT "Important: The thumb drive that you use must report itself as a removavle drive. If the drive reports itself"
  2358. PRINT "as a fixed disk it will not work. The program will automatically notify you if the drive is not removable."
  2359. PRINT "You will have the option to create up to 2 additional partitions on the thumb drive to hold other data. The"
  2360. PRINT "program can even encrypt these partitions with Bitlocker and optionally configure it to autounlock on your system."
  2361. PRINT "As a result, you can have a Windows 10 bootable thumb drive that also holds other data which can be optionally"
  2362. PRINT "Bitlocker protected."
  2363. SHELL "pause"
  2364. GOTO ShowHelp
  2365.  
  2366. Help6: ' Create a bootable ISO image from Windows files in a folder
  2367.  
  2368. ClearScreen
  2369. PRINT "If you have Windows files on a DVD, hard disk, SSD, thumb drive, etc. you can create a bootable ISO image from"
  2370. PRINT "those files using this option."
  2371. SHELL "pause"
  2372. GOTO ShowHelp
  2373.  
  2374. Help7: ' Export drivers from this system
  2375.  
  2376. ClearScreen
  2377. PRINT "This option takes all the active drivers from your current Windows installation and exports them to a folder."
  2378. PRINT "You can then use the menu option to inject these drivers into a Windows image."
  2379. SHELL "pause"
  2380. GOTO ShowHelp
  2381.  
  2382. Help8: ' Expand drivers supplied in a .CAB file
  2383.  
  2384. ClearScreen
  2385. PRINT "The menu option to inject drivers can only inject drivers where the .INF file is accessible. If you have drivers in"
  2386. PRINT "a .CAB file, you can use this routine to expand the CAB files. You can place multiple files in one folder and this"
  2387. PRINT "routine will expand all of them."
  2388. PRINT "Please be aware that the Windows EXPAND utility may not be able to expand all CAB files, however, any drivers"
  2389. PRINT "in CAB files from the Microsoft Update Catalog should work with this utility. If a CAB file cannot be expanded,"
  2390. PRINT "then the CAB file will simply be copied to the destination as is."
  2391. SHELL "pause"
  2392. GOTO ShowHelp
  2393.  
  2394. Help9: ' Create a Virtual Disk (VHDX)
  2395.  
  2396. ClearScreen
  2397. PRINT "This option will create a Virtual Hard Disk and mount it to the drive letter of your choosing. This can be"
  2398. PRINT "especially useful for saving updated ISO images on removable media since that process requires a fixed disk."
  2399. PRINT "Placing a Virtual Hard Disk on a removable drive provides a workaround."
  2400. SHELL "pause"
  2401. GOTO ShowHelp
  2402.  
  2403. Help10: ' Create a generic ISO image and inject files and folders into it
  2404.  
  2405. ClearScreen
  2406. PRINT "This option will take all the files from a folder and place them into an ISO image. If you are working on a"
  2407. PRINT "project where you have a lot of folders, files, documents, etc. that you want to keep together, this may be"
  2408. PRINT "useful. Naturally, you could always create a ZIP or other archive file, but I like ISO images because I can"
  2409. PRINT "simply mount them as another drive letter natively within Windows. Creation of an ISO is also quick."
  2410. SHELL "pause"
  2411. GOTO ShowHelp
  2412.  
  2413. Help11: ' Strip the time and date stamp from filenames
  2414.  
  2415. ClearScreen
  2416. PRINT "When you use the routines to inject either updates or drivers into Windows ISO images, a time and date stamp"
  2417. PRINT "is added to the file names. If you want to strip off this time and date stamp from the files, run this routine"
  2418. PRINT "to automate the process. This is especially handy if you have a lot of Windows images all in the same folder."
  2419. SHELL "pause"
  2420. GOTO ShowHelp
  2421.  
  2422. Help12: ' Cleanup files and folders
  2423.  
  2424. ClearScreen
  2425. PRINT "If the routine to inject updates or drivers into an ISO image is aborted prematurely, it is possible that you"
  2426. PRINT "may not be able to delete files or folders in the destination. Use this routine to try fixing that issue. If"
  2427. PRINT "this routine cannot resolve the issue then it will inform you that you may need to reboot and then try the"
  2428. PRINT "routine again."
  2429. PRINT "This routine is useful because a reboot alone may not resolve the issue. It may still be necessary to unmount"
  2430. PRINT "any image previously mounted by DISM and this routine will do that."
  2431. PRINT "IMPORTANT: Please note that if the program is terminated while an ISO image is mounted, you will need to unmount"
  2432. PRINT "it manually. You can simply right-click the drive representing the image in File Explorer and choose "; CHR$(34); "Eject"; CHR$(34); "."
  2433. SHELL "pause"
  2434. GOTO ShowHelp
  2435.  
  2436. Help13: ' Display help and information for this program
  2437.  
  2438. ClearScreen
  2439. PRINT "This will display the main help menu from which you selected this option."
  2440. SHELL "pause"
  2441. GOTO ShowHelp
  2442.  
  2443. Help14: ' Exit
  2444.  
  2445. ClearScreen
  2446. PRINT "This will end the program."
  2447. SHELL "pause"
  2448. GOTO ShowHelp
  2449.  
  2450.  
  2451. ' ***********************
  2452. ' * Menu Item #12: Exit *
  2453. ' ***********************
  2454.  
  2455. ProgramEnd:
  2456.  
  2457.  
  2458.  
  2459. ' *******************************************************************************************
  2460. ' * Sub PROCEDURES go here. These are subroutines that are still a part of the main program *
  2461. ' * and that are not in SUB / END SUB blocks.                                               *
  2462. ' *******************************************************************************************
  2463.  
  2464.  
  2465. DisplayIndices:
  2466.  
  2467. MountISO FileArray$(IndexLoop)
  2468. Cmd$ = "dism /Get-WimInfo /WimFile:" + MountedImageDriveLetter$ + "\Sources\Install.wim > WimInfo.txt"
  2469. SHELL Cmd$
  2470. Cmd$ = "powershell.exe -command " + CHR$(34) + "DiskMount-DiskImage " + CHR$(34) + "'" + FileArray$(IndexLoop) + "'" + CHR$(34) + CHR$(34) + " > NUL"
  2471. SHELL Cmd$
  2472.  
  2473. ' Display wiminfo.txt which lists all of the indicies now. Then, delete wiminfo.txt
  2474.  
  2475. ClearScreen
  2476.  
  2477. ' PRINT "Following is a list of Windows Editions and the index number associated with each edition."
  2478. ' PRINT "Note the index number for the edition that you want to update."
  2479.  
  2480. PRINT "We are about to display the list of Windows editions and the associated index numbers for this file:"
  2481. PRINT SourceFileNameOnly$(IndexLoop)
  2482. PRINT "If the listing exceeds one page in length, press Enter to advance one line at a time or press the"
  2483. PRINT "spacebar to advance a full page."
  2484. SHELL "pause"
  2485. ClearScreen
  2486. SHELL "type wiminfo.txt | more"
  2487. SHELL "pause"
  2488. KILL "wiminfo.txt"
  2489.  
  2490.  
  2491.  
  2492. ' ********************************************************************************
  2493. ' * Below are subroutines that need to appear after the end of the program.      *
  2494. ' * Subroutines called by name reference and placed in SUB / END SUB blocks must *
  2495. ' * be placed after the end of the main program.                                 *
  2496. ' ********************************************************************************
  2497.  
  2498.  
  2499. SUB CleanPath (Path$)
  2500.  
  2501.     ' Remove any quotes or trailing backslash from a path
  2502.     ' We the inner loop will strip the quotes off a path and a backslash if the path ends
  2503.     ' with a backslash. However, if the trailing backslash is within the quotes this won't
  2504.     ' get stripped. As a result, we'll run that inner loop a second time because now the
  2505.     ' trailing backslash is at the end of the path so it will get stripped this time.
  2506.  
  2507.     ' Note that we may want a trailing backslash in various places in the program, but to
  2508.     ' prevent getting a double backslash we are stripping off the trailing backslash here
  2509.     ' so that we always consistently have a path without the trailing backslash to start.
  2510.  
  2511.     ' To use this subroutine: Pass the path this sub, the sub will return the path
  2512.     ' without quotes or a trailing backslash in Temp$.
  2513.  
  2514.     FOR x = 1 TO 2
  2515.         Temp$ = ""
  2516.         FOR Y = 1 TO LEN(Path$)
  2517.             Character$ = MID$(Path$, Y, 1)
  2518.             IF Character$ <> CHR$(34) THEN
  2519.                 IF NOT (Character$ = "\" AND Y = LEN(Path$)) THEN
  2520.                     Temp$ = Temp$ + Character$
  2521.                 END IF
  2522.             END IF
  2523.         NEXT Y
  2524.     NEXT x
  2525.  
  2526.  
  2527. ' END OF CleanPath SUBROUTINE
  2528.  
  2529.  
  2530. SUB FileTypeSearch (Path$, FileType$)
  2531.  
  2532.     ' This routine will receive a path and a file name extension. It will search the path specified
  2533.     ' for any occurances of files with the specified extension. It will return the number of those files
  2534.     ' found in NumberOfFiles and each of those file names in an array called TempArray$().
  2535.  
  2536.     ' The path passed to this subroutine should end with a trailing backslash. Example: D:\MyFolder\
  2537.  
  2538.     ' Initialize the variables
  2539.  
  2540.     NumberOfFiles = 0 ' Set initial value
  2541.     FileType$ = UCASE$(FileType$)
  2542.  
  2543.     ' Build the command to be run
  2544.  
  2545.     Cmd$ = "DIR /B " + CHR$(34) + Path$ + "*" + FileType$ + CHR$(34) + " > TEMP.TXT"
  2546.     SHELL _HIDE Cmd$
  2547.  
  2548.     IF _FILEEXISTS("TEMP.TXT") THEN
  2549.         OPEN "TEMP.TXT" FOR INPUT AS #1
  2550.         DO UNTIL EOF(1)
  2551.             LINE INPUT #1, file$
  2552.             IF UCASE$(RIGHT$(file$, 4)) = UCASE$(FileType$) THEN
  2553.                 NumberOfFiles = NumberOfFiles + 1
  2554.                 TempArray$(NumberOfFiles) = Path$ + file$
  2555.             END IF
  2556.         LOOP
  2557.         CLOSE #1
  2558.         KILL "TEMP.TXT"
  2559.     END IF
  2560.  
  2561.  
  2562.  
  2563. SUB MountISO (ImagePath$)
  2564.  
  2565.     ' This routine will mount the ISO image at the path passed from the main program and
  2566.     ' will get the CDROM ID (Ex. \\.\CDROM0) and save in MountedImageCDROMID$. It will also
  2567.     ' get the drive letter from and save in MountedImageDriveLetter$.
  2568.  
  2569.     CleanPath (ImagePath$)
  2570.     ImagePath$ = Temp$
  2571.     cmd$ = "powershell.exe -command " + CHR$(34) + "Mount-DiskImage " + CHR$(34) + "'" + ImagePath$ + "'" + CHR$(34) + CHR$(34) + " > MountInfo1.txt"
  2572.     SHELL cmd$
  2573.     cmd$ = "powershell.exe -command " + CHR$(34) + "Get-DiskImage -ImagePath '" + ImagePath$ + "' | Get-Volume" + CHR$(34) + " > MountInfo2.txt"
  2574.     SHELL cmd$
  2575.     OPEN "MountInfo1.txt" FOR INPUT AS #1
  2576.     OPEN "MountInfo2.txt" FOR INPUT AS #2
  2577.  
  2578.     DO UNTIL EOF(1)
  2579.         LINE INPUT #1, GetLine$
  2580.         IF INSTR(1, GetLine$, "\\.\CDROM") THEN
  2581.             MountedImageCDROMID$ = RIGHT$(GetLine$, LEN(GetLine$) - ((INSTR(1, GetLine$, "\\.\CDROM"))) + 1)
  2582.             EXIT DO
  2583.         END IF
  2584.     LOOP
  2585.  
  2586.     CLOSE #1
  2587.  
  2588.     FOR count = 1 TO 4
  2589.         LINE INPUT #2, GetLine$
  2590.     NEXT count
  2591.  
  2592.     MountedImageDriveLetter$ = LEFT$(GetLine$, 1) + ":"
  2593.     CLOSE #2
  2594.     KILL "MountInfo1.txt"
  2595.     KILL "MountInfo2.txt"
  2596.  
  2597.  
  2598.  
  2599. SUB ClearScreen
  2600.  
  2601.     ' When a QB64 program is run with display going to the Windows console rather than the Program Console,
  2602.     ' the CLS command will not work. This will issue a CLS from the Windows console.
  2603.  
  2604.     SHELL CHR$(34) + "cls" + CHR$(34)
  2605.  
  2606.  
  2607.  
  2608. SUB Cleanup (CleanupPath$)
  2609.  
  2610.     DIM AutoCleanup AS INTEGER
  2611.  
  2612.     ' Pass the name of the folder to cleanup to this routine. It will return
  2613.     ' CleanupSuccess=0 if it failed, CleanupSuccess=1 if successful, CleanupSuccess=2 if
  2614.     ' the specified folder does not exist. NOTE: Currently, nothing is checking for a
  2615.     ' return of 2. This routine itself simply displays a message if the folder does not exist.
  2616.  
  2617.     ' When injecting either Windows updates or drivers into an ISO image, if
  2618.     ' the process is aborted without being allowed to finish, files may be
  2619.     ' left behind that you cannot delete manually. This routine will try to
  2620.     ' correct that situation. Note that this routine will only try to delete folders named
  2621.     ' "Mount", "ISO_Files", and "Scratch". This assures that we maintain any log files in
  2622.     ' the "LOGS" folder.
  2623.  
  2624.     ' If the files still exist after the intial cleanup attempt,  then we will attempt
  2625.     ' a fix by closing any open DISM session. If that still fails, we will inform the
  2626.     ' user that a reboot may be needed.
  2627.  
  2628.     ' After an automatic cleanup attempt, we will set the variable AutoCleanup to 1 so that we know that a
  2629.     ' cleanup has already been attempted. That way, if we fail again we know that we need to abort and
  2630.     ' warn the user.
  2631.  
  2632.     ' Initialize AutoCleanup to 0 before the start of the cleanup process.
  2633.  
  2634.     ' First, let's check to see if the specified folder even exists. If not, then there
  2635.     ' is nothing to cleanup but we still consider that successful because there are no
  2636.     ' unwanted files present.
  2637.  
  2638.     IF NOT (_DIREXISTS(CleanupPath$)) THEN
  2639.         ClearScreen
  2640.         PRINT "There is nothing to cleanup because that folder does not exist!"
  2641.         PRINT
  2642.         SHELL "pause"
  2643.         CleanupSuccess = 2
  2644.         GOTO NoSuchFolder
  2645.     END IF
  2646.  
  2647.     AutoCleanup = 0 ' Set initial value
  2648.  
  2649.     ClearScreen
  2650.     PRINT "Attempting cleanup of the folder: "; CHR$(34); CleanupPath$; CHR$(34)
  2651.     PRINT
  2652.     PRINT "Please standby. This may take a little while..."
  2653.  
  2654.     StartCleanup:
  2655.  
  2656.     TempPath$ = CleanupPath$ + "Mount\"
  2657.     IF _DIREXISTS(TempPath$) THEN
  2658.         SHELL _HIDE "rmdir /s /q " + CHR$(34) + TempPath$ + CHR$(34)
  2659.     END IF
  2660.  
  2661.     IF _DIREXISTS(TempPath$) THEN GOTO FoldersNotDeleted
  2662.     TempPath$ = CleanupPath$ + "ISO_Files\"
  2663.  
  2664.     IF _DIREXISTS(TempPath$) THEN
  2665.         SHELL _HIDE "rmdir /s /q " + CHR$(34) + TempPath$ + CHR$(34)
  2666.     END IF
  2667.  
  2668.     IF _DIREXISTS(TempPath$) THEN GOTO FoldersNotDeleted
  2669.  
  2670.     TempPath$ = CleanupPath$ + "Scratch\"
  2671.  
  2672.     IF _DIREXISTS(TempPath$) THEN
  2673.         SHELL _HIDE "rmdir /s /q " + CHR$(34) + TempPath$ + CHR$(34)
  2674.     END IF
  2675.  
  2676.     IF _DIREXISTS(TempPath$) THEN GOTO FoldersNotDeleted
  2677.  
  2678.     ' If we reach this point, then the folders we tried to delete were successfully deleted.
  2679.  
  2680.     GOTO DeletionSuccessful
  2681.  
  2682.     ' The following code handles the situation if we were not able to delete the folders.
  2683.  
  2684.     FoldersNotDeleted:
  2685.  
  2686.     ' If we arrive here, it means that trying to delete the "ISO_Files", "Mount", or "Scratch"
  2687.     ' folders from the location specified by the user failed. We are now going to try other measures
  2688.     ' to correct this.
  2689.  
  2690.     IF AutoCleanup = 0 THEN
  2691.         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"
  2692.         SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  2693.         Cmd$ = CHR$(34) + "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM\dism.exe" + CHR$(34) + " /cleanup-wim"
  2694.         SHELL _HIDE CHR$(34) + Cmd$ + CHR$(34)
  2695.         AutoCleanup = 1
  2696.         GOTO StartCleanup
  2697.     END IF
  2698.  
  2699.     ' If we arrive here, it means that we failed to delete at least one folder, and we have attempted an
  2700.     ' automatic cleanup, but that also failed.
  2701.  
  2702.     ClearScreen
  2703.     PRINT "We were not able to delete one or more folders under the"
  2704.     PRINT CHR$(34); CleanupPath$; CHR$(34); " folder."
  2705.     PRINT
  2706.     PRINT "The program has already automatically attempted to correct the situation but was not able to"
  2707.     PRINT "do so. The most likely cause for this is that a previous run of this program may have been"
  2708.     PRINT "aborted while the Microsoft DISM utility still had files locked."
  2709.     PRINT
  2710.     PRINT "Please try rebooting the computer, and then try running this program again."
  2711.     PRINT
  2712.     SHELL "pause"
  2713.  
  2714.     CleanupSuccess = 0
  2715.     GOTO EndCleanup
  2716.  
  2717.     DeletionSuccessful:
  2718.  
  2719.     CleanupSuccess = 1
  2720.     GOTO EndCleanup
  2721.  
  2722.     NoSuchFolder:
  2723.  
  2724.     CleanupSuccess = 2
  2725.     GOTO EndCleanup
  2726.  
  2727.     EndCleanup:
  2728.  
  2729.  
  2730.  
  2731. SUB YesOrNo (YesNo$)
  2732.  
  2733.     ' This routine checks whether a user responded with a valid "yes" or "no" response. The routine will return a capital "Y" in YN$
  2734.     ' 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.
  2735.     ' 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.
  2736.  
  2737.     SELECT CASE UCASE$(YesNo$)
  2738.         CASE "Y", "YES"
  2739.             YN$ = "Y"
  2740.         CASE "N", "NO"
  2741.             YN$ = "N"
  2742.         CASE ELSE
  2743.             YN$ = "X"
  2744.     END SELECT
  2745.  
  2746.  



« Last Edit: May 02, 2019, 11:11:38 pm by hanness »

Offline hanness

  • Forum Regular
  • Posts: 210
    • View Profile
Re: A program to manage and update Windows 10 images
« Reply #1 on: May 03, 2019, 08:51:40 am »
I should have mentioned that for the program to work, the Microsoft ADK needs to be installed. Only the deployment tools option needs to be installed which is about 100MB in size.

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: A program to manage and update Windows 10 images
« Reply #2 on: May 03, 2019, 11:04:47 am »

Offline hanness

  • Forum Regular
  • Posts: 210
    • View Profile
Re: A program to manage and update Windows 10 images
« Reply #3 on: May 03, 2019, 07:18:12 pm »
LOL. Thanks, Luke.