mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Improves driver injection for long paths
Adds a SUBST-based DISM injection loop to avoid path-length failures when adding large driver sets. Improves INI/INF parsing reliability with Unicode API settings, a larger auto-growing buffer, and normalization of GUID values. Hardens driver copying by using long-path prefixes and literal paths, reducing copy errors on deeply nested driver folders.
This commit is contained in:
+240
-15
@@ -568,7 +568,7 @@ class VhdxCacheItem {
|
|||||||
|
|
||||||
#Support for ini reading
|
#Support for ini reading
|
||||||
$definition = @'
|
$definition = @'
|
||||||
[DllImport("kernel32.dll")]
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
public static extern uint GetPrivateProfileString(
|
public static extern uint GetPrivateProfileString(
|
||||||
string lpAppName,
|
string lpAppName,
|
||||||
string lpKeyName,
|
string lpKeyName,
|
||||||
@@ -2746,8 +2746,27 @@ function Get-PrivateProfileString {
|
|||||||
[Parameter()]
|
[Parameter()]
|
||||||
[string]$KeyName
|
[string]$KeyName
|
||||||
)
|
)
|
||||||
$sbuilder = [System.Text.StringBuilder]::new(1024)
|
|
||||||
[void][Win32.Kernel32]::GetPrivateProfileString($SectionName, $KeyName, "", $sbuilder, $sbuilder.Capacity, $FileName)
|
# Read key from an INF/INI file. Use a larger buffer and allow it to grow if needed.
|
||||||
|
$bufferSize = 4096
|
||||||
|
$maxBufferSize = 65536
|
||||||
|
$sbuilder = $null
|
||||||
|
$charsCopied = 0
|
||||||
|
|
||||||
|
while ($true) {
|
||||||
|
$sbuilder = [System.Text.StringBuilder]::new($bufferSize)
|
||||||
|
$charsCopied = [Win32.Kernel32]::GetPrivateProfileString($SectionName, $KeyName, "", $sbuilder, [uint32]$sbuilder.Capacity, $FileName)
|
||||||
|
|
||||||
|
if ([int]$charsCopied -lt ($sbuilder.Capacity - 1)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bufferSize -ge $maxBufferSize) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
$bufferSize = [Math]::Min(($bufferSize * 2), $maxBufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
return $sbuilder.ToString()
|
return $sbuilder.ToString()
|
||||||
}
|
}
|
||||||
@@ -2800,6 +2819,187 @@ function Get-PrivateProfileSection {
|
|||||||
return $hashTable
|
return $hashTable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-AvailableDriveLetter {
|
||||||
|
# Get an unused drive letter for temporary SUBST mappings
|
||||||
|
$usedLetters = (Get-PSDrive -PSProvider FileSystem).Name | ForEach-Object { $_.ToUpperInvariant() }
|
||||||
|
for ($ascii = [int][char]'Z'; $ascii -ge [int][char]'A'; $ascii--) {
|
||||||
|
$candidate = [char]$ascii
|
||||||
|
if ($usedLetters -notcontains $candidate) {
|
||||||
|
return $candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function New-DriverSubstMapping {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$SourcePath
|
||||||
|
)
|
||||||
|
|
||||||
|
# Map a long driver source folder to a short drive root using SUBST
|
||||||
|
$resolvedPath = (Resolve-Path -Path $SourcePath -ErrorAction Stop).Path
|
||||||
|
$driveLetter = Get-AvailableDriveLetter
|
||||||
|
if ($null -eq $driveLetter) {
|
||||||
|
throw 'No drive letters are available for SUBST mapping.'
|
||||||
|
}
|
||||||
|
$driveName = "$driveLetter`:"
|
||||||
|
$mappedPath = "$driveLetter`:\"
|
||||||
|
WriteLog "Mapping driver folder '$resolvedPath' to $driveName with SUBST."
|
||||||
|
$escapedPath = $resolvedPath -replace '"', '""'
|
||||||
|
$arguments = "/c subst $driveName `"$escapedPath`""
|
||||||
|
Invoke-Process -FilePath cmd.exe -ArgumentList $arguments
|
||||||
|
return [PSCustomObject]@{
|
||||||
|
DriveLetter = $driveLetter
|
||||||
|
DriveName = $driveName
|
||||||
|
DrivePath = $mappedPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Remove-DriverSubstMapping {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$DriveLetter
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove the temporary SUBST mapping
|
||||||
|
$driveName = "$DriveLetter`:"
|
||||||
|
WriteLog "Removing SUBST drive $driveName"
|
||||||
|
try {
|
||||||
|
$arguments = "/c subst $driveName /d"
|
||||||
|
Invoke-Process -FilePath cmd.exe -ArgumentList $arguments
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "Failed to remove SUBST drive $($driveName): $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-DismDriverInjectionWithSubstLoop {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ImagePath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$DriverRoot
|
||||||
|
)
|
||||||
|
|
||||||
|
# Resolve input paths
|
||||||
|
$resolvedImagePath = (Resolve-Path -Path $ImagePath -ErrorAction Stop).Path
|
||||||
|
$resolvedDriverRoot = (Resolve-Path -Path $DriverRoot -ErrorAction Stop).Path
|
||||||
|
|
||||||
|
# Discover INF files under the driver root
|
||||||
|
WriteLog "Scanning for INF files under: $resolvedDriverRoot"
|
||||||
|
$infFiles = Get-ChildItem -Path $resolvedDriverRoot -Filter '*.inf' -File -Recurse -ErrorAction SilentlyContinue
|
||||||
|
if ($null -eq $infFiles -or $infFiles.Count -eq 0) {
|
||||||
|
WriteLog "No INF files found under: $resolvedDriverRoot"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine the deepest stable folders we can map with SUBST (SUBST has its own max path constraints)
|
||||||
|
# Strategy:
|
||||||
|
# - Start at the INF parent folder
|
||||||
|
# - If too long for SUBST, walk up until the path is short enough
|
||||||
|
# - Deduplicate and avoid redundant child folders when a parent already covers them via DISM /Recurse
|
||||||
|
$substTargetMaxLength = 240
|
||||||
|
$candidateDirs = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
|
||||||
|
foreach ($infFile in $infFiles) {
|
||||||
|
$candidateDir = Split-Path -Path $infFile.FullName -Parent
|
||||||
|
|
||||||
|
while ($candidateDir.Length -gt $substTargetMaxLength) {
|
||||||
|
$parentDir = Split-Path -Path $candidateDir -Parent
|
||||||
|
if ([string]::IsNullOrWhiteSpace($parentDir) -or $parentDir -eq $candidateDir) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
$candidateDir = $parentDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($candidateDir.Length -gt $substTargetMaxLength) {
|
||||||
|
WriteLog "Warning: Skipping INF folder due to SUBST length limit (len=$($candidateDir.Length)): $candidateDir"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
[void]$candidateDirs.Add($candidateDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
$sortedCandidates = $candidateDirs | Sort-Object Length, @{ Expression = { $_ }; Ascending = $true }
|
||||||
|
$selectedDirs = [System.Collections.Generic.List[string]]::new()
|
||||||
|
|
||||||
|
foreach ($candidateDir in $sortedCandidates) {
|
||||||
|
$isCovered = $false
|
||||||
|
foreach ($selectedDir in $selectedDirs) {
|
||||||
|
if ($candidateDir.Equals($selectedDir, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||||
|
$isCovered = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
$prefix = $selectedDir.TrimEnd('\') + '\'
|
||||||
|
if ($candidateDir.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||||
|
$isCovered = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $isCovered) {
|
||||||
|
[void]$selectedDirs.Add($candidateDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$infDirs = $selectedDirs | Sort-Object
|
||||||
|
WriteLog "Driver injection will process $($infDirs.Count) SUBST-safe folders (candidateFolders=$($candidateDirs.Count), INF total=$($infFiles.Count), substMaxLen=$substTargetMaxLength)."
|
||||||
|
|
||||||
|
# Use a single SUBST drive letter and reuse it in a loop (map -> dism -> unmap)
|
||||||
|
$driveLetter = Get-AvailableDriveLetter
|
||||||
|
if ($null -eq $driveLetter) {
|
||||||
|
throw 'No drive letters are available for SUBST mapping.'
|
||||||
|
}
|
||||||
|
|
||||||
|
$driveName = "$driveLetter`:"
|
||||||
|
$drivePath = "$driveLetter`:\"
|
||||||
|
WriteLog "Using SUBST drive $driveName for driver injection loop."
|
||||||
|
|
||||||
|
$currentIndex = 0
|
||||||
|
foreach ($infDir in $infDirs) {
|
||||||
|
$currentIndex++
|
||||||
|
$escapedPath = $infDir -replace '"', '""'
|
||||||
|
|
||||||
|
try {
|
||||||
|
WriteLog "[$currentIndex/$($infDirs.Count)] Mapping '$infDir' to $driveName with SUBST."
|
||||||
|
$mapArgs = "/c subst $driveName `"$escapedPath`""
|
||||||
|
Invoke-Process -FilePath cmd.exe -ArgumentList $mapArgs | Out-Null
|
||||||
|
|
||||||
|
# Inject drivers (do not use \\?\ with DISM)
|
||||||
|
$dismArgs = @(
|
||||||
|
"/Image:`"$resolvedImagePath`""
|
||||||
|
'/Add-Driver'
|
||||||
|
"/Driver:$drivePath"
|
||||||
|
'/Recurse'
|
||||||
|
)
|
||||||
|
|
||||||
|
WriteLog "dism.exe $($dismArgs -join ' ')"
|
||||||
|
Invoke-Process -FilePath dism.exe -ArgumentList $dismArgs | Out-Null
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "Warning: Driver injection failed for '$infDir': $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
WriteLog "Removing SUBST drive $driveName"
|
||||||
|
$unmapArgs = "/c subst $driveName /d"
|
||||||
|
Invoke-Process -FilePath cmd.exe -ArgumentList $unmapArgs | Out-Null
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "Warning: Failed removing SUBST drive $($driveName): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLog "Driver injection loop complete for $resolvedDriverRoot"
|
||||||
|
}
|
||||||
|
|
||||||
function Copy-Drivers {
|
function Copy-Drivers {
|
||||||
param (
|
param (
|
||||||
[Parameter()]
|
[Parameter()]
|
||||||
@@ -2817,7 +3017,6 @@ function Copy-Drivers {
|
|||||||
# 745a17a0-74d3-11d0-b6fe-00a0c90f57da = Human Interface Devices
|
# 745a17a0-74d3-11d0-b6fe-00a0c90f57da = Human Interface Devices
|
||||||
$filterGUIDs = @("{4D36E97D-E325-11CE-BFC1-08002BE10318}", "{4D36E97B-E325-11CE-BFC1-08002BE10318}", "{4d36e96b-e325-11ce-bfc1-08002be10318}", "{4d36e96f-e325-11ce-bfc1-08002be10318}", "{745a17a0-74d3-11d0-b6fe-00a0c90f57da}")
|
$filterGUIDs = @("{4D36E97D-E325-11CE-BFC1-08002BE10318}", "{4D36E97B-E325-11CE-BFC1-08002BE10318}", "{4d36e96b-e325-11ce-bfc1-08002be10318}", "{4d36e96f-e325-11ce-bfc1-08002be10318}", "{745a17a0-74d3-11d0-b6fe-00a0c90f57da}")
|
||||||
$exclusionList = "wdmaudio.inf|Sound|Machine Learning|Camera|Firmware"
|
$exclusionList = "wdmaudio.inf|Sound|Machine Learning|Camera|Firmware"
|
||||||
$pathLength = $Path.Length
|
|
||||||
|
|
||||||
# Log start and validate paths
|
# Log start and validate paths
|
||||||
WriteLog "Copying PE drivers from '$Path' to '$Output' (WindowsArch: $WindowsArch)"
|
WriteLog "Copying PE drivers from '$Path' to '$Output' (WindowsArch: $WindowsArch)"
|
||||||
@@ -2827,6 +3026,9 @@ function Copy-Drivers {
|
|||||||
}
|
}
|
||||||
[void](New-Item -Path $Output -ItemType Directory -Force)
|
[void](New-Item -Path $Output -ItemType Directory -Force)
|
||||||
|
|
||||||
|
$driverSourcePath = $Path
|
||||||
|
$pathLength = $Path.Length
|
||||||
|
|
||||||
# Determine common arch-specific SourceDisksFiles section names
|
# Determine common arch-specific SourceDisksFiles section names
|
||||||
# Many INFs use 'amd64' rather than 'x64' for 64-bit paths (e.g. SourceDisksFiles.amd64)
|
# Many INFs use 'amd64' rather than 'x64' for 64-bit paths (e.g. SourceDisksFiles.amd64)
|
||||||
$sourceDisksFileSections = @("SourceDisksFiles")
|
$sourceDisksFileSections = @("SourceDisksFiles")
|
||||||
@@ -2838,7 +3040,7 @@ function Copy-Drivers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$infFiles = Get-ChildItem -Path $Path -Recurse -Filter "*.inf"
|
$infFiles = Get-ChildItem -Path $Path -Recurse -Filter "*.inf"
|
||||||
WriteLog "Found $($infFiles.Count) INF files under: $Path"
|
WriteLog "Found $($infFiles.Count) INF files under: $driverSourcePath"
|
||||||
|
|
||||||
$matchedInfCount = 0
|
$matchedInfCount = 0
|
||||||
$skippedInfCount = 0
|
$skippedInfCount = 0
|
||||||
@@ -2847,13 +3049,32 @@ function Copy-Drivers {
|
|||||||
|
|
||||||
for ($i = 0; $i -lt $infFiles.Count; $i++) {
|
for ($i = 0; $i -lt $infFiles.Count; $i++) {
|
||||||
$infFullName = $infFiles[$i].FullName
|
$infFullName = $infFiles[$i].FullName
|
||||||
|
# Add long path prefix to handle long paths
|
||||||
|
$longInfFullName = "\\?\$infFullName"
|
||||||
$infPath = Split-Path -Path $infFullName
|
$infPath = Split-Path -Path $infFullName
|
||||||
$childPath = $infPath.Substring($pathLength)
|
$childPath = $infPath.Substring($pathLength).TrimStart('\')
|
||||||
$targetPath = Join-Path -Path $Output -ChildPath $childPath
|
$targetPath = Join-Path -Path $Output -ChildPath $childPath
|
||||||
|
|
||||||
|
# Log the INF files found
|
||||||
|
WriteLog "Examining PE driver INF ($($i + 1)/$($infFiles.Count)): $infFullName"
|
||||||
|
|
||||||
# Filter to known device classes
|
# Filter to known device classes
|
||||||
$classGuid = Get-PrivateProfileString -FileName $infFullName -SectionName "version" -KeyName "ClassGUID"
|
# Some INFs include trailing comments after the value (e.g. "{GUID} ; TODO: ..."), so normalize to the GUID token only.
|
||||||
|
$classGuidRaw = Get-PrivateProfileString -FileName $longInfFullName -SectionName "version" -KeyName "ClassGUID"
|
||||||
|
$classGuid = $classGuidRaw
|
||||||
|
if (-not [string]::IsNullOrWhiteSpace($classGuid)) {
|
||||||
|
# Remove any trailing ';' comment and trim whitespace
|
||||||
|
$classGuid = ($classGuid -split ';', 2)[0].Trim()
|
||||||
|
|
||||||
|
# Extract the GUID token if the value contains other text
|
||||||
|
if ($classGuid -match '\{[0-9A-Fa-f\-]{36}\}') {
|
||||||
|
$classGuid = $matches[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# WriteLog "ClassGUID: $classGuid"
|
||||||
if ($classGuid -notin $filterGUIDs) {
|
if ($classGuid -notin $filterGUIDs) {
|
||||||
|
# WriteLog "Skipping PE driver INF due to GUID: $infFullName"
|
||||||
$skippedInfCount++
|
$skippedInfCount++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -2868,7 +3089,7 @@ function Copy-Drivers {
|
|||||||
$matchedInfCount++
|
$matchedInfCount++
|
||||||
|
|
||||||
# Log the INF being processed
|
# Log the INF being processed
|
||||||
$providerName = (Get-PrivateProfileString -FileName $infFullName -SectionName "Version" -KeyName "Provider").Trim("%")
|
$providerName = (Get-PrivateProfileString -FileName $longInfFullName -SectionName "Version" -KeyName "Provider").Trim("%")
|
||||||
if ([string]::IsNullOrWhiteSpace($providerName)) {
|
if ([string]::IsNullOrWhiteSpace($providerName)) {
|
||||||
$providerName = "Unknown Provider"
|
$providerName = "Unknown Provider"
|
||||||
}
|
}
|
||||||
@@ -2881,7 +3102,7 @@ function Copy-Drivers {
|
|||||||
|
|
||||||
# Copy the INF itself
|
# Copy the INF itself
|
||||||
try {
|
try {
|
||||||
Copy-Item -Path $infFullName -Destination $targetPath -Force -ErrorAction Stop
|
Copy-Item -LiteralPath "$infFullName" -Destination "$targetPath" -Force -ErrorAction Stop
|
||||||
$copiedFileCount++
|
$copiedFileCount++
|
||||||
WriteLog "Copied: $infFullName -> $targetPath"
|
WriteLog "Copied: $infFullName -> $targetPath"
|
||||||
}
|
}
|
||||||
@@ -2891,12 +3112,12 @@ function Copy-Drivers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Copy the catalog file (if specified)
|
# Copy the catalog file (if specified)
|
||||||
$CatalogFileName = Get-PrivateProfileString -FileName $infFullName -SectionName "version" -KeyName "Catalogfile"
|
$CatalogFileName = Get-PrivateProfileString -FileName $longInfFullName -SectionName "version" -KeyName "Catalogfile"
|
||||||
if (-not [string]::IsNullOrWhiteSpace($CatalogFileName)) {
|
if (-not [string]::IsNullOrWhiteSpace($CatalogFileName)) {
|
||||||
$catalogSource = Join-Path -Path $infPath -ChildPath $CatalogFileName
|
$catalogSource = Join-Path -Path $infPath -ChildPath $CatalogFileName
|
||||||
if (Test-Path -Path $catalogSource) {
|
if (Test-Path -Path $catalogSource) {
|
||||||
try {
|
try {
|
||||||
Copy-Item -Path $catalogSource -Destination $targetPath -Force -ErrorAction Stop
|
Copy-Item -LiteralPath "$catalogSource" -Destination "$targetPath" -Force -ErrorAction Stop
|
||||||
$copiedFileCount++
|
$copiedFileCount++
|
||||||
WriteLog "Copied: $catalogSource -> $targetPath"
|
WriteLog "Copied: $catalogSource -> $targetPath"
|
||||||
}
|
}
|
||||||
@@ -2916,7 +3137,7 @@ function Copy-Drivers {
|
|||||||
|
|
||||||
# Copy all files referenced by SourceDisksFiles sections
|
# Copy all files referenced by SourceDisksFiles sections
|
||||||
foreach ($sectionName in $sourceDisksFileSections) {
|
foreach ($sectionName in $sourceDisksFileSections) {
|
||||||
$sourceDiskFiles = Get-PrivateProfileSection -FileName $infFullName -SectionName $sectionName
|
$sourceDiskFiles = Get-PrivateProfileSection -FileName $longInfFullName -SectionName $sectionName
|
||||||
if ($sourceDiskFiles.Count -eq 0) {
|
if ($sourceDiskFiles.Count -eq 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -2954,7 +3175,7 @@ function Copy-Drivers {
|
|||||||
# Copy with logging and error handling
|
# Copy with logging and error handling
|
||||||
if (Test-Path -Path $sourceFilePath) {
|
if (Test-Path -Path $sourceFilePath) {
|
||||||
try {
|
try {
|
||||||
Copy-Item -Path $sourceFilePath -Destination $destinationFolder -Force -ErrorAction Stop
|
Copy-Item -LiteralPath "$sourceFilePath" -Destination "$destinationFolder" -Force -ErrorAction Stop
|
||||||
$copiedFileCount++
|
$copiedFileCount++
|
||||||
WriteLog "Copied: $sourceFilePath -> $destinationFolder"
|
WriteLog "Copied: $sourceFilePath -> $destinationFolder"
|
||||||
}
|
}
|
||||||
@@ -3079,7 +3300,10 @@ function New-PEMedia {
|
|||||||
|
|
||||||
WriteLog "Adding drivers to WinPE media"
|
WriteLog "Adding drivers to WinPE media"
|
||||||
try {
|
try {
|
||||||
Add-WindowsDriver -Path "$WinPEFFUPath\Mount" -Driver $PEDriversFolder -Recurse -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Out-null
|
$WinPEMount = "$WinPEFFUPath\Mount"
|
||||||
|
|
||||||
|
# Inject drivers using deep SUBST mapping (reuse one drive letter and loop each INF folder)
|
||||||
|
Invoke-DismDriverInjectionWithSubstLoop -ImagePath $WinPEMount -DriverRoot $PEDriversFolder
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
WriteLog 'Some drivers failed to be added. This can be expected. Continuing.'
|
WriteLog 'Some drivers failed to be added. This can be expected. Continuing.'
|
||||||
@@ -3381,7 +3605,8 @@ function New-FFU {
|
|||||||
WriteLog 'Mounting complete'
|
WriteLog 'Mounting complete'
|
||||||
WriteLog 'Adding drivers - This will take a few minutes, please be patient'
|
WriteLog 'Adding drivers - This will take a few minutes, please be patient'
|
||||||
try {
|
try {
|
||||||
Add-WindowsDriver -Path "$FFUDevelopmentPath\Mount" -Driver "$DriversFolder" -Recurse -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Out-null
|
# Inject drivers using deep SUBST mapping (reuse one drive letter and loop each INF folder)
|
||||||
|
Invoke-DismDriverInjectionWithSubstLoop -ImagePath "$FFUDevelopmentPath\Mount" -DriverRoot "$DriversFolder"
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
WriteLog 'Some drivers failed to be added to the FFU. This can be expected. Continuing.'
|
WriteLog 'Some drivers failed to be added to the FFU. This can be expected. Continuing.'
|
||||||
|
|||||||
Reference in New Issue
Block a user