bobomb Posted January 1, 2022 Share Posted January 1, 2022 (edited) I am looking for pointers. I have been trying to follow advice up to this point that I have received. This is currently headless, only showing progress bars. I will try to have a dual progress bar GUI with current progress on top and overall progress on bottom. Advice on that is welcome. But mostly I am just looking for other set(s) of eyes to point out better ways to do things. It currently works fine. You select a boot.wim, select a folder with drivers, and it uses the DISM /add-driver /forceunsigned /recurse to add all drivers in the folder + subfolders (with spaces) to the boot.wim permanently. It captures progress from DISM. A couple snippets from around the forum have been adapted for use in, or are used, in this. I know its basic but the advice helps me learn faster. expandcollapse popup#NoTrayIcon #RequireAdmin #include <Constants.au3> #include <FileConstants.au3> #include <MsgBoxConstants.au3> Cleanup(False) Main() Func Main() Local $h_wimfile = FileOpenDialog('Select the boot.wim file to permanently inject drivers into', @WorkingDir & '\', 'Windows Image File (*.WIM)', 1, 'boot.wim') If $h_wimfile Then FileChangeDir(@ScriptDir) If PathIsWritable($h_wimfile) Then Local $h_folder = FileSelectFolder('Select the folder containing the drivers (Subfolders w/spaces Allowed)', @WorkingDir) If $h_folder Then FileChangeDir(@ScriptDir) MountWim($h_wimfile) FileChangeDir($h_folder) AddDrivers($h_folder) UnMountWim() ExportWim($h_wimfile) MsgBox(1, 'Process Complete...', 'Done.') Cleanup() Else MsgBox(1, 'Process Aborted...', 'No driver folder selected!') Cleanup() EndIf Else MsgBox(1, 'Process Aborted...', 'You do not have write access!') Cleanup() EndIf Else MsgBox(1, "Process Aborted...", "No WIM selected!") Exit EndIf EndFunc ;==>Main Func MountWim($h_wimfile) DirCreate(@ScriptDir & '\TempWIM') DirCreate(@ScriptDir & '\MountDir') CopyWIM($h_wimfile, @ScriptDir & '\TempWIM') Local $s_DISMCommand = ' /Mount-Image /ImageFile:' & '"' & @ScriptDir & '\TempWIM\' & WIMFileOnly($h_wimfile) & '"' & ' /Index:1 /MountDir:' & '"' & @ScriptDir & '\MountDir' & '"' Local $s_ProgTitle = 'Mounting Image', $s_ProgAction = 'Mounting boot.wim - ' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') EndFunc ;==>MountWim Func AddDrivers($h_folder) RunWait(@ComSpec & ' /c DISM /Image:' & '"' & @ScriptDir & '\MountDir' & '"' & ' /Add-Driver /Driver:' & '"' & $h_folder & '"' & ' /ForceUnsigned /Recurse', @WorkingDir) EndFunc ;==>AddDrivers Func UnMountWim($s_commit = '/Commit') Local $s_DISMCommand = ' /Unmount-Image /MountDir:' & '"' & @ScriptDir & '\MountDir' & '" ' & $s_commit Local $s_ProgTitle = 'Saving Image', $s_ProgTitle2 = 'UnMounting Image', $s_ProgAction = 'Processing with ' & $s_commit & ' flag - ' Local $s_step = False DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_step, $s_ProgTitle2) ProcessWaitClose('DISM.EXE') EndFunc ;==>UnMountWim Func Cleanup($b_Exit = True) If FileExists(@ScriptDir & '\MountDir') Then DirRemove(@ScriptDir & '\MountDir', $DIR_REMOVE) If FileExists(@ScriptDir & '\TempWIM') Then DirRemove(@ScriptDir & '\TempWIM', $DIR_REMOVE) If $b_Exit Then Exit EndFunc ;==>Cleanup Func WIMFileOnly($h_wimfile) Local Static $h_wimfilename = StringMid($h_wimfile, StringInStr($h_wimfile, "\", 2, -1) + 1) Return $h_wimfilename EndFunc ;==>WIMFileOnly Func ExportWim($h_wimfile) Local $s_DISMCommand = ' /Export-Image /SourceImageFile:' & '"' & @ScriptDir & '\TempWIM\' & WIMFileOnly($h_wimfile) & '"' & ' /SourceIndex:1 /DestinationImageFile:' & '"' & @ScriptDir & '\' & WIMFileOnly($h_wimfile) & '"' & ' /Compress:max' Local $s_ProgTitle = 'Exporting Image', $s_ProgAction = 'Cleaning Windows Image', $s_DestFolder = _FAF_DirectoryGetPathFromPath($h_wimfile) DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') FileDelete($h_wimfile) CopyWIM(@ScriptDir & '\' & WIMFileOnly($h_wimfile), $s_DestFolder) FileDelete(@ScriptDir & '\' & WIMFileOnly($h_wimfile)) EndFunc ;==>ExportWim Func _FAF_DirectoryGetPathFromPath($s_Path, $i_Backslash = 1) If $i_Backslash Then Return StringRegExpReplace($s_Path, "(^.*\\)(.*)", "\1") Return StringRegExpReplace($s_Path, "\\[^\\]*$", "") EndFunc ;==>_FAF_DirectoryGetPathFromPath Func PathIsWritable($sFile) Local $aRet = DllCall('kernel32.dll', 'handle', 'CreateFileW', _ 'wstr', $sFile, _ 'dword', 2, _ 'dword', 7, _ 'struct*', 0, _ 'dword', 3, _ 'dword', 0x02000000, _ 'handle', 0) If @error Or $aRet[0] = Ptr(-1) Or $aRet[0] = 0 Then Return False DllCall('kernel32.dll', 'bool', 'CloseHandle', 'handle', $aRet[0]) Return True EndFunc ;==>PathIsWritable Func CopyWIM($fromFile, $todirectory) Local $FOF_RESPOND_YES = 16 Local $FOF_SIMPLEPROGRESS = 256 $winShell = ObjCreate("shell.application") $winShell.namespace($todirectory).CopyHere($fromFile, $FOF_RESPOND_YES) EndFunc ;==>CopyWIM Func DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_step = True, $i_alt = '') Local $i_percent = 0, $i_value = 0, $i_a = 0 Local $s_DISM = Run(@ComSpec & ' /c DISM' & $s_DISMCommand, @ScriptDir, '', 2 + 4) ProgressOn($s_ProgTitle, $s_ProgAction, ' 0 Percent') While ProcessExists($s_DISM) $line = StdoutRead($s_DISM, 5) If StringInStr($line, '.0%') Then $line1 = StringSplit($line, '.') $i_value = StringRight($line1[$line1[0] - 1], 2) $i_value = StringStripWS($i_value, 7) EndIf If $i_value == "00" Then $i_value = 100 If @error Then ExitLoop Sleep(50) If $i_percent <> $i_value Then ProgressSet($i_value, $s_ProgTitle, $s_ProgAction & $i_value & "%") $i_percent = $i_value EndIf If $s_step = False Then If $i_value = 100 Then $i_a += 1 If $i_a = 1 Then $i_value = 1 $s_ProgTitle = $i_alt EndIf EndIf ElseIf $i_value = 100 Then ExitLoop EndIf WEnd ProgressOff() EndFunc ;==>DISMProgress Edited January 1, 2022 by bobomb Link to comment Share on other sites More sharing options...
benners Posted January 1, 2022 Share Posted January 1, 2022 I have added some comments\suggestions to the code. It's very clean for you're 2/3 rd script, you're picking things up faster than I did expandcollapse popup#NoTrayIcon #RequireAdmin #include <Constants.au3> #include <FileConstants.au3> #include <MsgBoxConstants.au3> Cleanup(False) Main() #cs #### General #### A Nice script for you 2/3 rd? script. Nice and clean with simple functions @scriptdir and @workingdir returns will include a trailing backslash if the script is in a root dir you will have issues with file paths that use these macros, which may cause copy errors. It's personal choice, but I like to add blank lines between code or comments to break it up, it makes it easier for me to read. See Main function for example add error checking for functions that you action later on, if it is vital they have succeeded. i.e. $winShell = ObjCreate("shell.application") add basic logging to keep a record of file paths, variable values, return values etc, especially if you are going to release it for others to use. If there's a problem, they can just post the log. Might help with debugging and save them some legwork add comments for future reference. It might help when you have the headscratch moment as in, WTF is that supposed to do\what was I thinking :) #### Main() #### FileOpenDialog\FileSelectFolder return string paths. A bit nit picky but $h_wimfile should be $s_wimfile and $h_folder, $s_folder if you wanted to follow variable naming conventions as a handle is not returned. FileOpenDialog - Don't use magic numbers for the options parameter. There are constants for this function 1 = $FD_FILEMUSTEXIST Again personal choice but I like to split function calls that have a lot of parameters or long parameter values for readability it makes it easier to see each parameter quickly instead of searching the line for the ',' i.e. as with PathIsWritable() dll call MsgBox - Don't use magic numbers for the flag parameter. There are constants for the msgbox types and perhaps add icons for the associated message error, info, warning etc #### MountWim #### Create a function that returns the TempWIM\MountDir paths. There is a lot of repeated code and if you decide to rename the folders later you'll have to find\replace. This way it is only set in one place. You could also add a regex to remove any trailing slash from the two macros mentioned above. you can call the function rather than keep repeating the path i.e. as in WIMFileOnly() Another personal thing but I like to declare same variable types together and on seperate lines. easier to read and add comments to describe what each variable is for (if needed) #### UnMountWim #### same as MountWim regarding variable declaration, line spacing or comments #### WIMFileOnly #### if you just want the file name of the wim then you could use a regexp Return StringRegExpReplace($s_Path, "^.*\\", "") #### ExportWim #### same as MountWim regarding variable declaration, line spacing or comments #### CopyWIM #### fails for me probably due to file paths and double '\\' "D:\New AutoIt v3 Script (2).au3" (188) : ==> The requested action with this object has failed.: $winShell.namespace($todirectory).CopyHere($fromFile, $FOF_RESPOND_YES) $winShell.namespace($todirectory)^ ERROR you could use _WinAPI_ShellFileOperation to copy the file or _WinAPI_CopyFileEx, this has a callback you can update the progress bar with. #### DISMProgress #### same as MountWim regarding variable declaration, line spacing or comments #ce Func Main() Local $s_wimfile = FileOpenDialog( _ 'Select the boot.wim file to permanently inject drivers into', _ @WorkingDir & '\', _ 'Windows Image File (*.WIM)', _ $FD_FILEMUSTEXIST, _ 'boot.wim') If $s_wimfile Then FileChangeDir(@ScriptDir) If PathIsWritable($s_wimfile) Then Local $h_folder = FileSelectFolder( _ 'Select the folder containing the drivers (Subfolders w/spaces Allowed)', _ @WorkingDir) If $h_folder Then FileChangeDir(@ScriptDir) MountWim($s_wimfile) FileChangeDir($h_folder) AddDrivers($h_folder) UnMountWim() ExportWim($s_wimfile) ; using $MB_OK (0) instead of 1 $MB_OKCANCEL. Unless you wanted ; OK and Cancel buttons. Also maybe add related icons for the msgbox ; MsgBox( _ BitOR($MB_OK, $MB_ICONINFORMATION), _ 'Process Complete...', _ 'Done.') Cleanup() Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'No driver folder selected!') Cleanup() EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'You do not have write access!') Cleanup() EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ "Process Aborted...", _ "No WIM selected!") Exit EndIf EndFunc ;==>Main Func MountWim($h_wimfile) DirCreate(@ScriptDir & '\TempWIM') DirCreate(@ScriptDir & '\MountDir') CopyWIM($h_wimfile, @ScriptDir & '\TempWIM') Local _ $s_DISMCommand = ' /Mount-Image /ImageFile:' & '"' & @ScriptDir & '\TempWIM\' & WIMFileOnly($h_wimfile) & '"' & ' /Index:1 /MountDir:' & '"' & @ScriptDir & '\MountDir' & '"', _ $s_ProgTitle = 'Mounting Image', _ $s_ProgAction = 'Mounting boot.wim - ' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') EndFunc ;==>MountWim Func AddDrivers($h_folder) RunWait(@ComSpec & ' /c DISM /Image:' & '"' & @ScriptDir & '\MountDir' & '"' & ' /Add-Driver /Driver:' & '"' & $h_folder & '"' & ' /ForceUnsigned /Recurse', @WorkingDir) EndFunc ;==>AddDrivers Func UnMountWim($s_commit = '/Commit') Local $s_DISMCommand = ' /Unmount-Image /MountDir:' & '"' & @ScriptDir & '\MountDir' & '" ' & $s_commit Local $s_ProgTitle = 'Saving Image', $s_ProgTitle2 = 'UnMounting Image', $s_ProgAction = 'Processing with ' & $s_commit & ' flag - ' Local $s_step = False DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_step, $s_ProgTitle2) ProcessWaitClose('DISM.EXE') EndFunc ;==>UnMountWim Func Cleanup($b_Exit = True) If FileExists(@ScriptDir & '\MountDir') Then DirRemove(@ScriptDir & '\MountDir', $DIR_REMOVE) If FileExists(@ScriptDir & '\TempWIM') Then DirRemove(@ScriptDir & '\TempWIM', $DIR_REMOVE) If $b_Exit Then Exit EndFunc ;==>Cleanup Func WIMFileOnly($h_wimfile) Local Static $h_wimfilename = StringMid($h_wimfile, StringInStr($h_wimfile, "\", 2, -1) + 1) Return $h_wimfilename EndFunc ;==>WIMFileOnly Func ExportWim($h_wimfile) Local $s_DISMCommand = ' /Export-Image /SourceImageFile:' & '"' & @ScriptDir & '\TempWIM\' & WIMFileOnly($h_wimfile) & '"' & ' /SourceIndex:1 /DestinationImageFile:' & '"' & @ScriptDir & '\' & WIMFileOnly($h_wimfile) & '"' & ' /Compress:max' Local $s_ProgTitle = 'Exporting Image', $s_ProgAction = 'Cleaning Windows Image', $s_DestFolder = _FAF_DirectoryGetPathFromPath($h_wimfile) DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') FileDelete($h_wimfile) CopyWIM(@ScriptDir & '\' & WIMFileOnly($h_wimfile), $s_DestFolder) FileDelete(@ScriptDir & '\' & WIMFileOnly($h_wimfile)) EndFunc ;==>ExportWim Func _FAF_DirectoryGetPathFromPath($s_Path, $i_Backslash = 1) If $i_Backslash Then Return StringRegExpReplace($s_Path, "(^.*\\)(.*)", "\1") Return StringRegExpReplace($s_Path, "\\[^\\]*$", "") EndFunc ;==>_FAF_DirectoryGetPathFromPath Func PathIsWritable($sFile) Local $aRet = DllCall('kernel32.dll', 'handle', 'CreateFileW', _ 'wstr', $sFile, _ 'dword', 2, _ 'dword', 7, _ 'struct*', 0, _ 'dword', 3, _ 'dword', 0x02000000, _ 'handle', 0) If @error Or $aRet[0] = Ptr(-1) Or $aRet[0] = 0 Then Return False DllCall('kernel32.dll', 'bool', 'CloseHandle', 'handle', $aRet[0]) Return True EndFunc ;==>PathIsWritable Func CopyWIM($fromFile, $todirectory) Local $FOF_RESPOND_YES = 16 Local $FOF_SIMPLEPROGRESS = 256 $winShell = ObjCreate("shell.application") $winShell.namespace($todirectory).CopyHere($fromFile, $FOF_RESPOND_YES) EndFunc ;==>CopyWIM Func DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_step = True, $i_alt = '') Local $i_percent = 0, $i_value = 0, $i_a = 0 Local $s_DISM = Run(@ComSpec & ' /c DISM' & $s_DISMCommand, @ScriptDir, '', 2 + 4) ProgressOn($s_ProgTitle, $s_ProgAction, ' 0 Percent') While ProcessExists($s_DISM) $line = StdoutRead($s_DISM, 5) If StringInStr($line, '.0%') Then $line1 = StringSplit($line, '.') $i_value = StringRight($line1[$line1[0] - 1], 2) $i_value = StringStripWS($i_value, 7) EndIf If $i_value == "00" Then $i_value = 100 If @error Then ExitLoop Sleep(50) If $i_percent <> $i_value Then ProgressSet($i_value, $s_ProgTitle, $s_ProgAction & $i_value & "%") $i_percent = $i_value EndIf If $s_step = False Then If $i_value = 100 Then $i_a += 1 If $i_a = 1 Then $i_value = 1 $s_ProgTitle = $i_alt EndIf EndIf ElseIf $i_value = 100 Then ExitLoop EndIf WEnd ProgressOff() EndFunc ;==>DISMProgress bobomb 1 Link to comment Share on other sites More sharing options...
bobomb Posted January 2, 2022 Author Share Posted January 2, 2022 (edited) Ok I think I got all the pathing issues sorted out. Changed the labels from h to s's How am I doing? expandcollapse popup#NoTrayIcon #RequireAdmin #include <Constants.au3> #include <FileConstants.au3> #include <MsgBoxConstants.au3> Main() Func Main($s_ScriptPath = PathRemoveTrail(@ScriptDir), $s_TempPath = PathRemoveTrail(TempLocation($s_ScriptPath))) Cleanup($s_TempPath, False) Local $s_WIMfile = FileOpenDialog( _ 'Select the boot.wim file to permanently inject drivers into', _ $s_ScriptPath & '\', _ 'Windows Image File (*.WIM)', _ $FD_FILEMUSTEXIST, _ 'boot.wim') If $s_WIMfile Then FileChangeDir($s_TempPath) If PathIsWritable($s_WIMfile) Then If PathIsWritable($s_TempPath) Then Local $s_DriverFolder = FileSelectFolder( _ 'Select the folder containing the drivers (Subfolders w/spaces Allowed)', _ $s_ScriptPath) If $s_DriverFolder Then If CheckForSpace($s_WIMfile, $s_TempPath) Then MountWim($s_WIMfile, $s_TempPath) AddDrivers($s_DriverFolder, $s_TempPath) UnMountWim($s_TempPath) ExportWim($s_WIMfile, $s_TempPath) ; using $MB_OK (0) instead of 1 $MB_OKCANCEL MsgBox( _ BitOR($MB_OK, $MB_ICONINFORMATION), _ 'Process Complete...', _ 'Done.') Cleanup($s_TempPath) Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'Not enough free space!') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'No driver folder selected!') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'You do not have write access to %Temp%') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'You do not have write access boot.wim') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ "Process Aborted...", _ "No WIM selected!") Exit EndIf EndFunc ;==>Main Func TempLocation($s_ScriptPath) ; Read INI for temp path, if not exist defaults to system %Temp% ; Temp path cannot be inside driver folder or reCurse Local Static $s_TempPath = IniRead($s_ScriptPath & '\' & StringTrimRight(@ScriptName, 4) & '.ini', "Settings", "TempPath", @TempDir) Return $s_TempPath EndFunc ;==>TempLocation Func CheckForSpace($s_WIMfile, $s_TempPath) Local _ $s_AvailSpace = DriveSpaceFree($s_TempPath), _ $s_expandedWIMsize = ((FileGetSize($s_WIMfile) * 6) / 1048576) ; compresses to ~Approx 20% max, 5x + 1x for boot.wim copy If $s_AvailSpace > $s_expandedWIMsize Then Return True Else Return False EndIf EndFunc ;==>CheckForSpace Func MountWim($s_WIMfile, $s_TempPath) DirCreate($s_TempPath & '\TempWIM') DirCreate($s_TempPath & '\MountDir') CopyWIM($s_WIMfile, $s_TempPath & '\TempWIM') Local _ $s_DISMCommand = ' /Mount-Image /ImageFile:' & '"' & $s_TempPath & '\TempWIM\' & WIMFileOnly($s_WIMfile) & '"' & ' /Index:1 /MountDir:' & '"' & $s_TempPath & '\MountDir' & '"', _ $s_ProgTitle = 'Mounting Image', _ $s_ProgAction = 'Mounting boot.wim - ' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') EndFunc ;==>MountWim Func AddDrivers($s_DriverFolder, $s_TempPath) RunWait(@ComSpec & ' /c DISM /Image:' & '"' & $s_TempPath & '\MountDir' & '"' & ' /Add-Driver /Driver:' & '"' & $s_DriverFolder & '"' & ' /ForceUnsigned /Recurse') ProcessWaitClose('DISM.EXE') EndFunc ;==>AddDrivers Func UnMountWim($s_TempPath, $s_singleProgBar = False) Local _ $s_ProgTitle = 'Saving Image', $s_ProgTitle2 = 'UnMounting Image', $s_ProgAction = 'Saving and UnMounting Image - ', _ $s_DISMCommand = ' /Unmount-Image /MountDir:' & '"' & $s_TempPath & '\MountDir' & '" /Commit' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_singleProgBar, $s_ProgTitle2) ProcessWaitClose('DISM.EXE') EndFunc ;==>UnMountWim Func Cleanup($s_TempPath, $b_Exit = True) If FileExists($s_TempPath & '\MountDir') Then DirRemove($s_TempPath & '\MountDir', $DIR_REMOVE) If FileExists($s_TempPath & '\TempWIM') Then DirRemove($s_TempPath & '\TempWIM', $DIR_REMOVE) If $b_Exit Then Exit EndFunc ;==>Cleanup Func WIMFileOnly($s_WIMfile) Local Static $s_WIMfilename = StringMid($s_WIMfile, StringInStr($s_WIMfile, "\", 2, -1) + 1) Return $s_WIMfilename EndFunc ;==>WIMFileOnly Func ExportWim($s_WIMfile, $s_TempPath) Local _ $s_ProgTitle = 'Exporting Image', $s_ProgAction = 'Cleaning Windows Image - ', $s_DestFolder = _FAF_DirectoryGetPathFromPath($s_WIMfile), $s_WIMname = WIMFileOnly($s_WIMfile), _ $s_DISMCommand = ' /Export-Image /SourceImageFile:' & '"' & $s_TempPath & '\TempWIM\' & $s_WIMname & '"' & ' /SourceIndex:1 /DestinationImageFile:' & '"' & $s_TempPath & '\' & $s_WIMname & '"' & ' /Compress:max' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') FileDelete($s_WIMfile) CopyWIM($s_TempPath & '\' & $s_WIMname, $s_DestFolder) FileDelete($s_TempPath & '\' & $s_WIMname) EndFunc ;==>ExportWim Func _FAF_DirectoryGetPathFromPath($s_Path) Return StringRegExpReplace($s_Path, "\\[^\\]*$", "") EndFunc ;==>_FAF_DirectoryGetPathFromPath Func PathRemoveTrail($sPath) If StringRight($sPath, 1) == '\' Then $sPath = StringTrimRight($sPath, 1) EndIf Return $sPath EndFunc ;==>PathRemoveTrail Func PathIsWritable($s_File) Local _ $a_Ret = DllCall('kernel32.dll', 'handle', 'CreateFileW', _ 'wstr', $s_File, _ 'dword', 2, _ 'dword', 7, _ 'struct*', 0, _ 'dword', 3, _ 'dword', 0x02000000, _ 'handle', 0) If @error Or $a_Ret[0] = Ptr(-1) Or $a_Ret[0] = 0 Then Return False DllCall('kernel32.dll', 'bool', 'CloseHandle', 'handle', $a_Ret[0]) Return True EndFunc ;==>PathIsWritable Func CopyWIM($fromFile, $todirectory) Local _ $FOF_RESPOND_YES = 16, _ $FOF_SIMPLEPROGRESS = 256 $winShell = ObjCreate("shell.application") $winShell.namespace($todirectory).CopyHere($fromFile, $FOF_RESPOND_YES) EndFunc ;==>CopyWIM Func DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_singleProgBar = True, $i_alt = '') Local _ $i_percent = 0, $i_value = 0, $i_a = 0, _ $s_DISM = Run(@ComSpec & ' /c DISM' & $s_DISMCommand, '', '', 2 + 4) ProgressOn($s_ProgTitle, $s_ProgAction, ' 0 Percent') While ProcessExists($s_DISM) $line = StdoutRead($s_DISM, 5) If StringInStr($line, '.0%') Then $line1 = StringSplit($line, '.') $i_value = StringRight($line1[$line1[0] - 1], 2) $i_value = StringStripWS($i_value, 7) EndIf If $i_value == "00" Then $i_value = 100 If @error Then ExitLoop Sleep(50) If $i_percent <> $i_value Then ProgressSet($i_value, $s_ProgTitle, $s_ProgAction & $i_value & "%") $i_percent = $i_value EndIf If $s_singleProgBar = False Then ; is not single progress bar, capture second progress bar If $i_value = 100 Then $i_a += 1 If $i_a = 1 Then $i_value = 1 $s_ProgTitle = $i_alt EndIf EndIf ElseIf $i_value = 100 Then ; single progress bar completes here ExitLoop EndIf WEnd ProgressOff() EndFunc ;==>DISMProgress Edited January 2, 2022 by bobomb Link to comment Share on other sites More sharing options...
benners Posted January 2, 2022 Share Posted January 2, 2022 Doing great. Just a few small changes. Again only personal preferences and making sure all variables are declared expandcollapse popup#NoTrayIcon #RequireAdmin #include <Constants.au3> #include <FileConstants.au3> #include <MsgBoxConstants.au3> Main() Func Main() ; ### declare the local vars here. No need for Main() parameters Local _ $s_ScriptPath = PathRemoveTrail(@ScriptDir), _ $s_TempPath = PathRemoveTrail(TempLocation($s_ScriptPath)) Cleanup($s_TempPath, False) Local $s_WIMfile = FileOpenDialog( _ 'Select the boot.wim file to permanently inject drivers into', _ $s_ScriptPath & '\', _ 'Windows Image File (*.WIM)', _ $FD_FILEMUSTEXIST, _ 'boot.wim') If $s_WIMfile Then FileChangeDir($s_TempPath) If PathIsWritable($s_WIMfile) Then If PathIsWritable($s_TempPath) Then Local $s_DriverFolder = FileSelectFolder( _ 'Select the folder containing the drivers (Subfolders w/spaces Allowed)', _ $s_ScriptPath) If $s_DriverFolder Then If CheckForSpace($s_WIMfile, $s_TempPath) Then MountWim($s_WIMfile, $s_TempPath) AddDrivers($s_DriverFolder, $s_TempPath) UnMountWim($s_TempPath) ExportWim($s_WIMfile, $s_TempPath) ; using $MB_OK (0) instead of 1 $MB_OKCANCEL MsgBox( _ BitOR($MB_OK, $MB_ICONINFORMATION), _ 'Process Complete...', _ 'Done.') Cleanup($s_TempPath) Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'Not enough free space!') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'No driver folder selected!') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'You do not have write access to %Temp%') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'You do not have write access boot.wim') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ "Process Aborted...", _ "No WIM selected!") Exit EndIf EndFunc ;==>Main Func TempLocation($s_ScriptPath) ; Read INI for temp path, if not exist defaults to system %Temp% ; Temp path cannot be inside driver folder or reCurse ; ### what if iniread doesn't fail but returns a blank string? ### Local Static $s_TempPath = IniRead($s_ScriptPath & '\' & StringTrimRight(@ScriptName, 4) & '.ini', "Settings", "TempPath", @TempDir) Return $s_TempPath EndFunc ;==>TempLocation Func CheckForSpace($s_WIMfile, $s_TempPath) Local _ $s_AvailSpace = DriveSpaceFree($s_TempPath), _ $s_expandedWIMsize = ((FileGetSize($s_WIMfile) * 6) / 1048576) ; compresses to ~Approx 20% max, 5x + 1x for boot.wim copy ; ### got rid of if then code If $s_AvailSpace > $s_expandedWIMsize Then Return True Return False EndFunc ;==>CheckForSpace Func MountWim($s_WIMfile, $s_TempPath) DirCreate($s_TempPath & '\TempWIM') DirCreate($s_TempPath & '\MountDir') CopyWIM($s_WIMfile, $s_TempPath & '\TempWIM') Local _ $s_DISMCommand = ' /Mount-Image /ImageFile:' & '"' & $s_TempPath & '\TempWIM\' & WIMFileOnly($s_WIMfile) & '"' & ' /Index:1 /MountDir:' & '"' & $s_TempPath & '\MountDir' & '"', _ $s_ProgTitle = 'Mounting Image', _ $s_ProgAction = 'Mounting boot.wim - ' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') EndFunc ;==>MountWim Func AddDrivers($s_DriverFolder, $s_TempPath) RunWait(@ComSpec & ' /c DISM /Image:' & '"' & $s_TempPath & '\MountDir' & '"' & ' /Add-Driver /Driver:' & '"' & $s_DriverFolder & '"' & ' /ForceUnsigned /Recurse') ProcessWaitClose('DISM.EXE') EndFunc ;==>AddDrivers Func UnMountWim($s_TempPath, $s_singleProgBar = False) ;~ DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_singleProgBar, $s_ProgTitle2) ; ### no need to create variables for titles if only used once, can be added to function directly DISMProgress( _ ' /Unmount-Image /MountDir:' & '"' & $s_TempPath & '\MountDir' & '" /Commit', _ ; _ $s_DISMCommand 'Saving Image', _ ; _ $s_ProgTitle 'Saving and UnMounting Image - ', _ ; _ $s_ProgAction $s_singleProgBar, _ ; _ $s_singleProgBar 'UnMounting Image') ; _ $s_ProgTitle2 ProcessWaitClose('DISM.EXE') EndFunc ;==>UnMountWim Func Cleanup($s_TempPath, $b_Exit = True) If FileExists($s_TempPath & '\MountDir') Then DirRemove($s_TempPath & '\MountDir', $DIR_REMOVE) If FileExists($s_TempPath & '\TempWIM') Then DirRemove($s_TempPath & '\TempWIM', $DIR_REMOVE) If $b_Exit Then Exit EndFunc ;==>Cleanup Func WIMFileOnly($s_WIMfile) Local Static $s_WIMfilename = StringMid($s_WIMfile, StringInStr($s_WIMfile, "\", 2, -1) + 1) Return $s_WIMfilename EndFunc ;==>WIMFileOnly Func ExportWim($s_WIMfile, $s_TempPath) Local _ $s_ProgTitle = 'Exporting Image', _ $s_ProgAction = 'Cleaning Windows Image - ', _ $s_DestFolder = _FAF_DirectoryGetPathFromPath($s_WIMfile), _ $s_WIMname = WIMFileOnly($s_WIMfile), _ $s_DISMCommand = ' /Export-Image /SourceImageFile:' & '"' & $s_TempPath & '\TempWIM\' & $s_WIMname & '"' & ' /SourceIndex:1 /DestinationImageFile:' & '"' & $s_TempPath & '\' & $s_WIMname & '"' & ' /Compress:max' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') FileDelete($s_WIMfile) CopyWIM($s_TempPath & '\' & $s_WIMname, $s_DestFolder) FileDelete($s_TempPath & '\' & $s_WIMname) EndFunc ;==>ExportWim Func _FAF_DirectoryGetPathFromPath($s_Path) Return StringRegExpReplace($s_Path, "\\[^\\]*$", "") EndFunc ;==>_FAF_DirectoryGetPathFromPath Func PathRemoveTrail($sPath) ; ### changed to single line if If StringRight($sPath, 1) == '\' Then $sPath = StringTrimRight($sPath, 1) Return $sPath EndFunc ;==>PathRemoveTrail Func PathIsWritable($s_File) Local _ $a_Ret = DllCall('kernel32.dll', 'handle', 'CreateFileW', _ 'wstr', $s_File, _ 'dword', 2, _ 'dword', 7, _ 'struct*', 0, _ 'dword', 3, _ 'dword', 0x02000000, _ 'handle', 0) If @error Or $a_Ret[0] = Ptr(-1) Or $a_Ret[0] = 0 Then Return False DllCall('kernel32.dll', 'bool', 'CloseHandle', 'handle', $a_Ret[0]) Return True EndFunc ;==>PathIsWritable Func CopyWIM($fromFile, $todirectory) Local _ $FOF_RESPOND_YES = 16, _ $FOF_SIMPLEPROGRESS = 256 $winShell = ObjCreate("shell.application") $winShell.namespace($todirectory).CopyHere($fromFile, $FOF_RESPOND_YES) EndFunc ;==>CopyWIM Func DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_singleProgBar = True, $i_alt = '') ; ### replaced magic numbers with AutoIt constant ; ### Run returns a PID which is integer. Replaced $s+DISM with $i_DISM Local $i_DISM = Run(@ComSpec & ' /c DISM' & $s_DISMCommand, '', '', $STDERR_MERGED) ProgressOn($s_ProgTitle, $s_ProgAction, ' 0 Percent') Local _ $i_percent = 0, _ $i_value = 0, _ $i_a = 0 Local _ ; #### declare variables $s_Line, _ $s_Line1 While ProcessExists($i_DISM) $s_Line = StdoutRead($i_DISM, 5) ; #### what does 5 represent? should be a boolean value for argument If @error Then ExitLoop ; ### moved the error check for stdout If StringInStr($s_Line, '.0%') Then $s_Line1 = StringSplit($s_Line, '.') $i_value = StringRight($s_Line1[$s_Line1[0] - 1], 2) $i_value = StringStripWS($i_value, 7) EndIf If $i_value == "00" Then $i_value = 100 Sleep(50) If $i_percent <> $i_value Then ProgressSet($i_value, $s_ProgTitle, $s_ProgAction & $i_value & "%") $i_percent = $i_value EndIf If $s_singleProgBar = False Then ; is not single progress bar, capture second progress bar If $i_value = 100 Then $i_a += 1 If $i_a = 1 Then $i_value = 1 $s_ProgTitle = $i_alt EndIf EndIf ElseIf $i_value = 100 Then ; single progress bar completes here ExitLoop EndIf WEnd ProgressOff() EndFunc ;==>DISMProgress Link to comment Share on other sites More sharing options...
bobomb Posted January 3, 2022 Author Share Posted January 3, 2022 (edited) Made some more improvements following your advice and adding some error checking... (Tested on Win10 Win11, will use @OSversion later check version and adjust accordingly) expandcollapse popup#NoTrayIcon #RequireAdmin #include <Constants.au3> #include <FileConstants.au3> #include <MsgBoxConstants.au3> Main() Func Main() Local _ $s_ScriptPath = PathRemoveTrail(@ScriptDir), _ $s_TempPath = PathRemoveTrail(TempLocation($s_ScriptPath)) If DriveGetType($s_TempPath) = "Fixed" Then Cleanup($s_TempPath, False) Local $s_WIMfile = FileOpenDialog( _ 'Select the boot.wim file to permanently inject drivers into', _ $s_ScriptPath & '\', _ 'Windows Image File (*.WIM)', _ $FD_FILEMUSTEXIST, _ 'boot.wim') If $s_WIMfile Then FileChangeDir($s_TempPath) If PathIsWritable($s_WIMfile) Then If PathIsWritable($s_TempPath) Then Local $s_DriverFolder = FileSelectFolder( _ 'Select the folder containing the drivers (Subfolders w/spaces Allowed)', _ $s_ScriptPath) If $s_DriverFolder Then If StringInStr($s_TempPath, $s_DriverFolder) Then MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'Working folder cannot be a subfolder of the driver folder!' & @CRLF & _ 'Move the drivers to a different folder.') Cleanup($s_TempPath) Else If CheckForSpace($s_WIMfile, $s_TempPath) Then MountWim($s_WIMfile, $s_TempPath) AddDrivers($s_DriverFolder, $s_TempPath) UnMountWim($s_TempPath) ExportWim($s_WIMfile, $s_TempPath, $s_ScriptPath) ; using $MB_OK (0) instead of 1 $MB_OKCANCEL MsgBox( _ BitOR($MB_OK, $MB_ICONINFORMATION), _ 'Process Complete...', _ 'Done.') Cleanup($s_TempPath) Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'Not enough free space!') Cleanup($s_TempPath) EndIf EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'No driver folder selected!') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'You do not have write access to %Temp%') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'You do not have write access boot.wim') Cleanup($s_TempPath) EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ "Process Aborted...", _ "No WIM selected!") Exit EndIf Else MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'Working folder must be located on a Fixed drive!' & @CRLF & @CRLF & _ 'Create ' & StringTrimRight(@ScriptName, 4) & '.ini and put it in' & @CRLF & _ 'in the same folder as the program. Example Below.' & @CRLF & @CRLF & _ '[Settings]' & @CRLF & _ 'TempDir=<put a valid path here>' & @CRLF & @CRLF & _ 'Set it to any path on a fixed drive.' & @CRLF & _ 'This is a requirement of DISM.') EndIf EndFunc ;==>Main Func TempLocation($s_ScriptPath) ; Read INI for temp path, if not exist defaults to system %Temp% ; Temp path cannot be inside driver folder or reCurse Local Static $s_TempPath = IniRead($s_ScriptPath & '\' & StringTrimRight(@ScriptName, 4) & '.ini', "Settings", "TempPath", @TempDir) If $s_TempPath = "" Then Return @ScriptDir Return $s_TempPath EndFunc ;==>TempLocation Func CheckForSpace($s_WIMfile, $s_TempPath) Local _ $s_AvailSpace = DriveSpaceFree($s_TempPath), _ $s_expandedWIMsize = ((FileGetSize($s_WIMfile) * 6) / 1048576) ; compresses to ~Approx 20% max, 5x + 1x for boot.wim copy If $s_AvailSpace > $s_expandedWIMsize Then Return True Return False EndFunc ;==>CheckForSpace Func MountWim($s_WIMfile, $s_TempPath) DirCreate($s_TempPath & '\TempWIM') DirCreate($s_TempPath & '\MountDir') CopyWIM($s_WIMfile, $s_TempPath & '\TempWIM') Local _ $s_DISMCommand = ' /Mount-Image /ImageFile:' & '"' & $s_TempPath & '\TempWIM\' & WIMFileOnly($s_WIMfile) & '"' & ' /Index:1 /MountDir:' & '"' & $s_TempPath & '\MountDir' & '"', _ $s_ProgTitle = 'Mounting Image', _ $s_ProgAction = 'Mounting boot.wim - ' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') EndFunc ;==>MountWim Func AddDrivers($s_DriverFolder, $s_TempPath) RunWait(@ComSpec & ' /c DISM /Image:' & '"' & $s_TempPath & '\MountDir' & '"' & ' /Add-Driver /Driver:' & '"' & $s_DriverFolder & '"' & ' /ForceUnsigned /Recurse') ProcessWaitClose('DISM.EXE') EndFunc ;==>AddDrivers Func UnMountWim($s_TempPath, $s_singleProgBar = False) DISMProgress( _ ' /Unmount-Image /MountDir:' & '"' & $s_TempPath & '\MountDir' & '" /Commit', _ ; _ $s_DISMCommand 'Saving Image', _ ; _ $s_ProgTitle 'Saving and UnMounting Image - ', _ ; _ $s_ProgAction $s_singleProgBar, _ ; _ $s_singleProgBar 'UnMounting Image') ; _ $s_ProgTitle2 ProcessWaitClose('DISM.EXE') EndFunc ;==>UnMountWim Func Cleanup($s_TempPath, $b_Exit = True) If FileExists($s_TempPath & '\MountDir') Then DirRemove($s_TempPath & '\MountDir', $DIR_REMOVE) If FileExists($s_TempPath & '\TempWIM') Then DirRemove($s_TempPath & '\TempWIM', $DIR_REMOVE) If $b_Exit Then Exit EndFunc ;==>Cleanup Func WIMFileOnly($s_WIMfile) Local Static $s_WIMfilename = StringMid($s_WIMfile, StringInStr($s_WIMfile, "\", 2, -1) + 1) Return $s_WIMfilename EndFunc ;==>WIMFileOnly Func ExportWim($s_WIMfile, $s_TempPath, $s_ScriptPath) Local _ $s_ProgTitle = 'Exporting Image', _ $s_ProgAction = 'Cleaning Windows Image - ', _ $s_DestFolder = _FAF_DirectoryGetPathFromPath($s_WIMfile), _ $s_WIMname = WIMFileOnly($s_WIMfile), _ $s_DISMCommand = ' /Export-Image /SourceImageFile:' & '"' & $s_TempPath & '\TempWIM\' & $s_WIMname & '"' & ' /SourceIndex:1 /DestinationImageFile:' & '"' & $s_TempPath & '\' & $s_WIMname & '"' & ' /Compress:max' Local $s_NewWimFile = ($s_TempPath & '\' & $s_WIMname) DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') VerifyAndCopy($s_WIMfile, $s_WIMname, $s_TempPath, $s_DestFolder, $s_ScriptPath) EndFunc ;==>ExportWim Func VerifyAndCopy($s_OldWimFile, $s_WIMname, $s_TempPath, $s_DestFolder, $s_ScriptPath) Local $s_NewWimFile = ($s_TempPath & '\' & $s_WIMname) Local _ $i_NewWimSizeMB = (FileGetSize($s_NewWimFile) / 1048576), _ $i_OldWimSizeMB = (FileGetSize($s_OldWimFile) / 1048576), _ $i_FreeSpaceMB = DriveSpaceFree($s_DestFolder), _ $i_OkCancel = 0 If $i_NewWimSizeMB > ($i_FreeSpaceMB + $i_OldWimSizeMB) Then Do $i_OkCancel = MsgBox( _ BitOR($MB_OKCANCEL, $MB_ICONERROR), _ 'Not enough space!', _ 'There is not enough space to create the new boot.wim' & @CRLF & _ 'in its original location. Please select an alternate' & @CRLF & _ 'location to save the new file. The original boot.wim' & @CRLF & _ 'will not be updated or removed.') If $i_OkCancel = 2 Then MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'Cleaning up. No changes were' & @CRLF & _ 'made to your original files.') Cleanup($s_TempPath) EndIf $s_DestFolder = FileSelectFolder( _ 'Select an alternate location to save the new boot.wim', _ @ScriptDir) If Not $s_DestFolder = '' Then $i_FreeSpaceMB = DriveSpaceFree($s_DestFolder) Until $i_NewWimSizeMB < $i_FreeSpaceMB CopyWIM($s_NewWimFile, $s_DestFolder) FileDelete($s_NewWimFile) Else Sleep(500) FileDelete($s_OldWimFile) CopyWIM($s_NewWimFile, $s_DestFolder) FileDelete($s_NewWimFile) EndIf EndFunc ;==>VerifyAndCopy Func _FAF_DirectoryGetPathFromPath($s_Path) Return StringRegExpReplace($s_Path, "\\[^\\]*$", "") EndFunc ;==>_FAF_DirectoryGetPathFromPath Func PathRemoveTrail($sPath) If StringRight($sPath, 1) == '\' Then $sPath = StringTrimRight($sPath, 1) Return $sPath EndFunc ;==>PathRemoveTrail Func PathIsWritable($s_File) Local _ $a_Ret = DllCall('kernel32.dll', 'handle', 'CreateFileW', _ 'wstr', $s_File, _ 'dword', 2, _ 'dword', 7, _ 'struct*', 0, _ 'dword', 3, _ 'dword', 0x02000000, _ 'handle', 0) If @error Or $a_Ret[0] = Ptr(-1) Or $a_Ret[0] = 0 Then Return False DllCall('kernel32.dll', 'bool', 'CloseHandle', 'handle', $a_Ret[0]) Return True EndFunc ;==>PathIsWritable Func CopyWIM($fromFile, $todirectory) Local _ $i_OldWimFile = FileGetSize($fromFile), _ $FOF_RESPOND_YES = 16, _ $FOF_SIMPLEPROGRESS = 256 $s_WIMname = WIMFileOnly($fromFile) Local $s_NewWimFile = ($todirectory & '\' & $s_WIMname) If FileExists($s_NewWimFile) Then FileDelete($s_NewWimFile) Sleep(500) Local $winShell = ObjCreate("shell.application") $winShell.namespace($todirectory).CopyHere($fromFile, $FOF_RESPOND_YES) Do Sleep(10) Until FileGetSize($s_NewWimFile) = $i_OldWimFile EndFunc ;==>CopyWIM Func DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_singleProgBar = True, $i_alt = '') Local $i_DISM = Run(@ComSpec & ' /c DISM' & $s_DISMCommand, '', '', $STDERR_MERGED) ProgressOn($s_ProgTitle, $s_ProgAction, ' 0 Percent') Local _ $i_percent = 0, _ $i_value = 0, _ $i_a = 0, _ $s_Line, _ $s_Line1 While ProcessExists($i_DISM) $s_Line = StdoutRead($i_DISM) If @error Then ExitLoop If StringInStr($s_Line, '.0%') Then $s_Line1 = StringSplit($s_Line, '.') $i_value = StringRight($s_Line1[$s_Line1[0] - 1], 2) $i_value = StringStripWS($i_value, 7) EndIf If $i_value == "00" Then $i_value = 100 Sleep(50) If $i_percent <> $i_value Then ProgressSet($i_value, $s_ProgTitle, $s_ProgAction & $i_value & "%") $i_percent = $i_value EndIf If $s_singleProgBar = False Then ; is not single progress bar, capture second progress bar If $i_value = 100 Then $i_a += 1 If $i_a = 1 Then $i_value = 1 $s_ProgTitle = $i_alt EndIf EndIf ElseIf $i_value = 100 Then ; single progress bar completes here ExitLoop EndIf WEnd ProgressOff() EndFunc ;==>DISMProgress Edited January 3, 2022 by bobomb Link to comment Share on other sites More sharing options...
benners Posted January 3, 2022 Share Posted January 3, 2022 Coming along nicely. The CopyWIM function now waits for Teracopy to copy the file before continuing. That might be just my setup as I have it as the default shell copy handler. I have altered the Main function to just have one msgbox and cleanup call expandcollapse popup#NoTrayIcon #RequireAdmin #include <Constants.au3> #include <FileConstants.au3> #include <MsgBoxConstants.au3> Main() Func Main() Local _ $s_ScriptPath = PathRemoveTrail(@ScriptDir), _ $s_TempPath = PathRemoveTrail(TempLocation($s_ScriptPath)) Cleanup($s_TempPath, False) Local $i_MBIcon = $MB_ICONERROR Local _ ; set default text for If $s_WIMfile is blank $s_MBTitle = 'Process Aborted...', _ $s_MBText = 'No WIM selected!' If DriveGetType($s_TempPath) = "Fixed" Then Local $s_WIMfile = FileOpenDialog( _ 'Select the boot.wim file to permanently inject drivers into', _ $s_ScriptPath & '\', _ 'Windows Image File (*.WIM)', _ $FD_FILEMUSTEXIST, _ 'boot.wim') If $s_WIMfile Then FileChangeDir($s_TempPath) If PathIsWritable($s_WIMfile) Then If PathIsWritable($s_TempPath) Then Local $s_DriverFolder = FileSelectFolder( _ 'Select the folder containing the drivers (Subfolders w/spaces Allowed)', _ $s_ScriptPath) If $s_DriverFolder Then If Not StringInStr($s_TempPath, $s_DriverFolder) Then If CheckForSpace($s_WIMfile, $s_TempPath) Then MountWim($s_WIMfile, $s_TempPath) AddDrivers($s_DriverFolder, $s_TempPath) UnMountWim($s_TempPath) ExportWim($s_WIMfile, $s_TempPath, $s_ScriptPath) $i_MBIcon = $MB_ICONINFORMATION $s_MBTitle = 'Process Complete...' $s_MBText = 'Done.' Else $s_MBText = 'Not enough free space!' EndIf Else $s_MBText = 'Working folder cannot be a subfolder of the driver folder!' & @CRLF & _ 'Move the drivers to a different folder.' EndIf Else $s_MBText = 'No driver folder selected!' EndIf Else $s_MBText = 'You do not have write access to ' & $s_TempPath EndIf Else $s_MBText = 'You do not have write access boot.wim' EndIf EndIf Else $s_MBText = _ 'Working folder must be located on a Fixed drive!' & @CRLF & @CRLF & _ 'Create ' & StringTrimRight(@ScriptName, 4) & '.ini and put it in' & @CRLF & _ 'in the same folder as the program. Example Below.' & @CRLF & @CRLF & _ '[Settings]' & @CRLF & _ 'TempDir=<put a valid path here>' & @CRLF & @CRLF & _ 'Set it to any path on a fixed drive.' & @CRLF & _ 'This is a requirement of DISM.' EndIf MsgBox(BitOR($MB_OK, $i_MBIcon), $s_MBTitle, $s_MBText) Cleanup($s_TempPath) EndFunc ;==>Main Func TempLocation($s_ScriptPath) ; Read INI for temp path, if not exist defaults to system %Temp% ; Temp path cannot be inside driver folder or reCurse Local Static $s_TempPath = IniRead($s_ScriptPath & '\' & StringTrimRight(@ScriptName, 4) & '.ini', "Settings", "TempPath", @TempDir) ;~ If $s_TempPath = "" Then Return @ScriptDir If $s_TempPath = "" Then $s_TempPath = PathRemoveTrail(@ScriptDir) Return $s_TempPath EndFunc ;==>TempLocation Func CheckForSpace($s_WIMfile, $s_TempPath) ; you can call the functions directly and compare the values rather than creating variables to store the values ; if you were using the variables for something like logging the values or returning the values then create them ; as you're using them more than once If DriveSpaceFree($s_TempPath) > ((FileGetSize($s_WIMfile) * 6) / 1048576) Then Return True Return False ; DriveSpaceFree returns a floating point number so it should be $f_ not $s_ ; FileGetSize returns an integer but when divided by, it will most likely be a float so same as above ;~ Local _ ;~ $f_AvailSpace = DriveSpaceFree($s_TempPath), _ ;~ $f_expandedWIMsize = ((FileGetSize($s_WIMfile) * 6) / 1048576) ; compresses to ~Approx 20% max, 5x + 1x for boot.wim copy ;~ If $f_AvailSpace > $f_expandedWIMsize Then Return True ;~ Return False EndFunc ;==>CheckForSpace Func MountWim($s_WIMfile, $s_TempPath) DirCreate($s_TempPath & '\TempWIM') DirCreate($s_TempPath & '\MountDir') CopyWIM($s_WIMfile, $s_TempPath & '\TempWIM') Local _ $s_DISMCommand = ' /Mount-Image /ImageFile:' & '"' & $s_TempPath & '\TempWIM\' & WIMFileOnly($s_WIMfile) & '"' & ' /Index:1 /MountDir:' & '"' & $s_TempPath & '\MountDir' & '"', _ $s_ProgTitle = 'Mounting Image', _ $s_ProgAction = 'Mounting boot.wim - ' DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') EndFunc ;==>MountWim Func AddDrivers($s_DriverFolder, $s_TempPath) RunWait(@ComSpec & ' /c DISM /Image:' & '"' & $s_TempPath & '\MountDir' & '"' & ' /Add-Driver /Driver:' & '"' & $s_DriverFolder & '"' & ' /ForceUnsigned /Recurse') ProcessWaitClose('DISM.EXE') EndFunc ;==>AddDrivers Func UnMountWim($s_TempPath, $s_singleProgBar = False) DISMProgress( _ ' /Unmount-Image /MountDir:' & '"' & $s_TempPath & '\MountDir' & '" /Commit', _ ; _ $s_DISMCommand 'Saving Image', _ ; _ $s_ProgTitle 'Saving and UnMounting Image - ', _ ; _ $s_ProgAction $s_singleProgBar, _ ; _ $s_singleProgBar 'UnMounting Image') ; _ $s_ProgTitle2 ProcessWaitClose('DISM.EXE') EndFunc ;==>UnMountWim Func Cleanup($s_TempPath, $b_Exit = True) If FileExists($s_TempPath & '\MountDir') Then DirRemove($s_TempPath & '\MountDir', $DIR_REMOVE) If FileExists($s_TempPath & '\TempWIM') Then DirRemove($s_TempPath & '\TempWIM', $DIR_REMOVE) If $b_Exit Then Exit EndFunc ;==>Cleanup Func WIMFileOnly($s_WIMfile) Local Static $s_WIMfilename = StringMid($s_WIMfile, StringInStr($s_WIMfile, "\", 2, -1) + 1) Return $s_WIMfilename EndFunc ;==>WIMFileOnly Func ExportWim($s_WIMfile, $s_TempPath, $s_ScriptPath) Local _ $s_ProgTitle = 'Exporting Image', _ $s_ProgAction = 'Cleaning Windows Image - ', _ $s_DestFolder = _FAF_DirectoryGetPathFromPath($s_WIMfile), _ $s_WIMname = WIMFileOnly($s_WIMfile), _ $s_DISMCommand = ' /Export-Image /SourceImageFile:' & '"' & $s_TempPath & '\TempWIM\' & $s_WIMname & '"' & ' /SourceIndex:1 /DestinationImageFile:' & '"' & $s_TempPath & '\' & $s_WIMname & '"' & ' /Compress:max' Local $s_NewWimFile = ($s_TempPath & '\' & $s_WIMname) DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction) ProcessWaitClose('DISM.EXE') VerifyAndCopy($s_WIMfile, $s_WIMname, $s_TempPath, $s_DestFolder, $s_ScriptPath) EndFunc ;==>ExportWim Func VerifyAndCopy($s_OldWimFile, $s_WIMname, $s_TempPath, $s_DestFolder, $s_ScriptPath) Local $s_NewWimFile = ($s_TempPath & '\' & $s_WIMname) Local _ $i_NewWimSizeMB = (FileGetSize($s_NewWimFile) / 1048576), _ $i_OldWimSizeMB = (FileGetSize($s_OldWimFile) / 1048576), _ $i_FreeSpaceMB = DriveSpaceFree($s_DestFolder), _ $i_OkCancel = 0 If $i_NewWimSizeMB > ($i_FreeSpaceMB + $i_OldWimSizeMB) Then Do $i_OkCancel = MsgBox( _ BitOR($MB_OKCANCEL, $MB_ICONERROR), _ 'Not enough space!', _ 'There is not enough space to create the new boot.wim' & @CRLF & _ 'in its original location. Please select an alternate' & @CRLF & _ 'location to save the new file. The original boot.wim' & @CRLF & _ 'will not be updated or removed.') If $i_OkCancel = 2 Then MsgBox( _ BitOR($MB_OK, $MB_ICONERROR), _ 'Process Aborted...', _ 'Cleaning up. No changes were' & @CRLF & _ 'made to your original files.') Cleanup($s_TempPath) EndIf $s_DestFolder = FileSelectFolder( _ 'Select an alternate location to save the new boot.wim', _ @ScriptDir) If Not $s_DestFolder = '' Then $i_FreeSpaceMB = DriveSpaceFree($s_DestFolder) Until $i_NewWimSizeMB < $i_FreeSpaceMB CopyWIM($s_NewWimFile, $s_DestFolder) FileDelete($s_NewWimFile) Else Sleep(500) FileDelete($s_OldWimFile) CopyWIM($s_NewWimFile, $s_DestFolder) FileDelete($s_NewWimFile) EndIf EndFunc ;==>VerifyAndCopy Func _FAF_DirectoryGetPathFromPath($s_Path) Return StringRegExpReplace($s_Path, "\\[^\\]*$", "") EndFunc ;==>_FAF_DirectoryGetPathFromPath Func PathRemoveTrail($sPath) If StringRight($sPath, 1) == '\' Then $sPath = StringTrimRight($sPath, 1) Return $sPath EndFunc ;==>PathRemoveTrail Func PathIsWritable($s_File) Local _ $a_Ret = DllCall('kernel32.dll', 'handle', 'CreateFileW', _ 'wstr', $s_File, _ 'dword', 2, _ 'dword', 7, _ 'struct*', 0, _ 'dword', 3, _ 'dword', 0x02000000, _ 'handle', 0) If @error Or $a_Ret[0] = Ptr(-1) Or $a_Ret[0] = 0 Then Return False DllCall('kernel32.dll', 'bool', 'CloseHandle', 'handle', $a_Ret[0]) Return True EndFunc ;==>PathIsWritable Func CopyWIM($fromFile, $todirectory) Local _ $i_OldWimFile = FileGetSize($fromFile), _ $FOF_RESPOND_YES = 16, _ $FOF_SIMPLEPROGRESS = 256 $s_WIMname = WIMFileOnly($fromFile) Local $s_NewWimFile = ($todirectory & '\' & $s_WIMname) If FileExists($s_NewWimFile) Then FileDelete($s_NewWimFile) Sleep(500) Local $winShell = ObjCreate("shell.application") $winShell.namespace($todirectory).CopyHere($fromFile, $FOF_RESPOND_YES) Do Sleep(10) Until FileGetSize($s_NewWimFile) = $i_OldWimFile EndFunc ;==>CopyWIM Func DISMProgress($s_DISMCommand, $s_ProgTitle, $s_ProgAction, $s_singleProgBar = True, $i_alt = '') Local $i_DISM = Run(@ComSpec & ' /c DISM' & $s_DISMCommand, '', '', $STDERR_MERGED) ProgressOn($s_ProgTitle, $s_ProgAction, ' 0 Percent') Local _ $i_percent = 0, _ $i_value = 0, _ $i_a = 0, _ $s_Line, _ $s_Line1 While ProcessExists($i_DISM) $s_Line = StdoutRead($i_DISM) If @error Then ExitLoop If StringInStr($s_Line, '.0%') Then $s_Line1 = StringSplit($s_Line, '.') $i_value = StringRight($s_Line1[$s_Line1[0] - 1], 2) $i_value = StringStripWS($i_value, 7) EndIf If $i_value == "00" Then $i_value = 100 Sleep(50) If $i_percent <> $i_value Then ProgressSet($i_value, $s_ProgTitle, $s_ProgAction & $i_value & "%") $i_percent = $i_value EndIf If $s_singleProgBar = False Then ; is not single progress bar, capture second progress bar If $i_value = 100 Then $i_a += 1 If $i_a = 1 Then $i_value = 1 $s_ProgTitle = $i_alt EndIf EndIf ElseIf $i_value = 100 Then ; single progress bar completes here ExitLoop EndIf WEnd ProgressOff() EndFunc ;==>DISMProgress Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now