Jump to content

Driver Injection for WinPE (For on the fly use between builds) - Suggestions


Recommended Posts

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.

#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 by bobomb
Link to post
Share on other sites

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 :thumbsup:

#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

 

Link to post
Share on other sites
Posted (edited)

Ok I think I got all the pathing issues sorted out. Changed the labels from h to s's ;) How am I doing?

#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 by bobomb
Link to post
Share on other sites

Doing great. Just a few small changes. Again only personal preferences and making sure all variables are declared

#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 post
Share on other sites
Posted (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)

#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 by bobomb
Link to post
Share on other sites

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

#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 post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...